diff --git a/.gitignore b/.gitignore index 516ed611b..a8da62f17 100644 --- a/.gitignore +++ b/.gitignore @@ -27,6 +27,7 @@ quagga-[0-9.][0-9.][0-9.]*.tar.gz quagga-[0-9.][0-9.][0-9.]*.tar.gz.asc .nfs* libtool +.libs .arch-inventory .arch-ids {arch} @@ -34,6 +35,11 @@ build .msg .rebase-* *~ +*.o *.loT m4/*.m4 - +!m4/ax_sys_weak_alias.m4 +cscope.* +*.pb.h +*.pb-c.h +*.pb-c.c diff --git a/COPYING b/COPYING index a43ea2126..b8cf3a1ab 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 161a3d1d4..2b4628e4a 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.md b/HACKING.md new file mode 100644 index 000000000..078de600e --- /dev/null +++ b/HACKING.md @@ -0,0 +1,741 @@ +--- +title: Conventions for working on Quagga +papersize: a4paper +geometry: a4paper,scale=0.82 +fontsize: 11pt +toc: true +date: \today +include-before: + \large This is a living document describing the processes and guidelines + for working on Quagga. You *must* read Section + ["REQUIRED READING"](#sec:required), before contributing to Quagga. + + Suggestions for updates, via the + [quagga-dev list](http://lists.quagga.net/mailman/listinfo/quagga-dev), + are welcome. \newpage +... + +\newpage + +OBJECTIVES {#sec:goals} +========== + +The objectives of the Quagga project are to develop and implement high +quality routing protocols and related software, maximising: + +* Free software: + * Quagga is and will remain a copyleft, free software project + * Some non-core parts may be available under compatible, permissive + licenses to facilitate code sharing, where contributors agree. + * The test and integration orchestration infrastructure shall be free + software, developed similarly to the rest of Quagga. Proprietary + conformance suites may be among the test tools orchestrated. +* Openness and transparency + * The business of the project shall be conducted on its public email + lists, to the greatest extent possible. + * The design of the software will be governed by open discussion on + the public email lists. + * Participants shall endeavour to be transparent about their interests + in the project, and any associations likely to be relevant. +* Ethical behaviour: + * The licenses of all copyright holders will be respected, and the + project will err in their favour where there is reasonable doubt or + legal advice to that effect. + * Participants will behave with respect for others, and in a manner that + builds and maintains the trust needed for productive collaboration. + +See also the Section on [CODE OF CONDUCT](#sec:codeconduct). + +Governance {#sec:governance} +========== + +Quagga is a Sociocracy, as it has been since its earliest days. + +Quagga was forked from GNU Zebra by Paul Jakma, who holds the domain name. +Governance was soon devolved to a collective group, the maintainers, +consisting of those who regularly contributed and reviewed code. The +details can easily be changed. + +You are free to use reason to _persuade_ others to adopt some alternative. +If, after that, you truly can not abide by what is mutually agreeable, you +are asked to do the honourable thing: take your copy of the code, make your +apologies, and be on your way with good grace. + +Those who repeatedly violate the [Code of Conduct](#sec:codeconduct) will be +asked to leave. + +Holding of project assets +------------------------- + +One or more mature, independent trustees, with technical and free software +experience, will be appointed as the executor(s) for key assets of the +project to ensure continuity, such as the domain name. + +Should a corporate vehicle ever be created to hold such assets it __must__: + +* Publish up to date accounts on a regular business. +* Generally operate openly and transparently. +* Have control distributed, with a significant degree of control held + independent of any contributors with business interests in the software. +* Carry out no other business itself that may be seen to conflict or compete + with the business of others in the community. +* Have all officers disclose all interests that could be + seen to have a bearing on the project, as far as is reasonable. + +It not clear at this time that the overheads and potential liabilities of +such a vehicle would be appropriate for this project. These principles +should though still be applied, where possible, to any non-corporate body +formed around the project. + +CODE OF CONDUCT {#sec:codeconduct} +=============== + +Participants will treat each other with respect and integrity. Participants +will build and treasure the trust that is required for parties to +successfully collaborate together on free software. Particularly when those +parties may have competing interests. The following principles and +guidelines should be followed to foster that trust: + +* Participants should be open about their goals, and their interests. + - Business associations with other participants should be disclosed, + so far as is reasonable and where applicable. E.g., if there is voting + on matters, or in endorsements or objections to contributions. + - Other associations and interests that may be relevant should be + disclosed, to the degree necessary to avoid any perception + by others of conflicts of interests or of deception. + - Be open about your goals, so as to maximise the common understanding + and minimise any misunderstandings and disputes. +* Design should be done in the open + - Do your design on list, ahead of significant implementation. Avoid + "Surprise!" development where possible. + - Where significant implementation work must be done behind closed + doors, accept that you may be asked to rework it, potentially from + scratch once you take it public. + - Get "buy in" from others ahead of time, to avoid disappointment. +* Interaction + - Feedback on design should be constructive, thoughtful and + understanding. + - Avoid personalising matters. Discuss the idea, the code, the abstract + subject and avoid unnecessary personal pronouns. + - Avoid language that paints either party into a corner. Leave some room + for doubt. Ask questions, rather than make assertions, where possible. +* Disputes should be resolved through calm, analytic discussion + - Separate out as much of the matter under dispute into principles that + can be agreed on, and into the objective domain (by measurement or + logic). + - Seek ways to resolve any remaining subjective differences by alternate + paths that can accommodate both sides, e.g., through abstraction or + modularisation. + - Aim for Win-Win. +* Respect others + - Avoid passive-aggressive behaviours. E.g., tit-for-tat + non-constructive behaviour. Be explicit. + - It is acceptable for management to allocate resources on development + according to their need. It is not acceptable to try use external, + management intervention to over-turn positions held by participants. + +REQUIRED READING {#sec:required} +================ + +Note well: By proposing a change to Quagga, by whatever means, you are +implicitly agreeing: + +- To licence your contribution according to the licence of any files in + Quagga being modified, _and_ according to the COPYING file in the + top-level directory of Quagga, other than where the contribution + explicitly and clearly indicates otherwise. + +- That it is your responsibility to ensure you hold whatever rights are + required to be able to contribute your changes under the licenses of the + files in Quagga being modified, and the top-level COPYING file. + +- That it is your responsibility to give with the contribution a full + account of all interests held and claims in the contribution; such as + through copyright, trademark and patent laws or otherwise; that are known + to you or your associates (e.g. your employer). + +Before contributing to Quagga, you *must* also read +[Section COMMIT MESSAGES](#sec:commit-messages). + +You _should_ ideally read the entire document, as it contains useful +information on the community norms and how to implement them. + +Please note that authorship and any relevant other rights information should +be _explicitly_ stated with the contribution. A "Signed-off-by" line is +_not_ sufficient. The "Signed-off-by" line is not used by the Quagga +project. + +You may document applicable copyright claims to files being modified or +added by your contribution. For new files, the standard way is to add a +string in the following format near the beginning of the file: + + Copyright (C) [, optional contact details] + +When adding copyright claims for modifications to an existing file, please +preface the claim with "Portions: " on a line before it and indent the +"Copyright ..." string. If such a case already exists, add your indented +claim immediately after. E.g.: + + Portions: + Copyright (C) .... + Copyright (C) [optional brief change description] + + +GUIDELINES FOR HACKING ON QUAGGA {#sec:guidelines} +================================ + +GNU coding standards apply. Indentation follows the result of +invoking GNU indent (as of 2.2.8a) with the -–nut argument. + +Originally, tabs were used instead of spaces, with tabs are every 8 columns. +However, tab’s interoperability issues mean space characters are now preferred for +new changes. We generally only clean up whitespace when code is unmaintainable +due to whitespace issues, to minimise merging conflicts. + +Be particularly careful not to break platforms/protocols that you +cannot test. + +Parsers or packet-writers of data from untrusted parties, particularly +remote ones, *MUST* use the lib/stream bounded-buffer abstraction, and use +its checked getters and putters. Twiddling of pointers based on contents of +untrusted data is _strongly_ discouraged - any such code is not acceptable, +unless there are very good reasons (e.g. compatibility with external or old +code that is not easily rewritten). + +New code should have good comments, which explain why the code is correct. +Changes to existing code should in many cases upgrade the comments when +necessary for a reviewer to conclude that the change has no unintended +consequences. + +Each file in the Git repository should have a git format-placeholder (like +an RCS Id keyword), somewhere very near the top, commented out appropriately +for the file type. The placeholder used for Quagga (replacing \ +with \$) is: + +`$QuaggaId: Format:%an, %ai, %h $` + +See line 2 of HACKING.tex, the source for this document, for an example. + +This placeholder string will be expanded out by the ‘git archive’ commands, +which is used to generate the tar archives for snapshots and releases. + +Please document fully the proper use of a new function in the header file +in which it is declared. And please consult existing headers for +documentation on how to use existing functions. In particular, please consult +these header files: + +lib/log.h logging levels and usage guidance + +[more to be added] + +If changing an exported interface, please try to deprecate the interface in +an orderly manner. If at all possible, try to retain the old deprecated +interface as is, or functionally equivalent. Make a note of when the +interface was deprecated and guard the deprecated interface definitions in +the header file, i.e.: + + /* Deprecated: 20050406 */ + #if !defined(QUAGGA_NO_DEPRECATED_INTERFACES) + #warning "Using deprecated (interface(s)|function(s))" + ... + #endif /* QUAGGA_NO_DEPRECATED_INTERFACES */ + +This is to ensure that the core Quagga sources do not use the deprecated +interfaces (you should update Quagga sources to use new interfaces, if +applicable), while allowing external sources to continue to build. +Deprecated interfaces should be excised in the next unstable cycle. + +Note: If you wish, you can test for GCC and use a function +marked with the ’deprecated’ attribute. However, you must provide the +warning for other compilers. + +If changing or removing a command definition, *ensure* that you +properly deprecate it - use the \_DEPRECATED form of the appropriate DEFUN +macro. This is *critical*. Even if the command can no longer +function, you *MUST* still implement it as a do-nothing stub. + +Failure to follow this causes grief for systems administrators, as an +upgrade may cause daemons to fail to start because of unrecognised commands. +Deprecated commands should be excised in the next unstable cycle. A list of +deprecated commands should be collated for each release. + +See also [Section SHARED LIBRARY VERSIONING](#sec:dll-versioning) below. + +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. + +- 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 + +- 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. + +- start using git clone, pwclient + + + $ 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 + +HANDY GUIDELINES FOR MAINTAINERS +================================ + +Get your cloned trie: + + git clone vjardin@git.sv.gnu.org:/srv/git/quagga.git + +Apply some ack’t patches: + + 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 + +Run a quick review. If the ack’t was not done properly, you know who you have +to blame. + +Push the patches: + + git push + +Set the patch to accepted on patchwork + + pwclient update -s Accepted 1046 + +COMPILE-TIME CONDITIONAL CODE +============================= + +Please think very carefully before making code conditional at compile time, +as it increases maintenance burdens and user confusion. In particular, +please avoid gratuitous -–enable-… switches to the configure script - +typically code should be good enough to be in Quagga, or it shouldn’t be +there at all. + +When code must be compile-time conditional, try have the compiler make it +conditional rather than the C pre-processor - so that it will still be +checked by the compiler, even if disabled. I.e. this: + + if (SOME_SYMBOL) + frobnicate(); + +rather than: + + #ifdef SOME_SYMBOL + frobnicate (); + #endif /* SOME_SYMBOL */ + +Note that the former approach requires ensuring that SOME\_SYMBOL will +be defined (watch your AC\_DEFINEs). + +COMMIT MESSAGES {#sec:commit-messages} +====================================== + +The commit message requirements are: + +- The message *MUST* provide a suitable one-line summary followed by a + blank line as the very first line of the message, in the form: + + `topic: high-level, one line summary` + + Where topic would tend to be name of a subdirectory, and/or daemon, unless + there’s a more suitable topic (e.g. ’build’). This topic is used to + organise change summaries in release announcements. + +- It should have a suitable “body”, which tries to address the + following areas, so as to help reviewers and future browsers of the + code-base understand why the change is correct (note also the code + comment requirements): + + - The motivation for the change (does it fix a bug, if so which? + add a feature?) + + - The general approach taken, and trade-offs versus any other + approaches. + + - Any testing undertaken or other information affecting the confidence + that can be had in the change. + + - Information to allow reviewers to be able to tell which specific + changes to the code are intended (and hence be able to spot any accidental + unintended changes). + +- The commit message *must* give details of all the authors of the change, + beyond the person listed in the Author field. Any and all affiliations + which may have a bearing on copyright in any way should be clearly + stated, unless those affiliations are already obvious from other + details, e.g. from the email address. This would cover employment and + contracting obligations (give details). + + Note: Do not rely on "Signed-off-by" for this, be explicit. + +- If the change introduces a new dependency on any code or other + copyrighted material, please explicitly note this. Give details of what + that external material is, the copyright licence the material may be + used under, and the nature of the dependency. + +The one-line summary must be limited to 54 characters, and all other +lines to 72 characters. + +Commit message bodies in the Quagga project have typically taken the +following form: + +- An optional introduction, describing the change generally. + +- A short description of each specific change made, preferably: + + - file by file + + - function by function (use of “ditto”, or globs is allowed) + +Contributors are strongly encouraged to follow this form. + +This itemised commit messages allows reviewers to have confidence that the +author has self-reviewed every line of the patch, as well as providing +reviewers a clear index of which changes are intended, and descriptions for +them (C-to-english descriptions are not desirable - some discretion is +useful). For short patches, a per-function/file break-down may be +redundant. For longer patches, such a break-down may be essential. A +contrived example (where the general discussion is obviously somewhat +redundant, given the one-line summary): + +> zebra: Enhance frob FSM to detect loss of frob +> +> Add a new DOWN state to the frob state machine to allow the barinator to +> detect loss of frob. +> +> * frob.h: (struct frob) Add DOWN state flag. +> * frob.c: (frob_change) set/clear DOWN appropriately on state change. +> * bar.c: (barinate) Check frob for DOWN state. + +Please have a look at the git commit logs to get a feel for what the norms +are. + +Note that the commit message format follows git norms, so that “git log +–oneline” will have useful output. + +HACKING THE BUILD SYSTEM +======================== + +If you change or add to the build system (configure.ac, any Makefile.am, +etc.), please heck that the following things still work: + +- make dist + +- resulting dist tarball builds + +- out-of-tree builds + +This can be achieved by running 'make distcheck' + +The quagga.net site relies on make dist to work to generate snapshots. It +must work. Common problems are to forget to have some additional file +included in the dist, or to have a make rule refer to a source file without +using the srcdir variable. + +RELEASE PROCEDURE +================= + +To make a release: + +- Edit configure.ac, bump the version and commit the change with + a "release: + +- Create a fresh tar archive of the quagga.net repository, and do a + test build. Use git archive to ensure it consists of files in the + repository, and to carry out the keyword expansions. Do NOT do this in + a subdirectory of the Quagga sources, autoconf will think it’s a + sub-package and fail to include neccessary files. + + git archive ... | tar xC .. + + autoreconf -i && ./configure && make && make dist-gzip + +- Similarly test the dist tarball produced. This is the tarball to be + released. This is important. + +- Sign the dist tarball to be released + + gpg -u 54CD2E60 -a --detach-sign quagga-0.99.99.99.tar + +The 'release.sh' script, if finishes successfully, will print out +instructions on the files it has created and the details on remaining steps +to be carried out to complete the release. Which roughly are: + +- Upload the release tarball, its PGP signature, and the full changelog + to the public release area on Savannah + +- Add the version number on https://bugzilla.quagga.net/, under + Administration, Products, “Quagga”, Edit versions, Add a version. + +- Post a news entry on Savannah + +- Send a mail to quagga-dev and quagga-users + +If any errors occur, move tags as needed and start over again with the +release.sh script. Do not try to append stuff to tarballs, as this has +produced non-standards-conforming tarballs in the past. + +[TODO: collation of a list of deprecated commands. Possibly can be +scripted to extract from vtysh/vtysh\_cmd.c] + +TOOL VERSIONS +============= + +Require versions of support tools are listed in INSTALL.quagga.txt. +Required versions should only be done with due deliberation, as it can +cause environments to no longer be able to compile quagga. + +SHARED LIBRARY VERSIONING {#sec:dll-versioning} +========================= + +[this section is at the moment just gdt’s opinion] + +Quagga builds several shared libaries (lib/libzebra, ospfd/libospf, +ospfclient/libsopfapiclient). These may be used by external programs, +e.g. a new routing protocol that works with the zebra daemon, or +ospfapi clients. The libtool info pages (node Versioning) explain +when major and minor version numbers should be changed. These values +are set in Makefile.am near the definition of the library. If you +make a change that requires changing the shared library version, +please update Makefile.am. + +libospf exports far more than it should, and is needed by ospfapi +clients. Only bump libospf for changes to functions for which it is +reasonable for a user of ospfapi to call, and please err on the side +of not bumping. + +There is no support intended for installing part of zebra. The core +library libzebra and the included daemons should always be built and +installed together. + +GIT COMMIT SUBMISSION {#sec:git-submission} +===================== + +The preferred method for submitting changes is to provide git commits via a +publicly-accessible git repository, which the maintainers can easily pull. + +The commits should be in a branch based off the Quagga.net master - a +“feature branch”. Ideally there should be no commits to this branch other +than those in master, and those intended to be submitted. However, merge +commits to this branch from the Quagga master are permitted, though strongly +discouraged - use another (potentially local and throw-away) branch to test +merge with the latest Quagga master. + +Recommended practice is to keep different logical sets of changes on +separate branches - “topic” or “feature” branches. This allows you to still +merge them together to one branch (potentially local and/or “throw-away”) +for testing or use, while retaining smaller, independent branches that are +easier to merge. + +All content guidelines in [Section PATCH SUBMISSION](#sec:patch-submission) +apply. + +PATCH SUBMISSION {#sec:patch-submission} +================ + +- For complex changes, contributors are strongly encouraged to first + start a design discussion on the quagga-dev list *before* starting + any coding. + +- Send a clean diff against the ’master’ branch of the quagga.git + repository, in unified diff format, preferably with the ’-p’ + argument to show C function affected by any chunk, and with the -w + and -b arguments to minimise changes. E.g: + + git diff -up mybranch..remotes/quagga.net/master + + It is preferable to use git format-patch, and even more preferred to + publish a git repository (see + [Section GIT COMMIT SUBMISSION](#sec:git-submission)). + + If not using git format-patch, Include the commit message in the + email. + +- After a commit, code should have comments explaining to the reviewer + why it is correct, without reference to history. The commit message + should explain why the change is correct. + +- Include NEWS entries as appropriate. + +- Include only one semantic change or group of changes per patch. + +- Do not make gratuitous changes to whitespace. See the w and b + arguments to diff. + +- Changes should be arranged so that the least controversial and most + trivial are first, and the most complex or more controversial are + last. This will maximise how many the Quagga maintainers can merge, + even if some other commits need further work. + +- Providing a unit-test is strongly encouraged. Doing so will make it + much easier for maintainers to have confidence that they will be + able to support your change. + +- New code should be arranged so that it easy to verify and test. E.g. + stateful logic should be separated out from functional logic as much + as possible: wherever possible, move complex logic out to smaller + helper functions which access no state other than their arguments. + +- State on which platforms and with what daemons the patch has been + tested. Understand that if the set of testing locations is small, + and the patch might have unforeseen or hard to fix consequences that + there may be a call for testers on quagga-dev, and that the patch + may be blocked until test results appear. + + If there are no users for a platform on quagga-dev who are able and + willing to verify -current occasionally, that platform may be + dropped from the “should be checked” list. + +PATCH APPLICATION +================= + +- Only apply patches that meet the submission guidelines. + +- If the patch might break something, issue a call for testing on the + mailing-list. + +- Give an appropriate commit message (see above), and use the –author + argument to git-commit, if required, to ensure proper attribution + (you should still be listed as committer) + +- Immediately after commiting, double-check (with git-log and/or + gitk). If there’s a small mistake you can easily fix it with ‘git + commit –amend ..’ + +- When merging a branch, always use an explicit merge commit. Giving + –no-ff ensures a merge commit is created which documents “this human + decided to merge this branch at this time”. + +STABLE PLATFORMS AND DAEMONS +============================ + +The list of platforms that should be tested follow. This is a list +derived from what quagga is thought to run on and for which +maintainers can test or there are people on quagga-dev who are able +and willing to verify that -current does or does not work correctly. + +- BSD (Free, Net or Open, any platform) + +- GNU/Linux (any distribution, i386) + +- Solaris (strict alignment, any platform) + +- future: NetBSD/sparc64 + +The list of daemons that are thought to be stable and that should be +tested are: + +- zebra + +- bgpd + +- ripd + +- ospfd + +- ripngd + +Daemons which are in a testing phase are + +- ospf6d + +- isisd + +- watchquagga + +USEFUL URLs +=========== + +* The project homepage is at: + + + + +* Bugs can be reported via Bugzilla at: + + + +* Buildbot runs CI tests, and is at: + + + + It tests commits and jobs submitted on local changes via + 'buildbot try ...' for developers. + +* Patchwork tracks any patches emailed to the quagga-dev list, and is at: + + + + +BUILDBOT +======== + +The buildbot client can be used to test changes before committing, with +"buildbot try". + +- Ask for a buildbot account + +- Install the buildbot client + +- Configure it, e.g.: + + ~~~~~ + $ cat ~/.buildbot/options + try_master = 'radia.quagga.net:8031' + try_username = 'paul' + try_password = 'password123' + try_vc = 'git' + try_branch = 'master' + try_wait = True + $ buildbot try -c pb --get-builder-names + using 'pb' connect method + The following builders are available for the try scheduler: + build-fedora-24 + ... + ~~~~~ + +- You can then submit your local changes to try build: + + ~~~~ + $ buildbot try -c pb + ~~~~ + + or use the -b argument to limit to a specific builder (recommended). + + ~~~~~ + $ buildbot try -c pb -b build-distcheck + ~~~~~ + +- To test a series of locally committed change use git diff: + + ~~~~ + git diff .. | buildbot try -c pb --vc git \ + -b build-centos-7 --branch=volatile/next --diff=- -p 1 + ~~~~ \ No newline at end of file diff --git a/HACKING.pending b/HACKING.pending index dfce5ceca..73d3194fa 100644 --- a/HACKING.pending +++ b/HACKING.pending @@ -1,34 +1,8 @@ This file contains pointers to work done on quagga that is not in the quagga git repository or quagga bugzilla. -* bug/patch trackers - -** diac24 patchwork instance - -David Lamparter runs a patchwork instance at - - http://patchwork.diac24.net/project/quagga/list/ - -which contains about 225 patches to quagga. Many of these are -collected in his git repository. - * public git repositories -** git remote add quagga-re git://github.com/Quagga-RE/quagga-RE.git - -Maintained by Denis Ovsienko, and geared towards producing a -production-ready branch of Quagga, in the Quagga-RE-stable branch. - -** git remote add equinox git://git.spaceboyz.net/equinox/quagga.git/ - -This repository has topic branches for patches intended for inclusion -in the main quagga tree, named patches/, plus some other branches. - -** git remote add balajig http://github.com/balajig/quagga-next.git - -Balaji G has prepared a git repository where a number of patches to -the list have been stored. - ** git remote add mtr http://github.com/tomhenderson/quagga-mtr.git Tom Henderson of Boeing has created a repository to work on @@ -36,19 +10,3 @@ multi-topology routing support for OSPF. Work on this repository takes place on the branch mtr, which has a branch point of 0.99.17 * posted patches - -** Boeing - -Boeing has posted patches - - quagga-0.99.9.ospfv3-addressfamilies.patch - quagga-0.99.9.ospfv3-manetmdr.patch - -against 0.99.9 at - - http://hipserver.mct.phantomworks.org/ietf/ospf/ - -Both patches include functional enhancements as well as support for -gcc 2.95. - -[TODO: Are any of these obsolete with respect to mtr/mtr?] diff --git a/HACKING.tex b/HACKING.tex deleted file mode 100644 index a49113fb9..000000000 --- a/HACKING.tex +++ /dev/null @@ -1,462 +0,0 @@ -%% -*- mode: text; -*- -%% $QuaggaId: Format:%an, %ai, %h$ $ - -\documentclass[oneside]{article} -\usepackage{parskip} -\usepackage[bookmarks,colorlinks=true]{hyperref} - -\title{Conventions for working on Quagga} - -\begin{document} -\maketitle - -This is a living document. Suggestions for updates, via the -\href{http://lists.quagga.net/mailman/listinfo/quagga-dev}{quagga-dev list}, -are welcome. - -\tableofcontents - -\section{GUIDELINES FOR HACKING ON QUAGGA} -\label{sec:guidelines} - - -GNU coding standards apply. Indentation follows the result of -invoking GNU indent (as of 2.2.8a) with no arguments. Note that this -uses tabs instead of spaces where possible for leading whitespace, and -assumes that tabs are every 8 columns. Do not attempt to redefine the -location of tab stops. Note also that some indentation does not -follow GNU style. This is a historical accident, and we generally -only clean up whitespace when code is unmaintainable due to whitespace -issues, to minimise merging conflicts. - -For GNU emacs, use indentation style ``gnu''. - -For Vim, use the following lines (note that tabs are at 8, and that -softtabstop sets the indentation level): - -set tabstop=8 -set softtabstop=2 -set shiftwidth=2 -set noexpandtab - -Be particularly careful not to break platforms/protocols that you -cannot test. - -New code should have good comments, which explain why the code is correct. -Changes to existing code should in many cases upgrade the comments when -necessary for a reviewer to conclude that the change has no unintended -consequences. - -Each file in the Git repository should have a git format-placeholder (like -an RCS Id keyword), somewhere very near the top, commented out appropriately -for the file type. The placeholder used for Quagga (replacing with -\$) is: - - \verb|$QuaggaId: Format:%an, %ai, %h $| - -See line 2 of HACKING.tex, the source for this document, for an example. - -This placeholder string will be expanded out by the `git archive' commands, -wihch is used to generate the tar archives for snapshots and releases. - -Please document fully the proper use of a new function in the header file -in which it is declared. And please consult existing headers for -documentation on how to use existing functions. In particular, please consult -these header files: - -\begin{description} - \item{lib/log.h} logging levels and usage guidance - \item{[more to be added]} -\end{description} - -If changing an exported interface, please try to deprecate the interface in -an orderly manner. If at all possible, try to retain the old deprecated -interface as is, or functionally equivalent. Make a note of when the -interface was deprecated and guard the deprecated interface definitions in -the header file, ie: - -\begin{verbatim} -/* Deprecated: 20050406 */ -#if !defined(QUAGGA_NO_DEPRECATED_INTERFACES) -#warning "Using deprecated (interface(s)|function(s))" -... -#endif /* QUAGGA_NO_DEPRECATED_INTERFACES */ -\end{verbatim} - -This is to ensure that the core Quagga sources do not use the deprecated -interfaces (you should update Quagga sources to use new interfaces, if -applicable), while allowing external sources to continue to build. -Deprecated interfaces should be excised in the next unstable cycle. - -Note: If you wish, you can test for GCC and use a function -marked with the 'deprecated' attribute. However, you must provide the -warning for other compilers. - -If changing or removing a command definition, \emph{ensure} that you -properly deprecate it - use the \_DEPRECATED form of the appropriate DEFUN -macro. This is \emph{critical}. Even if the command can no longer -function, you \emph{MUST} still implement it as a do-nothing stub. - -Failure to follow this causes grief for systems administrators, as an -upgrade may cause daemons to fail to start because of unrecognised commands. -Deprecated commands should be excised in the next unstable cycle. A list of -deprecated commands should be collated for each release. - -See also section~\ref{sec:dll-versioning} below regarding SHARED LIBRARY -VERSIONING. - - -\section{COMPILE-TIME CONDITIONAL CODE} - -Please think very carefully before making code conditional at compile time, -as it increases maintenance burdens and user confusion. In particular, -please avoid gratuitious --enable-\ldots switches to the configure script - -typically code should be good enough to be in Quagga, or it shouldn't be -there at all. - -When code must be compile-time conditional, try have the compiler make it -conditional rather than the C pre-processor - so that it will still be -checked by the compiler, even if disabled. I.e. this: - -\begin{verbatim} - if (SOME_SYMBOL) - frobnicate(); -\end{verbatim} - -rather than: - -\begin{verbatim} - #ifdef SOME_SYMBOL - frobnicate (); - #endif /* SOME_SYMBOL */ -\end{verbatim} - -Note that the former approach requires ensuring that SOME\_SYMBOL will be -defined (watch your AC\_DEFINEs). - - -\section{COMMIT MESSAGES} - -The commit message requirements are: - -\begin{itemize} - -\item The message \emph{MUST} provide a suitable one-line summary followed - by a blank line as the very first line of the message, in the form: - - \verb|topic: high-level, one line summary| - - Where topic would tend to be name of a subdirectory, and/or daemon, unless - there's a more suitable topic (e.g. 'build'). This topic is used to - organise change summaries in release announcements. - -\item It should have a suitable "body", which tries to address the - following areas, so as to help reviewers and future browsers of the - code-base understand why the change is correct (note also the code - comment requirements): - - \begin{itemize} - - \item The motivation for the change (does it fix a bug, if so which? - add a feature?) - - \item The general approach taken, and trade-offs versus any other - approaches. - - \item Any testing undertaken or other information affecting the confidence - that can be had in the change. - - \item Information to allow reviewers to be able to tell which specific - changes to the code are intended (and hence be able to spot any accidental - unintended changes). - - \end{itemize} -\end{itemize} - -The one-line summary must be limited to 54 characters, and all other -lines to 72 characters. - -Commit message bodies in the Quagga project have typically taken the -following form: - -\begin{itemize} -\item An optional introduction, describing the change generally. -\item A short description of each specific change made, preferably: - \begin{itemize} \item file by file - \begin{itemize} \item function by function (use of "ditto", or globs is - allowed) - \end{itemize} - \end{itemize} -\end{itemize} - -Contributors are strongly encouraged to follow this form. - -This itemised commit messages allows reviewers to have confidence that the -author has self-reviewed every line of the patch, as well as providing -reviewers a clear index of which changes are intended, and descriptions for -them (C-to-english descriptions are not desireable - some discretion is -useful). For short patches, a per-function/file break-down may be -redundant. For longer patches, such a break-down may be essential. A -contrived example (where the general discussion is obviously somewhat -redundant, given the one-line summary): - -\begin{quote}\begin{verbatim} -zebra: Enhance frob FSM to detect loss of frob - -Add a new DOWN state to the frob state machine to allow the barinator to -detect loss of frob. - -* frob.h: (struct frob) Add DOWN state flag. -* frob.c: (frob\_change) set/clear DOWN appropriately on state change. -* bar.c: (barinate) Check frob for DOWN state. -\end{verbatim}\end{quote} - -Please have a look at the git commit logs to get a feel for what the norms -are. - -Note that the commit message format follows git norms, so that ``git -log --oneline'' will have useful output. - -\section{HACKING THE BUILD SYSTEM} - -If you change or add to the build system (configure.ac, any Makefile.am, -etc.), try to check that the following things still work: - -\begin{itemize} -\item make dist -\item resulting dist tarball builds -\item out-of-tree builds -\end{itemize} - -The quagga.net site relies on make dist to work to generate snapshots. It -must work. Common problems are to forget to have some additional file -included in the dist, or to have a make rule refer to a source file without -using the srcdir variable. - - -\section{RELEASE PROCEDURE} - -\begin{itemize} -\item Tag the apppropriate commit with a release tag (follow existing - conventions). - - [This enables recreating the release, and is just good CM practice.] - -\item Create a fresh tar archive of the quagga.net repository, and do a test - build: - - \begin{verbatim} - git-clone git:///code.quagga.net/quagga.git quagga - git-archive --remote=git://code.quagga.net/quagga.git \ - --prefix=quagga-release/ master | tar -xf - - cd quagga-release - - autoreconf -i - ./configure - make - make dist - \end{verbatim} -\end{itemize} - -The tarball which `make dist' creates is the tarball to be released! The -git-archive step ensures you're working with code corresponding to that in -the official repository, and also carries out keyword expansion. If any -errors occur, move tags as needed and start over from the fresh checkouts. -Do not append to tarballs, as this has produced non-standards-conforming -tarballs in the past. - -See also: \url{http://wiki.quagga.net/index.php/Main/Processes} - -[TODO: collation of a list of deprecated commands. Possibly can be scripted -to extract from vtysh/vtysh\_cmd.c] - - -\section{TOOL VERSIONS} - -Require versions of support tools are listed in INSTALL.quagga.txt. -Required versions should only be done with due deliberation, as it can -cause environments to no longer be able to compile quagga. - - -\section{SHARED LIBRARY VERSIONING} -\label{sec:dll-versioning} - -[this section is at the moment just gdt's opinion] - -Quagga builds several shared libaries (lib/libzebra, ospfd/libospf, -ospfclient/libsopfapiclient). These may be used by external programs, -e.g. a new routing protocol that works with the zebra daemon, or -ospfapi clients. The libtool info pages (node Versioning) explain -when major and minor version numbers should be changed. These values -are set in Makefile.am near the definition of the library. If you -make a change that requires changing the shared library version, -please update Makefile.am. - -libospf exports far more than it should, and is needed by ospfapi -clients. Only bump libospf for changes to functions for which it is -reasonable for a user of ospfapi to call, and please err on the side -of not bumping. - -There is no support intended for installing part of zebra. The core -library libzebra and the included daemons should always be built and -installed together. - - -\section{GIT COMMIT SUBMISSION} -\label{sec:git-submission} - -The preferred method for submitting changes is to provide git commits via a -publically-accessible git repository, which the maintainers can easily pull. - -The commits should be in a branch based off the Quagga.net master - a -"feature branch". Ideally there should be no commits to this branch other -than those in master, and those intended to be submitted. However, merge -commits to this branch from the Quagga master are permitted, though strongly -discouraged - use another (potentially local and throw-away) branch to test -merge with the latest Quagga master. - -Recommended practice is to keep different logical sets of changes on -separate branches - "topic" or "feature" branches. This allows you to still -merge them together to one branch (potentially local and/or "throw-away") -for testing or use, while retaining smaller, independent branches that are -easier to merge. - -All content guidelines in section \ref{sec:patch-submission}, PATCH -SUBMISSION apply. - - -\section{PATCH SUBMISSION} -\label{sec:patch-submission} - -\begin{itemize} - -\item For complex changes, contributors are strongly encouraged to first - start a design discussion on the quagga-dev list \emph{before} - starting any coding. - -\item Send a clean diff against the 'master' branch of the quagga.git - repository, in unified diff format, preferably with the '-p' argument to - show C function affected by any chunk, and with the -w and -b arguments to - minimise changes. E.g: - - git diff -up mybranch..remotes/quagga.net/master - - It is preferable to use git format-patch, and even more preferred to - publish a git repository (see GIT COMMIT SUBMISSION, section - \ref{sec:git-submission}). - - If not using git format-patch, Include the commit message in the email. - -\item After a commit, code should have comments explaining to the reviewer - why it is correct, without reference to history. The commit message - should explain why the change is correct. - -\item Include NEWS entries as appropriate. - -\item Include only one semantic change or group of changes per patch. - -\item Do not make gratuitous changes to whitespace. See the w and b arguments - to diff. - -\item Changes should be arranged so that the least contraversial and most - trivial are first, and the most complex or more contraversial are - last. This will maximise how many the Quagga maintainers can merge, - even if some other commits need further work. - -\item Providing a unit-test is strongly encouraged. Doing so will make it - much easier for maintainers to have confidence that they will be able - to support your change. - -\item New code should be arranged so that it easy to verify and test. E.g. - stateful logic should be separated out from functional logic as much as - possible: wherever possible, move complex logic out to smaller helper - functions which access no state other than their arguments. - -\item State on which platforms and with what daemons the patch has been - tested. Understand that if the set of testing locations is small, - and the patch might have unforeseen or hard to fix consequences that - there may be a call for testers on quagga-dev, and that the patch - may be blocked until test results appear. - - If there are no users for a platform on quagga-dev who are able and - willing to verify -current occasionally, that platform may be - dropped from the "should be checked" list. - -\end{itemize} - -\section{PATCH APPLICATION} - -\begin{itemize} - -\item Only apply patches that meet the submission guidelines. - -\item If the patch might break something, issue a call for testing on the - mailinglist. - -\item Give an appropriate commit message (see above), and use the --author - argument to git-commit, if required, to ensure proper attribution (you - should still be listed as committer) - -\item Immediately after commiting, double-check (with git-log and/or gitk). - If there's a small mistake you can easily fix it with `git commit - --amend ..' - -\item When merging a branch, always use an explicit merge commit. Giving - --no-ff ensures a merge commit is created which documents ``this human - decided to merge this branch at this time''. -\end{itemize} - -\section{STABLE PLATFORMS AND DAEMONS} - -The list of platforms that should be tested follow. This is a list -derived from what quagga is thought to run on and for which -maintainers can test or there are people on quagga-dev who are able -and willing to verify that -current does or does not work correctly. - -\begin{itemize} - \item BSD (Free, Net or Open, any platform) - \item GNU/Linux (any distribution, i386) - \item Solaris (strict alignment, any platform) - \item future: NetBSD/sparc64 -\end{itemize} - -The list of daemons that are thought to be stable and that should be -tested are: - -\begin{itemize} - \item zebra - \item bgpd - \item ripd - \item ospfd - \item ripngd -\end{itemize} -Daemons which are in a testing phase are - -\begin{itemize} - \item ospf6d - \item isisd - \item watchquagga -\end{itemize} - -\section{IMPORT OR UPDATE VENDOR SPECIFIC ROUTING PROTOCOLS} - -The source code of Quagga is based on two vendors: - - \verb|zebra_org| (\url{http://www.zebra.org/}) - \verb|isisd_sf| (\url{http://isisd.sf.net/}) - -To import code from further sources, e.g. for archival purposes without -necessarily having to review and/or fix some changeset, create a branch from -`master': - -\begin{verbatim} - git checkout -b archive/foo master - - git commit -a "Joe Bar " - git push quagga archive/foo -\end{verbatim} - -presuming `quagga' corresponds to a file in your .git/remotes with -configuration for the appropriate Quagga.net repository. - -\end{document} diff --git a/INSTALL.quagga.txt b/INSTALL.quagga.txt index ec4e799d2..11c85b1a3 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 19a90227d..3c3b65eb9 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,23 +1,22 @@ ## 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@ +SUBDIRS = lib qpb fpm @ZEBRA@ @BGPD@ @RIPD@ @RIPNGD@ @OSPFD@ @OSPF6D@ @NHRPD@ \ + @ISISD@ @PIMD@ @WATCHQUAGGA@ @VTYSH@ @OSPFCLIENT@ @DOC@ m4 @pkgsrcdir@ \ + redhat @SOLARIS@ tests -DIST_SUBDIRS = lib zebra bgpd ripd ripngd ospfd ospf6d babeld \ +DIST_SUBDIRS = lib qpb fpm zebra bgpd ripd ripngd ospfd ospf6d nhrpd \ 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 -if HAVE_LATEX +if HAVE_PANDOC -HACKING.pdf: HACKING.tex - $(LATEXMK) -pdf $< +HACKING.pdf: HACKING.md + pandoc -o $@ $< clean-local: -$(LATEXMK) -C HACKING.tex diff --git a/NEWS b/NEWS index e9d3a998c..6db831e5c 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,108 @@ +Note: this file lists major user-visible changes only. + +* Changes in Quagga [] + +- [zebra] "no link-detect" is no longer the default. + + The previous release of Quagga always explicitly writes-out the + link-detect configuration state. Therefore, to retain current behavior + save your config with the prior release before updating. + + Otherwise, review your configuration. Note, most users will generally + want to have link-detect enabled, and so can just remove 'no + link-detect' from their interface configuration. + + This release also adds a global configuration to specify the default, + which can be specified in the zebra configuration as: + + default link-detect (on|off) + + This will then apply to any interface which does not have link-detect + explicitly configured. + +* 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 +- [bgpd] SAFI (Multicast topology) support has been extended to propagate + the topology to zebra. +- [bgpd] AS path limit functionality has been removed +- [babeld] a new routing daemon implementing the BABEL ad-hoc mesh routing + protocol has been merged. +- [isisd] a major overhaul has been picked up. Please note that isisd is + STILL NOT SUITABLE FOR PRODUCTION USE. +- [*] a lot of bugs have been fixed, please refer to the git log + * Changes in Quagga 0.99.10 - [bgpd] 4-byte AS support added @@ -167,7 +272,7 @@ which the PtP patch introduced. * Chages in ospf6d ** Many bugs are fixed. - + * Changes in zebra-0.92a * Changes in bgpd @@ -184,7 +289,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. @@ -241,7 +346,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. @@ -293,7 +398,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 @@ -629,7 +734,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 @@ -723,7 +828,7 @@ value. it is available. ** Reflect IPv6 interface's address change to protocol daemons. - + * Changes in zebra-0.88 * Changes in lib @@ -844,7 +949,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 @@ -937,7 +1042,7 @@ bgp ASN. * Changes in zebra ** Better interface up/down event handle. - + * Changes in zebra-0.86 * Changes in lib @@ -1021,7 +1126,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 @@ -1044,7 +1149,7 @@ drastically improved. * Changes in ripd ** RIPv1 update is done by class-full manner. - + * Changes in zebra-0.84b * Changes in lib @@ -1063,14 +1168,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 @@ -1149,7 +1254,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 @@ -1161,7 +1266,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 @@ -1212,7 +1317,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 @@ -1221,7 +1326,7 @@ to configure script. * Changes in bgpd ** Fix FSM bug which introduced in zebra-0.80. - + * Changes in zebra-0.80 * access-list @@ -1484,7 +1589,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 @@ -1520,7 +1625,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 @@ -1565,7 +1670,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 @@ -1636,7 +1741,7 @@ timeout, garbage timer. * Changes in ospf6d ** Redistribute route works. - + * Changes in zebra-0.76 * Changes in lib @@ -1646,7 +1751,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 @@ -1693,7 +1798,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 @@ -1754,7 +1859,7 @@ router. ** LSA data structure is changed. ** Call of log_rotate() is removed. - + * Changes in zebra-0.73 * Changes in lib @@ -1816,7 +1921,7 @@ DEFAULT_LOCAL_PREF(100). ** Clean up logging message. ** Reflect routing information to zebra daemon. - + * Changes in zebra-0.72 * Changes in lib @@ -1837,7 +1942,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 @@ -1888,7 +1993,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 @@ -1909,7 +2014,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 @@ -1959,7 +2064,7 @@ peer. * Changes in ospfd ** LS request and LS update can be send and received. - + * Changes in zebra-0.68 * Changes in lib @@ -1992,7 +2097,7 @@ summary-only mode. * Changes in ospf6d ** router zebra related bug is fixed. - + * Changes in zebra-0.67 * Changes in lib @@ -2034,7 +2139,7 @@ it is fixed. * Changes in ospf6d ** `router zebra' is default behavior. - + * Changes in zebra-0.66 * Changes in zebra @@ -2078,7 +2183,7 @@ routes to the kernel, please configure like below: router zebra no redistribute ripng ! - + * Changes in zebra-0.65 * Changes in lib @@ -2130,7 +2235,7 @@ zebra. * Changes in ospf6d ** Bug fix about network vertex. - + * Changes in zebra-0.64.1. This is bug fix release. @@ -2168,7 +2273,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 @@ -2207,7 +2312,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 @@ -2226,7 +2331,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. @@ -2269,7 +2374,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. @@ -2300,7 +2405,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 @@ -2327,7 +2432,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 @@ -2346,7 +2451,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 @@ -2391,7 +2496,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 @@ -2482,8 +2587,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 9c3546bc8..0322d451d 100644 --- a/SERVICES +++ b/SERVICES @@ -17,3 +17,5 @@ bgpd 2605/tcp ospf6d 2606/tcp ospfapi 2607/tcp isisd 2608/tcp +pimd 2611/tcp +nhrpd 2612/tcp diff --git a/TODO b/TODO index 24941de03..246cc2572 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/.gitignore b/babeld/.gitignore deleted file mode 100644 index 8384763a6..000000000 --- a/babeld/.gitignore +++ /dev/null @@ -1,7 +0,0 @@ -* -!*.c -!*.h -!LICENCE -!Makefile.am -!babeld.conf.sample -!.gitignore \ No newline at end of file diff --git a/babeld/LICENCE b/babeld/LICENCE deleted file mode 100644 index 9da569dc2..000000000 --- a/babeld/LICENCE +++ /dev/null @@ -1,36 +0,0 @@ -Code in this directory is made available under the following licence: - - --------------------------------------------------------------------------- - Copyright (c) 2007, 2008 by Juliusz Chroboczek - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. - --------------------------------------------------------------------------- - -The code also makes calls to and links with the "libzebra" code of Quagga, -in the lib/ directory of this project, which is subject to the GPL licence -as given in the top-level COPYING file included with Quagga. - -Contributors to the code in babeld/ are asked to make their work available -under the same MIT/X11 licence as given immediately above. Please indicate -your assent to this by updating this file and appending the appropriate - - Copyright , - -line to the existing copyright assertion lines in the MIT/X11 licence text -above in this file. diff --git a/babeld/Makefile.am b/babeld/Makefile.am deleted file mode 100644 index 468b5a5f1..000000000 --- a/babeld/Makefile.am +++ /dev/null @@ -1,29 +0,0 @@ -## Process this file with automake to produce Makefile.in. - -INCLUDES = @INCLUDES@ -I.. -I$(top_srcdir) -I$(top_srcdir)/lib @SNMP_INCLUDES@ -DEFS = @DEFS@ -DSYSCONFDIR=\"$(sysconfdir)/\" -INSTALL_SDATA=@INSTALL@ -m 600 - -AM_CFLAGS = $(PICFLAGS) -AM_LDFLAGS = $(PILDFLAGS) - -noinst_LIBRARIES = libbabel.a -sbin_PROGRAMS = babeld - -libbabel_a_SOURCES = \ - babel_zebra.c net.c kernel.c util.c source.c neighbour.c \ - route.c xroute.c message.c resend.c babel_interface.c babeld.c \ - babel_filter.c - -noinst_HEADERS = \ - babel_zebra.h net.h kernel.h util.h source.h neighbour.h \ - route.h xroute.h message.h resend.h babel_interface.h babeld.h \ - babel_filter.h - -babeld_SOURCES = \ - babel_main.c $(libbabel_a_SOURCES) - -babeld_LDADD = ../lib/libzebra.la @LIBCAP@ - -examplesdir = $(exampledir) -dist_examples_DATA = babeld.conf.sample diff --git a/babeld/babel_filter.c b/babeld/babel_filter.c deleted file mode 100644 index 191a9f77e..000000000 --- a/babeld/babel_filter.c +++ /dev/null @@ -1,124 +0,0 @@ -/* - * This file is free software: you may copy, redistribute 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 file 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, see . - * - * This file incorporates work covered by the following copyright and - * permission notice: - * - -Copyright 2011 by Matthieu Boutier and Juliusz Chroboczek - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -*/ - -#include "babel_filter.h" -#include "vty.h" -#include "filter.h" -#include "log.h" -#include "plist.h" -#include "distribute.h" -#include "util.h" - -int -babel_filter(int output, const unsigned char *prefix, unsigned short plen, - unsigned int ifindex) -{ - struct interface *ifp = if_lookup_by_index(ifindex); - babel_interface_nfo *babel_ifp = ifp ? babel_get_if_nfo(ifp) : NULL; - struct prefix p; - struct distribute *dist; - struct access_list *alist; - struct prefix_list *plist; - int filter = output ? BABEL_FILTER_OUT : BABEL_FILTER_IN; - int distribute = output ? DISTRIBUTE_OUT : DISTRIBUTE_IN; - - p.family = v4mapped(prefix) ? AF_INET : AF_INET6; - p.prefixlen = v4mapped(prefix) ? plen - 96 : plen; - if (p.family == AF_INET) - uchar_to_inaddr(&p.u.prefix4, prefix); - else - uchar_to_in6addr(&p.u.prefix6, prefix); - - if (babel_ifp != NULL && babel_ifp->list[filter]) { - if (access_list_apply (babel_ifp->list[filter], &p) - == FILTER_DENY) { - debugf(BABEL_DEBUG_FILTER, - "%s/%d filtered by distribute in", - p.family == AF_INET ? - inet_ntoa(p.u.prefix4) : - inet6_ntoa (p.u.prefix6), - p.prefixlen); - return INFINITY; - } - } - if (babel_ifp != NULL && babel_ifp->prefix[filter]) { - if (prefix_list_apply (babel_ifp->prefix[filter], &p) - == PREFIX_DENY) { - debugf(BABEL_DEBUG_FILTER, "%s/%d filtered by distribute in", - p.family == AF_INET ? - inet_ntoa(p.u.prefix4) : - inet6_ntoa (p.u.prefix6), - p.prefixlen); - return INFINITY; - } - } - - /* All interface filter check. */ - dist = distribute_lookup (NULL); - if (dist) { - if (dist->list[distribute]) { - alist = access_list_lookup (AFI_IP6, dist->list[distribute]); - - if (alist) { - if (access_list_apply (alist, &p) == FILTER_DENY) { - debugf(BABEL_DEBUG_FILTER, "%s/%d filtered by distribute in", - p.family == AF_INET ? - inet_ntoa(p.u.prefix4) : - inet6_ntoa (p.u.prefix6), - p.prefixlen); - return INFINITY; - } - } - } - if (dist->prefix[distribute]) { - plist = prefix_list_lookup (AFI_IP6, dist->prefix[distribute]); - if (plist) { - if (prefix_list_apply (plist, &p) == PREFIX_DENY) { - debugf(BABEL_DEBUG_FILTER, "%s/%d filtered by distribute in", - p.family == AF_INET ? - inet_ntoa(p.u.prefix4) : - inet6_ntoa (p.u.prefix6), - p.prefixlen); - return INFINITY; - } - } - } - } - return 0; -} diff --git a/babeld/babel_filter.h b/babeld/babel_filter.h deleted file mode 100644 index 73722e0a3..000000000 --- a/babeld/babel_filter.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * This file is free software: you may copy, redistribute 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 file 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, see . - * - * This file incorporates work covered by the following copyright and - * permission notice: - * -Copyright 2011 by Matthieu Boutier and Juliusz Chroboczek - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -*/ - -#ifndef BABELD_BABEL_FILTER_H -#define BABELD_BABEL_FILTER_H - -#include -#include "prefix.h" -#include "babel_interface.h" - -int babel_filter(int output, const unsigned char *prefix, unsigned short plen, - unsigned int index); - -#endif /* BABELD_BABEL_FILTER_H */ diff --git a/babeld/babel_interface.c b/babeld/babel_interface.c deleted file mode 100644 index ace28127f..000000000 --- a/babeld/babel_interface.c +++ /dev/null @@ -1,1022 +0,0 @@ -/* - * This file is free software: you may copy, redistribute 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 file 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, see . - * - * This file incorporates work covered by the following copyright and - * permission notice: - * - -Copyright 2011 by Matthieu Boutier and Juliusz Chroboczek - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -*/ - -#include -#include "memory.h" -#include "log.h" -#include "command.h" -#include "prefix.h" -#include "vector.h" -#include "distribute.h" - -#include "babel_main.h" -#include "util.h" -#include "kernel.h" -#include "babel_interface.h" -#include "message.h" -#include "route.h" -#include "babel_zebra.h" -#include "neighbour.h" -#include "route.h" -#include "xroute.h" - - -#define IS_ENABLE(ifp) (babel_enable_if_lookup(ifp->name) >= 0) - -static int babel_enable_if_lookup (const char *ifname); -static int babel_enable_if_add (const char *ifname); -static int babel_enable_if_delete (const char *ifname); -static int interface_recalculate(struct interface *ifp); -static int interface_reset(struct interface *ifp); -static int babel_if_new_hook (struct interface *ifp); -static int babel_if_delete_hook (struct interface *ifp); -static int interface_config_write (struct vty *vty); -static babel_interface_nfo * babel_interface_allocate (void); -static void babel_interface_free (babel_interface_nfo *bi); - - -static vector babel_enable_if; /* enable interfaces (by cmd). */ -static struct cmd_node babel_interface_node = /* babeld's interface node. */ -{ - INTERFACE_NODE, - "%s(config-if)# ", - 1 /* VTYSH */ -}; - - -int -babel_interface_up (int cmd, struct zclient *client, zebra_size_t length) -{ - struct stream *s = NULL; - struct interface *ifp = NULL; - - debugf(BABEL_DEBUG_IF, "receive a 'interface up'"); - - s = zclient->ibuf; - ifp = zebra_interface_state_read(s); /* it updates iflist */ - - if (ifp == NULL) { - return 0; - } - - interface_recalculate(ifp); - return 0; -} - -int -babel_interface_down (int cmd, struct zclient *client, zebra_size_t length) -{ - struct stream *s = NULL; - struct interface *ifp = NULL; - - debugf(BABEL_DEBUG_IF, "receive a 'interface down'"); - - s = zclient->ibuf; - ifp = zebra_interface_state_read(s); /* it updates iflist */ - - if (ifp == NULL) { - return 0; - } - - interface_reset(ifp); - return 0; -} - -int -babel_interface_add (int cmd, struct zclient *client, zebra_size_t length) -{ - struct interface *ifp = NULL; - - debugf(BABEL_DEBUG_IF, "receive a 'interface add'"); - - /* read and add the interface in the iflist. */ - ifp = zebra_interface_add_read (zclient->ibuf); - - if (ifp == NULL) { - return 0; - } - - interface_recalculate(ifp); - return 0; -} - -int -babel_interface_delete (int cmd, struct zclient *client, zebra_size_t length) -{ - struct interface *ifp; - struct stream *s; - - debugf(BABEL_DEBUG_IF, "receive a 'interface delete'"); - - s = zclient->ibuf; - ifp = zebra_interface_state_read(s); /* it updates iflist */ - - if (ifp == NULL) - return 0; - - if (IS_ENABLE(ifp)) - interface_reset(ifp); - - /* To support pseudo interface do not free interface structure. */ - /* if_delete(ifp); */ - ifp->ifindex = IFINDEX_INTERNAL; - - return 0; -} - -int -babel_interface_address_add (int cmd, struct zclient *client, - zebra_size_t length) -{ - babel_interface_nfo *babel_ifp; - struct connected *ifc; - struct prefix *prefix; - - debugf(BABEL_DEBUG_IF, "receive a 'interface address add'"); - - ifc = zebra_interface_address_read (ZEBRA_INTERFACE_ADDRESS_ADD, - zclient->ibuf); - - if (ifc == NULL) - return 0; - - prefix = ifc->address; - - if (prefix->family == AF_INET) { - flush_interface_routes(ifc->ifp, 0); - babel_ifp = babel_get_if_nfo(ifc->ifp); - if (babel_ifp->ipv4 == NULL) { - babel_ifp->ipv4 = malloc(4); - if (babel_ifp->ipv4 == NULL) { - zlog_err("not einough memory"); - } else { - memcpy(babel_ifp->ipv4, &prefix->u.prefix4, 4); - } - } - } - - send_request(ifc->ifp, NULL, 0); - send_update(ifc->ifp, 0, NULL, 0); - - return 0; -} - -int -babel_interface_address_delete (int cmd, struct zclient *client, - zebra_size_t length) -{ - babel_interface_nfo *babel_ifp; - struct connected *ifc; - struct prefix *prefix; - - debugf(BABEL_DEBUG_IF, "receive a 'interface address add'"); - - ifc = zebra_interface_address_read (ZEBRA_INTERFACE_ADDRESS_ADD, - zclient->ibuf); - - if (ifc == NULL) - return 0; - - prefix = ifc->address; - - if (prefix->family == AF_INET) { - flush_interface_routes(ifc->ifp, 0); - babel_ifp = babel_get_if_nfo(ifc->ifp); - if (babel_ifp->ipv4 != NULL - && memcmp(babel_ifp->ipv4, &prefix->u.prefix4, 4) == 0) { - free(babel_ifp->ipv4); - babel_ifp->ipv4 = NULL; - } - } - - send_request(ifc->ifp, NULL, 0); - send_update(ifc->ifp, 0, NULL, 0); - - return 0; -} - -/* Lookup function. */ -static int -babel_enable_if_lookup (const char *ifname) -{ - unsigned int i; - char *str; - - for (i = 0; i < vector_active (babel_enable_if); i++) - if ((str = vector_slot (babel_enable_if, i)) != NULL) - if (strcmp (str, ifname) == 0) - return i; - return -1; -} - -/* Add interface to babel_enable_if. */ -static int -babel_enable_if_add (const char *ifname) -{ - int ret; - struct interface *ifp = NULL; - - ret = babel_enable_if_lookup (ifname); - if (ret >= 0) - return -1; - - vector_set (babel_enable_if, strdup (ifname)); - - ifp = if_lookup_by_name(ifname); - if (ifp != NULL) - interface_recalculate(ifp); - - return 1; -} - -/* Delete interface from babel_enable_if. */ -static int -babel_enable_if_delete (const char *ifname) -{ - int babel_enable_if_index; - char *str; - struct interface *ifp = NULL; - - babel_enable_if_index = babel_enable_if_lookup (ifname); - if (babel_enable_if_index < 0) - return -1; - - str = vector_slot (babel_enable_if, babel_enable_if_index); - free (str); - vector_unset (babel_enable_if, babel_enable_if_index); - - ifp = if_lookup_by_name(ifname); - if (ifp != NULL) - interface_reset(ifp); - - return 1; -} - -/* [Babel Command] Babel enable on specified interface or matched network. */ -DEFUN (babel_network, - babel_network_cmd, - "network IF_OR_ADDR", - "Enable Babel protocol on specified interface or network.\n" - "Interface or address") -{ - int ret; - struct prefix p; - - ret = str2prefix (argv[0], &p); - - /* Given string is: */ - if (ret) /* an IPv4 or v6 network */ - return CMD_ERR_NO_MATCH; /* not implemented yet */ - else /* an interface name */ - ret = babel_enable_if_add (argv[0]); - - if (ret < 0) { - vty_out (vty, "There is same network configuration %s%s", argv[0], - VTY_NEWLINE); - return CMD_WARNING; - } - - return CMD_SUCCESS; -} - -/* [Babel Command] Babel enable on specified interface or matched network. */ -DEFUN (no_babel_network, - no_babel_network_cmd, - "no network IF_OR_ADDR", - NO_STR - "Disable Babel protocol on specified interface or network.\n" - "Interface or address") -{ - int ret; - struct prefix p; - - ret = str2prefix (argv[0], &p); - - /* Given string is: */ - if (ret) /* an IPv4 or v6 network */ - return CMD_ERR_NO_MATCH; /* not implemented yet */ - else /* an interface name */ - ret = babel_enable_if_delete (argv[0]); - - if (ret < 0) { - vty_out (vty, "can't find network %s%s", argv[0], - VTY_NEWLINE); - return CMD_WARNING; - } - - return CMD_SUCCESS; -} - -/* There are a number of interface parameters that must be changed when - an interface becomes wired/wireless. In Quagga, they cannot be - configured separately. */ - -static void -babel_set_wired_internal(babel_interface_nfo *babel_ifp, int wired) -{ - if(wired) { - babel_ifp->flags |= BABEL_IF_WIRED; - babel_ifp->cost = 96; - babel_ifp->flags &= ~BABEL_IF_LQ; - } else { - babel_ifp->flags &= ~BABEL_IF_WIRED; - babel_ifp->cost = 256; - babel_ifp->flags |= BABEL_IF_LQ; - } - -} - -/* [Interface Command] Tell the interface is wire. */ -DEFUN (babel_set_wired, - babel_set_wired_cmd, - "babel wired", - "Babel interface commands\n" - "Enable wired optimisations") -{ - struct interface *ifp; - babel_interface_nfo *babel_ifp; - - ifp = vty->index; - babel_ifp = babel_get_if_nfo(ifp); - - assert (babel_ifp != NULL); - babel_set_wired_internal(babel_ifp, 1); - return CMD_SUCCESS; -} - -/* [Interface Command] Tell the interface is wireless (default). */ -DEFUN (babel_set_wireless, - babel_set_wireless_cmd, - "babel wireless", - "Babel interface commands\n" - "Disable wired optimiations (assume wireless)") -{ - struct interface *ifp; - babel_interface_nfo *babel_ifp; - - ifp = vty->index; - babel_ifp = babel_get_if_nfo(ifp); - - assert (babel_ifp != NULL); - babel_set_wired_internal(babel_ifp, 0); - return CMD_SUCCESS; -} - -/* [Interface Command] Enable split horizon. */ -DEFUN (babel_split_horizon, - babel_split_horizon_cmd, - "babel split-horizon", - "Babel interface commands\n" - "Enable split horizon processing") -{ - struct interface *ifp; - babel_interface_nfo *babel_ifp; - - ifp = vty->index; - babel_ifp = babel_get_if_nfo(ifp); - - assert (babel_ifp != NULL); - babel_ifp->flags |= BABEL_IF_SPLIT_HORIZON; - return CMD_SUCCESS; -} - -/* [Interface Command] Disable split horizon (default). */ -DEFUN (no_babel_split_horizon, - no_babel_split_horizon_cmd, - "no babel split-horizon", - NO_STR - "Babel interface commands\n" - "Disable split horizon processing") -{ - struct interface *ifp; - babel_interface_nfo *babel_ifp; - - ifp = vty->index; - babel_ifp = babel_get_if_nfo(ifp); - - assert (babel_ifp != NULL); - babel_ifp->flags &= ~BABEL_IF_SPLIT_HORIZON; - return CMD_SUCCESS; -} - -/* [Interface Command]. */ -DEFUN (babel_set_hello_interval, - babel_set_hello_interval_cmd, - "babel hello-interval <20-655340>", - "Babel interface commands\n" - "Time between scheduled hellos\n" - "Milliseconds\n") -{ - struct interface *ifp; - babel_interface_nfo *babel_ifp; - int interval; - - VTY_GET_INTEGER_RANGE("milliseconds", interval, argv[0], 20, 10 * 0xFFFE); - - ifp = vty->index; - babel_ifp = babel_get_if_nfo(ifp); - assert (babel_ifp != NULL); - - babel_ifp->hello_interval = interval; - return CMD_SUCCESS; -} - -/* [Interface Command]. */ -DEFUN (babel_set_update_interval, - babel_set_update_interval_cmd, - "babel update-interval <20-655340>", - "Babel interface commands\n" - "Time between scheduled updates\n" - "Milliseconds\n") -{ - struct interface *ifp; - babel_interface_nfo *babel_ifp; - int interval; - - VTY_GET_INTEGER_RANGE("milliseconds", interval, argv[0], 20, 10 * 0xFFFE); - - ifp = vty->index; - babel_ifp = babel_get_if_nfo(ifp); - assert (babel_ifp != NULL); - - babel_ifp->update_interval = interval; - return CMD_SUCCESS; -} - -/* This should be no more than half the hello interval, so that hellos - aren't sent late. The result is in milliseconds. */ -unsigned -jitter(babel_interface_nfo *babel_ifp, int urgent) -{ - unsigned interval = babel_ifp->hello_interval; - if(urgent) - interval = MIN(interval, 100); - else - interval = MIN(interval, 4000); - return roughly(interval) / 4; -} - -unsigned -update_jitter(babel_interface_nfo *babel_ifp, int urgent) -{ - unsigned interval = babel_ifp->hello_interval; - if(urgent) - interval = MIN(interval, 100); - else - interval = MIN(interval, 4000); - return roughly(interval); -} - -/* calculate babeld's specific datas of an interface (change when the interface - change) */ -static int -interface_recalculate(struct interface *ifp) -{ - babel_interface_nfo *babel_ifp = babel_get_if_nfo(ifp); - unsigned char *tmp = NULL; - int mtu, rc; - struct ipv6_mreq mreq; - - if (!IS_ENABLE(ifp)) - return -1; - - if (!if_is_operative(ifp) || !CHECK_FLAG(ifp->flags, IFF_RUNNING)) { - interface_reset(ifp); - return -1; - } - - babel_ifp->flags |= BABEL_IF_IS_UP; - - mtu = MIN(ifp->mtu, ifp->mtu6); - - /* We need to be able to fit at least two messages into a packet, - so MTUs below 116 require lower layer fragmentation. */ - /* In IPv6, the minimum MTU is 1280, and every host must be able - to reassemble up to 1500 bytes, but I'd rather not rely on this. */ - if(mtu < 128) { - debugf(BABEL_DEBUG_IF, "Suspiciously low MTU %d on interface %s (%d).", - mtu, ifp->name, ifp->ifindex); - mtu = 128; - } - - /* 40 for IPv6 header, 8 for UDP header, 12 for good luck. */ - babel_ifp->bufsize = mtu - sizeof(packet_header) - 60; - tmp = babel_ifp->sendbuf; - babel_ifp->sendbuf = realloc(babel_ifp->sendbuf, babel_ifp->bufsize); - if(babel_ifp->sendbuf == NULL) { - zlog_err("Couldn't reallocate sendbuf."); - free(tmp); - babel_ifp->bufsize = 0; - return -1; - } - tmp = NULL; - - resize_receive_buffer(mtu); - - memset(&mreq, 0, sizeof(mreq)); - memcpy(&mreq.ipv6mr_multiaddr, protocol_group, 16); - mreq.ipv6mr_interface = ifp->ifindex; - - rc = setsockopt(protocol_socket, IPPROTO_IPV6, IPV6_JOIN_GROUP, - (char*)&mreq, sizeof(mreq)); - if(rc < 0) { - zlog_err("setsockopt(IPV6_JOIN_GROUP) on interface '%s': %s", - ifp->name, safe_strerror(errno)); - /* This is probably due to a missing link-local address, - so down this interface, and wait until the main loop - tries to up it again. */ - interface_reset(ifp); - return -1; - } - - set_timeout(&babel_ifp->hello_timeout, babel_ifp->hello_interval); - set_timeout(&babel_ifp->update_timeout, babel_ifp->update_interval); - send_hello(ifp); - send_request(ifp, NULL, 0); - - update_interface_metric(ifp); - - debugf(BABEL_DEBUG_COMMON, - "Upped interface %s (%s, cost=%d, channel=%d%s).", - ifp->name, - (babel_ifp->flags & BABEL_IF_WIRED) ? "wired" : "wireless", - babel_ifp->cost, - babel_ifp->channel, - babel_ifp->ipv4 ? ", IPv4" : ""); - - if(rc > 0) - send_update(ifp, 0, NULL, 0); - - return 1; -} - -/* Reset the interface as it was new: it's not removed from the interface list, - and may be considered as a upped interface. */ -static int -interface_reset(struct interface *ifp) -{ - int rc; - struct ipv6_mreq mreq; - babel_interface_nfo *babel_ifp = babel_get_if_nfo(ifp); - - if (!(babel_ifp->flags & BABEL_IF_IS_UP)) - return 0; - - debugf(BABEL_DEBUG_IF, "interface reset: %s", ifp->name); - babel_ifp->flags &= ~BABEL_IF_IS_UP; - - flush_interface_routes(ifp, 0); - babel_ifp->buffered = 0; - babel_ifp->bufsize = 0; - free(babel_ifp->sendbuf); - babel_ifp->num_buffered_updates = 0; - babel_ifp->update_bufsize = 0; - if(babel_ifp->buffered_updates) - free(babel_ifp->buffered_updates); - babel_ifp->buffered_updates = NULL; - babel_ifp->sendbuf = NULL; - - if(ifp->ifindex > 0) { - memset(&mreq, 0, sizeof(mreq)); - memcpy(&mreq.ipv6mr_multiaddr, protocol_group, 16); - mreq.ipv6mr_interface = ifp->ifindex; - rc = setsockopt(protocol_socket, IPPROTO_IPV6, IPV6_LEAVE_GROUP, - (char*)&mreq, sizeof(mreq)); - if(rc < 0) - zlog_err("setsockopt(IPV6_LEAVE_GROUP) on interface '%s': %s", - ifp->name, safe_strerror(errno)); - } - - update_interface_metric(ifp); - - debugf(BABEL_DEBUG_COMMON,"Upped network %s (%s, cost=%d%s).", - ifp->name, - (babel_ifp->flags & BABEL_IF_WIRED) ? "wired" : "wireless", - babel_ifp->cost, - babel_ifp->ipv4 ? ", IPv4" : ""); - - return 1; -} - -/* Send retraction to all, and reset all interfaces statistics. */ -void -babel_interface_close_all(void) -{ - struct interface *ifp = NULL; - struct listnode *linklist_node = NULL; - - FOR_ALL_INTERFACES(ifp, linklist_node) { - if(!if_up(ifp)) - continue; - send_wildcard_retraction(ifp); - /* Make sure that we expire quickly from our neighbours' - association caches. */ - send_hello_noupdate(ifp, 10); - flushbuf(ifp); - usleep(roughly(1000)); - gettime(&babel_now); - } - FOR_ALL_INTERFACES(ifp, linklist_node) { - if(!if_up(ifp)) - continue; - /* Make sure they got it. */ - send_wildcard_retraction(ifp); - send_hello_noupdate(ifp, 1); - flushbuf(ifp); - usleep(roughly(10000)); - gettime(&babel_now); - interface_reset(ifp); - } -} - -/* return "true" if address is one of our ipv6 addresses */ -int -is_interface_ll_address(struct interface *ifp, const unsigned char *address) -{ - struct connected *connected; - struct listnode *node; - - if(!if_up(ifp)) - return 0; - - FOR_ALL_INTERFACES_ADDRESSES(ifp, connected, node) { - if(connected->address->family == AF_INET6 && - memcmp(&connected->address->u.prefix6, address, 16) == 0) - return 1; - } - - return 0; -} - -static void -show_babel_interface_sub (struct vty *vty, struct interface *ifp) -{ - int is_up; - babel_interface_nfo *babel_ifp; - - vty_out (vty, "%s is %s%s", ifp->name, - ((is_up = if_is_operative(ifp)) ? "up" : "down"), VTY_NEWLINE); - vty_out (vty, " ifindex %u, MTU %u bytes %s%s", - ifp->ifindex, ifp->mtu, if_flag_dump(ifp->flags), VTY_NEWLINE); - - if (babel_enable_if_lookup (ifp->name) < 0) - { - vty_out (vty, " Babel protocol is not enabled on this interface%s", VTY_NEWLINE); - return; - } - if (!is_up) - { - vty_out (vty, " Babel protocol is enabled, but not running on this interface%s", VTY_NEWLINE); - return; - } - babel_ifp = babel_get_if_nfo (ifp); - vty_out (vty, " Babel protocol is running on this interface%s", VTY_NEWLINE); - vty_out (vty, " Operating mode is \"%s\"%s", - CHECK_FLAG (babel_ifp->flags, BABEL_IF_WIRED) ? "wired" : "wireless", VTY_NEWLINE); - vty_out (vty, " Split horizon mode is %s%s", - CHECK_FLAG (babel_ifp->flags, BABEL_IF_SPLIT_HORIZON) ? "On" : "Off", VTY_NEWLINE); - vty_out (vty, " Hello interval is %u ms%s", babel_ifp->hello_interval, VTY_NEWLINE); - vty_out (vty, " Update interval is %u ms%s", babel_ifp->update_interval, VTY_NEWLINE); -} - -DEFUN (show_babel_interface, - show_babel_interface_cmd, - "show babel interface [INTERFACE]", - SHOW_STR - IP_STR - "Babel information\n" - "Interface information\n" - "Interface name\n") -{ - struct interface *ifp; - struct listnode *node; - - if (argc == 0) - { - for (ALL_LIST_ELEMENTS_RO (iflist, node, ifp)) - show_babel_interface_sub (vty, ifp); - return CMD_SUCCESS; - } - if ((ifp = if_lookup_by_name (argv[0])) == NULL) - { - vty_out (vty, "No such interface name%s", VTY_NEWLINE); - return CMD_WARNING; - } - show_babel_interface_sub (vty, ifp); - return CMD_SUCCESS; -} - -static void -show_babel_neighbour_sub (struct vty *vty, struct neighbour *neigh) -{ - vty_out (vty, - "Neighbour %s dev %s reach %04x rxcost %d txcost %d %s.%s", - format_address(neigh->address), - neigh->ifp->name, - neigh->reach, - neighbour_rxcost(neigh), - neigh->txcost, - if_up(neigh->ifp) ? "" : " (down)", - VTY_NEWLINE); -} - -DEFUN (show_babel_neighbour, - show_babel_neighbour_cmd, - "show babel neighbour [INTERFACE]", - SHOW_STR - IP_STR - "Babel information\n" - "Print neighbours\n" - "Interface name\n") -{ - struct neighbour *neigh; - struct interface *ifp; - - if (argc == 0) { - FOR_ALL_NEIGHBOURS(neigh) { - show_babel_neighbour_sub(vty, neigh); - } - return CMD_SUCCESS; - } - if ((ifp = if_lookup_by_name (argv[0])) == NULL) - { - vty_out (vty, "No such interface name%s", VTY_NEWLINE); - return CMD_WARNING; - } - FOR_ALL_NEIGHBOURS(neigh) { - if(ifp->ifindex == neigh->ifp->ifindex) { - show_babel_neighbour_sub(vty, neigh); - } - } - return CMD_SUCCESS; -} - -static void -show_babel_routes_sub (struct babel_route *route, void *closure) -{ - struct vty *vty = (struct vty*) closure; - const unsigned char *nexthop = - memcmp(route->nexthop, route->neigh->address, 16) == 0 ? - NULL : route->nexthop; - char channels[100]; - - if(route->channels[0] == 0) - channels[0] = '\0'; - else { - int k, j = 0; - snprintf(channels, 100, " chan ("); - j = strlen(channels); - for(k = 0; k < DIVERSITY_HOPS; k++) { - if(route->channels[k] == 0) - break; - if(k > 0) - channels[j++] = ','; - snprintf(channels + j, 100 - j, "%d", route->channels[k]); - j = strlen(channels); - } - snprintf(channels + j, 100 - j, ")"); - if(k == 0) - channels[0] = '\0'; - } - - vty_out(vty, - "%s metric %d refmetric %d id %s seqno %d%s age %d " - "via %s neigh %s%s%s%s%s", - format_prefix(route->src->prefix, route->src->plen), - route_metric(route), route->refmetric, - format_eui64(route->src->id), - (int)route->seqno, - channels, - (int)(babel_now.tv_sec - route->time), - route->neigh->ifp->name, - format_address(route->neigh->address), - nexthop ? " nexthop " : "", - nexthop ? format_address(nexthop) : "", - route->installed ? " (installed)" : - route_feasible(route) ? " (feasible)" : "", - VTY_NEWLINE); -} - -static void -show_babel_xroutes_sub (struct xroute *xroute, void *closure) -{ - struct vty *vty = (struct vty *) closure; - vty_out(vty, "%s metric %d (exported)%s", - format_prefix(xroute->prefix, xroute->plen), - xroute->metric, - VTY_NEWLINE); -} - -DEFUN (show_babel_database, - show_babel_database_cmd, - "show babel database", - SHOW_STR - IP_STR - "Babel information\n" - "Database information\n" - "No attributes\n") -{ - for_all_routes(show_babel_routes_sub, vty); - for_all_xroutes(show_babel_xroutes_sub, vty); - return CMD_SUCCESS; -} - -DEFUN (show_babel_parameters, - show_babel_parameters_cmd, - "show babel parameters", - SHOW_STR - IP_STR - "Babel information\n" - "Configuration information\n" - "No attributes\n") -{ - vty_out(vty, " -- Babel running configuration --%s", VTY_NEWLINE); - show_babel_main_configuration(vty); - vty_out(vty, " -- distribution lists --%s", VTY_NEWLINE); - config_show_distribute(vty); - - return CMD_SUCCESS; -} - -void -babel_if_init () -{ - /* initialize interface list */ - if_init(); - if_add_hook (IF_NEW_HOOK, babel_if_new_hook); - if_add_hook (IF_DELETE_HOOK, babel_if_delete_hook); - - babel_enable_if = vector_init (1); - - /* install interface node and commands */ - install_element (CONFIG_NODE, &interface_cmd); - install_element (CONFIG_NODE, &no_interface_cmd); - install_node (&babel_interface_node, interface_config_write); - install_default(INTERFACE_NODE); - install_element(INTERFACE_NODE, &interface_cmd); - install_element(INTERFACE_NODE, &no_interface_cmd); - - install_element(BABEL_NODE, &babel_network_cmd); - install_element(BABEL_NODE, &no_babel_network_cmd); - install_element(INTERFACE_NODE, &babel_split_horizon_cmd); - install_element(INTERFACE_NODE, &no_babel_split_horizon_cmd); - install_element(INTERFACE_NODE, &babel_set_wired_cmd); - install_element(INTERFACE_NODE, &babel_set_wireless_cmd); - install_element(INTERFACE_NODE, &babel_set_hello_interval_cmd); - install_element(INTERFACE_NODE, &babel_set_update_interval_cmd); - - /* "show babel ..." commands */ - install_element(VIEW_NODE, &show_babel_interface_cmd); - install_element(ENABLE_NODE, &show_babel_interface_cmd); - install_element(VIEW_NODE, &show_babel_neighbour_cmd); - install_element(ENABLE_NODE, &show_babel_neighbour_cmd); - install_element(VIEW_NODE, &show_babel_database_cmd); - install_element(ENABLE_NODE, &show_babel_database_cmd); - install_element(VIEW_NODE, &show_babel_parameters_cmd); - install_element(ENABLE_NODE, &show_babel_parameters_cmd); -} - -/* hooks: functions called respectively when struct interface is - created or deleted. */ -static int -babel_if_new_hook (struct interface *ifp) -{ - ifp->info = babel_interface_allocate(); - return 0; -} - -static int -babel_if_delete_hook (struct interface *ifp) -{ - babel_interface_free(ifp->info); - ifp->info = NULL; - return 0; -} - -/* Output an "interface" section for each of the known interfaces with -babeld-specific statement lines where appropriate. */ -static int -interface_config_write (struct vty *vty) -{ - struct listnode *node; - struct interface *ifp; - int write = 0; - - for (ALL_LIST_ELEMENTS_RO (iflist, node, ifp)) { - vty_out (vty, "interface %s%s", ifp->name, - VTY_NEWLINE); - if (ifp->desc) - vty_out (vty, " description %s%s", ifp->desc, - VTY_NEWLINE); - if (IS_ENABLE (ifp)) - { - babel_interface_nfo *babel_ifp = babel_get_if_nfo (ifp); - /* wireless/no split-horizon is the default */ - if (CHECK_FLAG (babel_ifp->flags, BABEL_IF_WIRED)) - { - vty_out (vty, " babel wired%s", VTY_NEWLINE); - write++; - } - if (CHECK_FLAG (babel_ifp->flags, BABEL_IF_SPLIT_HORIZON)) - { - vty_out (vty, " babel split-horizon%s", VTY_NEWLINE); - write++; - } - if (babel_ifp->hello_interval != BABEL_DEFAULT_HELLO_INTERVAL) - { - vty_out (vty, " babel hello-interval %u%s", babel_ifp->hello_interval, VTY_NEWLINE); - write++; - } - if (babel_ifp->update_interval != BABEL_DEFAULT_UPDATE_INTERVAL) - { - vty_out (vty, " babel update-interval %u%s", babel_ifp->update_interval, VTY_NEWLINE); - write++; - } - } - vty_out (vty, "!%s", VTY_NEWLINE); - write++; - } - return write; -} - -/* Output a "network" statement line for each of the enabled interfaces. */ -int -babel_enable_if_config_write (struct vty * vty) -{ - unsigned int i, lines = 0; - char *str; - - for (i = 0; i < vector_active (babel_enable_if); i++) - if ((str = vector_slot (babel_enable_if, i)) != NULL) - { - vty_out (vty, " network %s%s", str, VTY_NEWLINE); - lines++; - } - return lines; -} - -/* functions to allocate or free memory for a babel_interface_nfo, filling - needed fields */ -static babel_interface_nfo * -babel_interface_allocate (void) -{ - babel_interface_nfo *babel_ifp; - babel_ifp = XMALLOC(MTYPE_BABEL_IF, sizeof(babel_interface_nfo)); - if(babel_ifp == NULL) - return NULL; - - /* Here are set the default values for an interface. */ - memset(babel_ifp, 0, sizeof(babel_interface_nfo)); - /* All flags are unset */ - babel_ifp->bucket_time = babel_now.tv_sec; - babel_ifp->bucket = BUCKET_TOKENS_MAX; - babel_ifp->hello_seqno = (random() & 0xFFFF); - babel_ifp->hello_interval = BABEL_DEFAULT_HELLO_INTERVAL; - babel_ifp->update_interval = BABEL_DEFAULT_UPDATE_INTERVAL; - babel_ifp->channel = BABEL_IF_CHANNEL_INTERFERING; - babel_set_wired_internal(babel_ifp, 0); - - return babel_ifp; -} - -static void -babel_interface_free (babel_interface_nfo *babel_ifp) -{ - XFREE(MTYPE_BABEL_IF, babel_ifp); -} diff --git a/babeld/babel_interface.h b/babeld/babel_interface.h deleted file mode 100644 index 94fd8e5d7..000000000 --- a/babeld/babel_interface.h +++ /dev/null @@ -1,152 +0,0 @@ -/* - * This file is free software: you may copy, redistribute 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 file 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, see . - * - * This file incorporates work covered by the following copyright and - * permission notice: - * -Copyright 2011 by Matthieu Boutier and Juliusz Chroboczek - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -*/ - -#ifndef BABEL_INTERFACE_H -#define BABEL_INTERFACE_H - -#include -#include "zclient.h" -#include "vty.h" - -#define CONFIG_DEFAULT 0 -#define CONFIG_NO 1 -#define CONFIG_YES 2 - -/* babeld interface informations */ -struct babel_interface { - unsigned short flags; /* see below */ - unsigned short cost; - int channel; - struct timeval hello_timeout; - struct timeval update_timeout; - struct timeval flush_timeout; - struct timeval update_flush_timeout; - unsigned char *ipv4; - int buffered; - int bufsize; - char have_buffered_hello; - char have_buffered_id; - char have_buffered_nh; - char have_buffered_prefix; - unsigned char buffered_id[16]; - unsigned char buffered_nh[4]; - unsigned char buffered_prefix[16]; - unsigned char *sendbuf; - struct buffered_update *buffered_updates; - int num_buffered_updates; - int update_bufsize; - time_t bucket_time; - unsigned int bucket; - time_t last_update_time; - unsigned short hello_seqno; - unsigned hello_interval; - unsigned update_interval; - - /* For filter type slot. */ -#define BABEL_FILTER_IN 0 -#define BABEL_FILTER_OUT 1 -#define BABEL_FILTER_MAX 2 - struct access_list *list[BABEL_FILTER_MAX]; /* Access-list. */ - struct prefix_list *prefix[BABEL_FILTER_MAX]; /* Prefix-list. */ -}; - -typedef struct babel_interface babel_interface_nfo; -static inline babel_interface_nfo* babel_get_if_nfo(struct interface *ifp) -{ - return ((babel_interface_nfo*) ifp->info); -} - -/* babel_interface_nfo flags */ -#define BABEL_IF_IS_UP (1 << 0) -#define BABEL_IF_WIRED (1 << 1) -#define BABEL_IF_SPLIT_HORIZON (1 << 2) -#define BABEL_IF_LQ (1 << 3) -#define BABEL_IF_FARAWAY (1 << 4) - -/* Only INTERFERING can appear on the wire. */ -#define BABEL_IF_CHANNEL_UNKNOWN 0 -#define BABEL_IF_CHANNEL_INTERFERING 255 -#define BABEL_IF_CHANNEL_NONINTERFERING -2 - -static inline int -if_up(struct interface *ifp) -{ - return (if_is_operative(ifp) && - ifp->connected != NULL && - (babel_get_if_nfo(ifp)->flags & BABEL_IF_IS_UP)); -} - -/* types: - struct interface _ifp, struct listnode node */ -#define FOR_ALL_INTERFACES(_ifp, _node) \ - for(ALL_LIST_ELEMENTS_RO(iflist, _node, _ifp)) - -/* types: - struct interface *ifp, struct connected *_connected, struct listnode *node */ -#define FOR_ALL_INTERFACES_ADDRESSES(ifp, _connected, _node) \ - for(ALL_LIST_ELEMENTS_RO(ifp->connected, _node, _connected)) - -struct buffered_update { - unsigned char id[8]; - unsigned char prefix[16]; - unsigned char plen; - unsigned char pad[3]; -}; - - -/* init function */ -void babel_if_init(void); - -/* Callback functions for zebra client */ -int babel_interface_up (int, struct zclient *, zebra_size_t); -int babel_interface_down (int, struct zclient *, zebra_size_t); -int babel_interface_add (int, struct zclient *, zebra_size_t); -int babel_interface_delete (int, struct zclient *, zebra_size_t); -int babel_interface_address_add (int, struct zclient *, zebra_size_t); -int babel_interface_address_delete (int, struct zclient *, zebra_size_t); - -unsigned jitter(babel_interface_nfo *, int); -unsigned update_jitter(babel_interface_nfo *babel_ifp, int urgent); -/* return "true" if "address" is one of our ipv6 addresses */ -int is_interface_ll_address(struct interface *ifp, const unsigned char *address); -/* Send retraction to all, and reset all interfaces statistics. */ -void babel_interface_close_all(void); -extern int babel_enable_if_config_write (struct vty *); - - -#endif diff --git a/babeld/babel_main.c b/babeld/babel_main.c deleted file mode 100644 index 2f3b5552e..000000000 --- a/babeld/babel_main.c +++ /dev/null @@ -1,532 +0,0 @@ -/* - * This file is free software: you may copy, redistribute 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 file 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, see . - * - * This file incorporates work covered by the following copyright and - * permission notice: - * - -Copyright 2011 by Matthieu Boutier and Juliusz Chroboczek - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -*/ - -/* include zebra library */ -#include -#include "getopt.h" -#include "if.h" -#include "log.h" -#include "thread.h" -#include "privs.h" -#include "sigevent.h" -#include "version.h" -#include "command.h" -#include "vty.h" -#include "memory.h" - -#include "babel_main.h" -#include "babeld.h" -#include "util.h" -#include "kernel.h" -#include "babel_interface.h" -#include "neighbour.h" -#include "route.h" -#include "xroute.h" -#include "message.h" -#include "resend.h" -#include "babel_zebra.h" - - -static void babel_init (int argc, char **argv); -static char *babel_get_progname(char *argv_0); -static void babel_fail(void); -static void babel_init_random(void); -static void babel_replace_by_null(int fd); -static void babel_init_signals(void); -static void babel_exit_properly(void); -static void babel_save_state_file(void); - - -struct thread_master *master; /* quagga's threads handler */ -struct timeval babel_now; /* current time */ - -unsigned char myid[8]; /* unique id (mac address of an interface) */ -int debug = 0; - -int resend_delay = -1; -static const char *pidfile = PATH_BABELD_PID; - -const unsigned char zeroes[16] = {0}; -const unsigned char ones[16] = - {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; - -static const char *state_file = DAEMON_VTY_DIR "/babel-state"; - -unsigned char protocol_group[16]; /* babel's link-local multicast address */ -int protocol_port; /* babel's port */ -int protocol_socket = -1; /* socket: communicate with others babeld */ - -static char babel_config_default[] = SYSCONFDIR BABEL_DEFAULT_CONFIG; -static char *babel_config_file = NULL; -static char *babel_vty_addr = NULL; -static int babel_vty_port = BABEL_VTY_PORT; - -/* Babeld options. */ -struct option longopts[] = -{ - { "daemon", no_argument, NULL, 'd'}, - { "config_file", required_argument, NULL, 'f'}, - { "pid_file", required_argument, NULL, 'i'}, - { "socket", required_argument, NULL, 'z'}, - { "help", no_argument, NULL, 'h'}, - { "vty_addr", required_argument, NULL, 'A'}, - { "vty_port", required_argument, NULL, 'P'}, - { "user", required_argument, NULL, 'u'}, - { "group", required_argument, NULL, 'g'}, - { "version", no_argument, NULL, 'v'}, - { 0 } -}; - -/* babeld privileges */ -static zebra_capabilities_t _caps_p [] = -{ - ZCAP_NET_RAW, - ZCAP_BIND -}; -static struct zebra_privs_t babeld_privs = -{ -#if defined(QUAGGA_USER) - .user = QUAGGA_USER, -#endif -#if defined QUAGGA_GROUP - .group = QUAGGA_GROUP, -#endif -#ifdef VTY_GROUP - .vty_group = VTY_GROUP, -#endif - .caps_p = _caps_p, - .cap_num_p = 2, - .cap_num_i = 0 -}; - - -int -main(int argc, char **argv) -{ - struct thread thread; - /* and print banner too */ - babel_init(argc, argv); - while (thread_fetch (master, &thread)) { - thread_call (&thread); - } - return 0; -} - -static void -babel_usage (char *progname, int status) -{ - if (status != 0) - fprintf (stderr, "Try `%s --help' for more information.\n", progname); - else - { - printf ("Usage : %s [OPTION...]\n\ -Daemon which manages Babel routing protocol.\n\n\ --d, --daemon Runs 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\ --u, --user User to run as\n\ --g, --group Group to run as\n\ --v, --version Print program version\n\ --h, --help Display this help and exit\n\ -\n\ -Report bugs to %s\n", progname, ZEBRA_BUG_ADDRESS); - } - exit (status); -} - -/* make initialisations witch don't need infos about kernel(interfaces, etc.) */ -static void -babel_init(int argc, char **argv) -{ - int rc, opt; - int do_daemonise = 0; - char *progname = NULL; - - /* Set umask before anything for security */ - umask (0027); - progname = babel_get_progname(argv[0]); - - /* set default log (lib/log.h) */ - zlog_default = openzlog(progname, ZLOG_BABEL, - LOG_CONS|LOG_NDELAY|LOG_PID, LOG_DAEMON); - /* set log destination as stdout until the config file is read */ - zlog_set_level(NULL, ZLOG_DEST_STDOUT, LOG_WARNING); - - babel_init_random(); - - /* set the Babel's default link-local multicast address and Babel's port */ - parse_address("ff02:0:0:0:0:0:1:6", protocol_group, NULL); - protocol_port = 6696; - - /* get options */ - while(1) { - opt = getopt_long(argc, argv, "df:i:z:hA:P:u:g:v", longopts, 0); - if(opt < 0) - break; - - switch(opt) { - case 0: - break; - case 'd': - do_daemonise = -1; - break; - case 'f': - babel_config_file = optarg; - break; - case 'i': - pidfile = optarg; - break; - case 'z': - zclient_serv_path_set (optarg); - break; - case 'A': - babel_vty_addr = optarg; - break; - case 'P': - babel_vty_port = atoi (optarg); - if (babel_vty_port <= 0 || babel_vty_port > 0xffff) - babel_vty_port = BABEL_VTY_PORT; - break; - case 'u': - babeld_privs.user = optarg; - break; - case 'g': - babeld_privs.group = optarg; - break; - case 'v': - print_version (progname); - exit (0); - break; - case 'h': - babel_usage (progname, 0); - break; - default: - babel_usage (progname, 1); - break; - } - } - - /* create the threads handler */ - master = thread_master_create (); - - /* Library inits. */ - zprivs_init (&babeld_privs); - babel_init_signals(); - cmd_init (1); - vty_init (master); - memory_init (); - - resend_delay = BABEL_DEFAULT_RESEND_DELAY; - - babel_replace_by_null(STDIN_FILENO); - - if (do_daemonise && daemonise() < 0) { - zlog_err("daemonise: %s", safe_strerror(errno)); - exit (1); - } - - /* write pid file */ - if (pid_output(pidfile) < 0) { - zlog_err("error while writing pidfile"); - exit (1); - }; - - /* init some quagga's dependencies, and babeld's commands */ - babeld_quagga_init(); - /* init zebra client's structure and it's commands */ - /* 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); - - /* Create VTY socket */ - vty_serv_sock (babel_vty_addr, babel_vty_port, BABEL_VTYSH_PATH); - - /* init buffer */ - rc = resize_receive_buffer(1500); - if(rc < 0) - babel_fail(); - - schedule_neighbours_check(5000, 1); - - zlog_notice ("BABELd %s starting: vty@%d", BABEL_VERSION, babel_vty_port); -} - -/* return the progname (without path, example: "./x/progname" --> "progname") */ -static char * -babel_get_progname(char *argv_0) { - char *p = strrchr (argv_0, '/'); - return (p ? ++p : argv_0); -} - -static void -babel_fail(void) -{ - exit(1); -} - -/* initialize random value, and set 'babel_now' by the way. */ -static void -babel_init_random(void) -{ - gettime(&babel_now); - int rc; - unsigned int seed; - - rc = read_random_bytes(&seed, sizeof(seed)); - if(rc < 0) { - zlog_err("read(random): %s", safe_strerror(errno)); - seed = 42; - } - - seed ^= (babel_now.tv_sec ^ babel_now.tv_usec); - srandom(seed); -} - -/* - close fd, and replace it by "/dev/null" - exit if error - */ -static void -babel_replace_by_null(int fd) -{ - int fd_null; - int rc; - - fd_null = open("/dev/null", O_RDONLY); - if(fd_null < 0) { - zlog_err("open(null): %s", safe_strerror(errno)); - exit(1); - } - - rc = dup2(fd_null, fd); - if(rc < 0) { - zlog_err("dup2(null, 0): %s", safe_strerror(errno)); - exit(1); - } - - close(fd_null); -} - -/* - Load the state file: check last babeld's running state, usefull in case of - "/etc/init.d/babeld restart" - */ -void -babel_load_state_file(void) -{ - int fd; - int rc; - - fd = open(state_file, O_RDONLY); - if(fd < 0 && errno != ENOENT) - zlog_err("open(babel-state: %s)", safe_strerror(errno)); - rc = unlink(state_file); - if(fd >= 0 && rc < 0) { - zlog_err("unlink(babel-state): %s", safe_strerror(errno)); - /* If we couldn't unlink it, it's probably stale. */ - close(fd); - fd = -1; - } - if(fd >= 0) { - char buf[100]; - char buf2[100]; - int s; - long t; - rc = read(fd, buf, 99); - if(rc < 0) { - zlog_err("read(babel-state): %s", safe_strerror(errno)); - } else { - buf[rc] = '\0'; - rc = sscanf(buf, "%99s %d %ld\n", buf2, &s, &t); - if(rc == 3 && s >= 0 && s <= 0xFFFF) { - unsigned char sid[8]; - rc = parse_eui64(buf2, sid); - if(rc < 0) { - zlog_err("Couldn't parse babel-state."); - } else { - struct timeval realnow; - debugf(BABEL_DEBUG_COMMON, - "Got %s %d %ld from babel-state.", - format_eui64(sid), s, t); - gettimeofday(&realnow, NULL); - if(memcmp(sid, myid, 8) == 0) - myseqno = seqno_plus(s, 1); - else - zlog_err("ID mismatch in babel-state. id=%s; old=%s", - format_eui64(myid), - format_eui64(sid)); - } - } else { - zlog_err("Couldn't parse babel-state."); - } - } - close(fd); - fd = -1; - } -} - -static void -babel_sigexit(void) -{ - zlog_notice("Terminating on signal"); - - babel_exit_properly(); -} - -static void -babel_sigusr1 (void) -{ - zlog_rotate (NULL); -} - -static void -babel_init_signals(void) -{ - static struct quagga_signal_t babel_signals[] = - { - { - .signal = SIGUSR1, - .handler = &babel_sigusr1, - }, - { - .signal = SIGINT, - .handler = &babel_sigexit, - }, - { - .signal = SIGTERM, - .handler = &babel_sigexit, - }, - }; - - signal_init (master, Q_SIGC(babel_signals), babel_signals); -} - -static void -babel_exit_properly(void) -{ - debugf(BABEL_DEBUG_COMMON, "Exiting..."); - usleep(roughly(10000)); - gettime(&babel_now); - - /* Uninstall and flush all routes. */ - debugf(BABEL_DEBUG_COMMON, "Uninstall routes."); - flush_all_routes(); - babel_interface_close_all(); - babel_zebra_close_connexion(); - babel_save_state_file(); - debugf(BABEL_DEBUG_COMMON, "Remove pid file."); - if(pidfile) - unlink(pidfile); - debugf(BABEL_DEBUG_COMMON, "Done."); - - exit(0); -} - -static void -babel_save_state_file(void) -{ - int fd; - int rc; - - debugf(BABEL_DEBUG_COMMON, "Save state file."); - fd = open(state_file, O_WRONLY | O_TRUNC | O_CREAT, 0644); - if(fd < 0) { - zlog_err("creat(babel-state): %s", safe_strerror(errno)); - unlink(state_file); - } else { - struct timeval realnow; - char buf[100]; - gettimeofday(&realnow, NULL); - rc = snprintf(buf, 100, "%s %d %ld\n", - format_eui64(myid), (int)myseqno, - (long)realnow.tv_sec); - if(rc < 0 || rc >= 100) { - zlog_err("write(babel-state): overflow."); - unlink(state_file); - } else { - rc = write(fd, buf, rc); - if(rc < 0) { - zlog_err("write(babel-state): %s", safe_strerror(errno)); - unlink(state_file); - } - fsync(fd); - } - close(fd); - } -} - -void -show_babel_main_configuration (struct vty *vty) -{ - vty_out(vty, - "pid file = %s%s" - "state file = %s%s" - "configuration file = %s%s" - "protocol informations:%s" - " multicast address = %s%s" - " port = %d%s" - "vty address = %s%s" - "vty port = %d%s" - "id = %s%s" - "allow_duplicates = %s%s" - "kernel_metric = %d%s", - pidfile, VTY_NEWLINE, - state_file, VTY_NEWLINE, - babel_config_file ? babel_config_file : babel_config_default, - VTY_NEWLINE, - VTY_NEWLINE, - format_address(protocol_group), VTY_NEWLINE, - protocol_port, VTY_NEWLINE, - babel_vty_addr ? babel_vty_addr : "None", - VTY_NEWLINE, - babel_vty_port, VTY_NEWLINE, - format_eui64(myid), VTY_NEWLINE, - format_bool(allow_duplicates), VTY_NEWLINE, - kernel_metric, VTY_NEWLINE); -} diff --git a/babeld/babel_main.h b/babeld/babel_main.h deleted file mode 100644 index a4038decb..000000000 --- a/babeld/babel_main.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * This file is free software: you may copy, redistribute 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 file 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, see . - * - * This file incorporates work covered by the following copyright and - * permission notice: - * -Copyright 2011 by Matthieu Boutier and Juliusz Chroboczek - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -*/ - -#include "vty.h" - -extern struct timeval babel_now; /* current time */ -extern struct thread_master *master; /* quagga's threads handler */ -extern int debug; -extern int resend_delay; - -extern unsigned char myid[8]; - -extern const unsigned char zeroes[16], ones[16]; - -extern int protocol_port; -extern unsigned char protocol_group[16]; -extern int protocol_socket; -extern int kernel_socket; -extern int max_request_hopcount; - -void babel_load_state_file(void); -void show_babel_main_configuration (struct vty *vty); diff --git a/babeld/babel_zebra.c b/babeld/babel_zebra.c deleted file mode 100644 index 75a1e6a84..000000000 --- a/babeld/babel_zebra.c +++ /dev/null @@ -1,378 +0,0 @@ -/* - * This file is free software: you may copy, redistribute 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 file 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, see . - * - * This file incorporates work covered by the following copyright and - * permission notice: - * -Copyright 2011 by Matthieu Boutier and Juliusz Chroboczek - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -*/ - -/* quagga's includes */ -#include -#include "command.h" -#include "zclient.h" -#include "stream.h" - -/* babel's includes*/ -#include "babel_zebra.h" -#include "babel_interface.h" -#include "xroute.h" -#include "util.h" - -void babelz_zebra_init(void); - - -/* we must use a pointer because of zclient.c's functions (new, free). */ -struct zclient *zclient; -static int zebra_config_write (struct vty *vty); - -/* Debug types */ -static struct { - int type; - int str_min_len; - const char *str; -} debug_type[] = { - {BABEL_DEBUG_COMMON, 1, "common"}, - {BABEL_DEBUG_KERNEL, 1, "kernel"}, - {BABEL_DEBUG_FILTER, 1, "filter"}, - {BABEL_DEBUG_TIMEOUT, 1, "timeout"}, - {BABEL_DEBUG_IF, 1, "interface"}, - {BABEL_DEBUG_ROUTE, 1, "route"}, - {BABEL_DEBUG_ALL, 1, "all"}, - {0, 0, NULL} -}; - -/* Zebra node structure. */ -struct cmd_node zebra_node = -{ - ZEBRA_NODE, - "%s(config-router)# ", - 1 /* vtysh? yes */ -}; - - -/* Zebra route add and delete treatment (ipv6). */ -static int -babel_zebra_read_ipv6 (int command, struct zclient *zclient, - zebra_size_t length) -{ - struct stream *s; - struct zapi_ipv6 api; - unsigned long ifindex = -1; - struct in6_addr nexthop; - struct prefix_ipv6 prefix; - - s = zclient->ibuf; - ifindex = 0; - memset (&nexthop, 0, sizeof (struct in6_addr)); - memset (&api, 0, sizeof(struct zapi_ipv6)); - memset (&prefix, 0, sizeof (struct prefix_ipv6)); - - /* Type, flags, message. */ - api.type = stream_getc (s); - api.flags = stream_getc (s); - api.message = stream_getc (s); - - /* IPv6 prefix. */ - prefix.family = AF_INET6; - prefix.prefixlen = stream_getc (s); - stream_get (&prefix.prefix, s, PSIZE (prefix.prefixlen)); - - /* Nexthop, ifindex, distance, metric. */ - if (CHECK_FLAG (api.message, ZAPI_MESSAGE_NEXTHOP)) { - api.nexthop_num = stream_getc (s); - stream_get (&nexthop, s, sizeof(nexthop)); - } - if (CHECK_FLAG (api.message, ZAPI_MESSAGE_IFINDEX)) { - api.ifindex_num = stream_getc (s); - ifindex = stream_getl (s); - } - if (CHECK_FLAG (api.message, ZAPI_MESSAGE_DISTANCE)) - api.distance = stream_getc (s); - else - api.distance = 0; - if (CHECK_FLAG (api.message, ZAPI_MESSAGE_METRIC)) - api.metric = stream_getl (s); - else - api.metric = 0; - - if (command == ZEBRA_IPV6_ROUTE_ADD) - babel_ipv6_route_add(&api, &prefix, ifindex, &nexthop); - else - babel_ipv6_route_delete(&api, &prefix, ifindex); - - return 0; -} - -static int -babel_zebra_read_ipv4 (int command, struct zclient *zclient, - zebra_size_t length) -{ - struct stream *s; - struct zapi_ipv4 api; - unsigned long ifindex = -1; - struct in_addr nexthop; - struct prefix_ipv4 prefix; - - s = zclient->ibuf; - ifindex = 0; - memset (&nexthop, 0, sizeof (struct in_addr)); - memset (&api, 0, sizeof(struct zapi_ipv4)); - memset (&prefix, 0, sizeof (struct prefix_ipv4)); - - /* Type, flags, message. */ - api.type = stream_getc (s); - api.flags = stream_getc (s); - api.message = stream_getc (s); - - /* IPv6 prefix. */ - prefix.family = AF_INET; - prefix.prefixlen = stream_getc (s); - stream_get (&prefix.prefix, s, PSIZE (prefix.prefixlen)); - - /* Nexthop, ifindex, distance, metric. */ - if (CHECK_FLAG (api.message, ZAPI_MESSAGE_NEXTHOP)) { - api.nexthop_num = stream_getc (s); - stream_get (&nexthop, s, sizeof(nexthop)); - } - if (CHECK_FLAG (api.message, ZAPI_MESSAGE_IFINDEX)) { - api.ifindex_num = stream_getc (s); - ifindex = stream_getl (s); - } - if (CHECK_FLAG (api.message, ZAPI_MESSAGE_DISTANCE)) - api.distance = stream_getc (s); - else - api.distance = 0; - if (CHECK_FLAG (api.message, ZAPI_MESSAGE_METRIC)) - api.metric = stream_getl (s); - else - api.metric = 0; - - if (command == ZEBRA_IPV6_ROUTE_ADD) { - babel_ipv4_route_add(&api, &prefix, ifindex, &nexthop); - } else { - babel_ipv4_route_delete(&api, &prefix, ifindex); - } - - return 0; -} - -/* [Babel Command] */ -DEFUN (babel_redistribute_type, - babel_redistribute_type_cmd, - "redistribute " QUAGGA_REDIST_STR_BABELD, - "Redistribute\n" - QUAGGA_REDIST_HELP_STR_BABELD) -{ - int type; - - type = proto_redistnum(AFI_IP6, argv[0]); - - if (type < 0) - type = proto_redistnum(AFI_IP, argv[0]); - - if (type < 0) { - vty_out(vty, "Invalid type %s%s", argv[0], VTY_NEWLINE); - return CMD_WARNING; - } - - zclient_redistribute (ZEBRA_REDISTRIBUTE_ADD, zclient, type); - return CMD_SUCCESS; -} - -/* [Babel Command] */ -DEFUN (no_babel_redistribute_type, - no_babel_redistribute_type_cmd, - "no redistribute " QUAGGA_REDIST_STR_BABELD, - NO_STR - "Redistribute\n" - QUAGGA_REDIST_HELP_STR_BABELD) -{ - int type; - - type = proto_redistnum(AFI_IP6, argv[0]); - - if (type < 0) - type = proto_redistnum(AFI_IP, argv[0]); - - if (type < 0) { - vty_out(vty, "Invalid type %s%s", argv[0], VTY_NEWLINE); - return CMD_WARNING; - } - - zclient_redistribute (ZEBRA_REDISTRIBUTE_DELETE, zclient, type); - /* perhaps should we remove xroutes having the same type... */ - return CMD_SUCCESS; -} - -#ifndef NO_DEBUG -/* [Babel Command] */ -DEFUN (debug_babel, - debug_babel_cmd, - "debug babel (common|kernel|filter|timeout|interface|route|all)", - "Enable debug messages for specific or all part.\n" - "Babel information\n" - "Common messages (default)\n" - "Kernel messages\n" - "Filter messages\n" - "Timeout messages\n" - "Interface messages\n" - "Route messages\n" - "All messages\n") -{ - int i; - - for(i = 0; debug_type[i].str != NULL; i++) { - if (strncmp (debug_type[i].str, argv[0], - debug_type[i].str_min_len) == 0) { - debug |= debug_type[i].type; - return CMD_SUCCESS; - } - } - - vty_out(vty, "Invalid type %s%s", argv[0], VTY_NEWLINE); - - return CMD_WARNING; -} - -/* [Babel Command] */ -DEFUN (no_debug_babel, - no_debug_babel_cmd, - "no debug babel (common|kernel|filter|timeout|interface|route|all)", - NO_STR - "Disable debug messages for specific or all part.\n" - "Babel information\n" - "Common messages (default)\n" - "Kernel messages\n" - "Filter messages\n" - "Timeout messages\n" - "Interface messages\n" - "Route messages\n" - "All messages\n") -{ - int i; - - for (i = 0; debug_type[i].str; i++) { - if (strncmp(debug_type[i].str, argv[0], - debug_type[i].str_min_len) == 0) { - debug &= ~debug_type[i].type; - return CMD_SUCCESS; - } - } - - vty_out(vty, "Invalid type %s%s", argv[0], VTY_NEWLINE); - - return CMD_WARNING; -} -#endif /* NO_DEBUG */ - -/* Output "debug" statement lines, if necessary. */ -int -debug_babel_config_write (struct vty * vty) -{ -#ifdef NO_DEBUG - return 0; -#else - int i, lines = 0; - - if (debug == BABEL_DEBUG_ALL) - { - vty_out (vty, "debug babel all%s", VTY_NEWLINE); - lines++; - } - else - for (i = 0; debug_type[i].str != NULL; i++) - if - ( - debug_type[i].type != BABEL_DEBUG_ALL - && CHECK_FLAG (debug, debug_type[i].type) - ) - { - vty_out (vty, "debug babel %s%s", debug_type[i].str, VTY_NEWLINE); - lines++; - } - if (lines) - { - vty_out (vty, "!%s", VTY_NEWLINE); - lines++; - } - return lines; -#endif /* NO_DEBUG */ -} - -void babelz_zebra_init(void) -{ - zclient = zclient_new(); - zclient_init(zclient, ZEBRA_ROUTE_BABEL); - - zclient->interface_add = babel_interface_add; - zclient->interface_delete = babel_interface_delete; - zclient->interface_up = babel_interface_up; - zclient->interface_down = babel_interface_down; - zclient->interface_address_add = babel_interface_address_add; - zclient->interface_address_delete = babel_interface_address_delete; - zclient->ipv4_route_add = babel_zebra_read_ipv4; - zclient->ipv4_route_delete = babel_zebra_read_ipv4; - zclient->ipv6_route_add = babel_zebra_read_ipv6; - zclient->ipv6_route_delete = babel_zebra_read_ipv6; - - install_node (&zebra_node, zebra_config_write); - install_element(BABEL_NODE, &babel_redistribute_type_cmd); - install_element(BABEL_NODE, &no_babel_redistribute_type_cmd); - install_element(ENABLE_NODE, &debug_babel_cmd); - install_element(ENABLE_NODE, &no_debug_babel_cmd); - install_element(CONFIG_NODE, &debug_babel_cmd); - install_element(CONFIG_NODE, &no_debug_babel_cmd); -} - -static int -zebra_config_write (struct vty *vty) -{ - if (! zclient->enable) - { - vty_out (vty, "no router zebra%s", VTY_NEWLINE); - return 1; - } - else if (! zclient->redist[ZEBRA_ROUTE_BABEL]) - { - vty_out (vty, "router zebra%s", VTY_NEWLINE); - vty_out (vty, " no redistribute babel%s", VTY_NEWLINE); - return 1; - } - return 0; -} - -void -babel_zebra_close_connexion(void) -{ - zclient_stop(zclient); -} diff --git a/babeld/babel_zebra.h b/babeld/babel_zebra.h deleted file mode 100644 index 99601aa7c..000000000 --- a/babeld/babel_zebra.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * This file is free software: you may copy, redistribute 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 file 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, see . - * - * This file incorporates work covered by the following copyright and - * permission notice: - * -Copyright 2011 by Matthieu Boutier and Juliusz Chroboczek - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -*/ - -#ifndef BABEL_ZEBRA_H -#define BABEL_ZEBRA_H - -#include "vty.h" - -extern struct zclient *zclient; - -void babelz_zebra_init(void); -void babel_zebra_close_connexion(void); -extern int debug_babel_config_write (struct vty *); - -#endif diff --git a/babeld/babeld.c b/babeld/babeld.c deleted file mode 100644 index 1ae3f042c..000000000 --- a/babeld/babeld.c +++ /dev/null @@ -1,728 +0,0 @@ -/* - * This file is free software: you may copy, redistribute 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 file 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, see . - * - * This file incorporates work covered by the following copyright and - * permission notice: - * - -Copyright 2011 by Matthieu Boutier and Juliusz Chroboczek - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -*/ - -#include -#include "command.h" -#include "prefix.h" -#include "memory.h" -#include "memtypes.h" -#include "table.h" -#include "distribute.h" -#include "prefix.h" -#include "filter.h" -#include "plist.h" - -#include "babel_main.h" -#include "babeld.h" -#include "util.h" -#include "net.h" -#include "kernel.h" -#include "babel_interface.h" -#include "neighbour.h" -#include "route.h" -#include "message.h" -#include "resend.h" -#include "babel_filter.h" -#include "babel_zebra.h" - - -static int babel_init_routing_process(struct thread *thread); -static void babel_get_myid(void); -static void babel_initial_noise(void); -static int babel_read_protocol (struct thread *thread); -static int babel_main_loop(struct thread *thread); -static void babel_set_timer(struct timeval *timeout); -static void babel_fill_with_next_timeout(struct timeval *tv); - - -/* Informations relative to the babel running daemon. */ -static struct babel *babel_routing_process = NULL; -static unsigned char *receive_buffer = NULL; -static int receive_buffer_size = 0; - -/* timeouts */ -struct timeval check_neighbours_timeout; -static time_t expiry_time; -static time_t source_expiry_time; - -/* Babel node structure. */ -static struct cmd_node cmd_babel_node = -{ - .node = BABEL_NODE, - .prompt = "%s(config-router)# ", - .vtysh = 1, -}; - -/* print current babel configuration on vty */ -static int -babel_config_write (struct vty *vty) -{ - int lines = 0; - int i; - - /* list enabled debug modes */ - lines += debug_babel_config_write (vty); - - if (!babel_routing_process) - return lines; - vty_out (vty, "router babel%s", VTY_NEWLINE); - if (resend_delay != BABEL_DEFAULT_RESEND_DELAY) - { - vty_out (vty, " babel resend-delay %u%s", resend_delay, VTY_NEWLINE); - lines++; - } - /* list enabled interfaces */ - lines = 1 + babel_enable_if_config_write (vty); - /* list redistributed protocols */ - for (i = 0; i < ZEBRA_ROUTE_MAX; i++) - if (i != zclient->redist_default && zclient->redist[i]) - { - vty_out (vty, " redistribute %s%s", zebra_route_string (i), VTY_NEWLINE); - lines++; - } - - return lines; -} - - -static int -babel_create_routing_process (void) -{ - assert (babel_routing_process == NULL); - - /* Allocaste Babel instance. */ - babel_routing_process = XCALLOC (MTYPE_BABEL, sizeof (struct babel)); - - /* Initialize timeouts */ - gettime(&babel_now); - expiry_time = babel_now.tv_sec + roughly(30); - source_expiry_time = babel_now.tv_sec + roughly(300); - - /* Make socket for Babel protocol. */ - protocol_socket = babel_socket(protocol_port); - if (protocol_socket < 0) { - zlog_err("Couldn't create link local socket: %s", safe_strerror(errno)); - goto fail; - } - - /* Threads. */ - babel_routing_process->t_read = - thread_add_read(master, &babel_read_protocol, NULL, protocol_socket); - /* wait a little: zebra will announce interfaces, addresses, routes... */ - babel_routing_process->t_update = - thread_add_timer_msec(master, &babel_init_routing_process, NULL, 200L); - return 0; - -fail: - XFREE(MTYPE_BABEL, babel_routing_process); - babel_routing_process = NULL; - return -1; -} - -/* thread reading entries form others babel daemons */ -static int -babel_read_protocol (struct thread *thread) -{ - int rc; - struct interface *ifp = NULL; - struct sockaddr_in6 sin6; - struct listnode *linklist_node = NULL; - - assert(babel_routing_process != NULL); - assert(protocol_socket >= 0); - - rc = babel_recv(protocol_socket, - receive_buffer, receive_buffer_size, - (struct sockaddr*)&sin6, sizeof(sin6)); - if(rc < 0) { - if(errno != EAGAIN && errno != EINTR) { - zlog_err("recv: %s", safe_strerror(errno)); - } - } else { - FOR_ALL_INTERFACES(ifp, linklist_node) { - if(!if_up(ifp)) - continue; - if(ifp->ifindex == sin6.sin6_scope_id) { - parse_packet((unsigned char*)&sin6.sin6_addr, ifp, - receive_buffer, rc); - break; - } - } - } - - /* re-add thread */ - babel_routing_process->t_read = - thread_add_read(master, &babel_read_protocol, NULL, protocol_socket); - return 0; -} - -/* Zebra will give some information, especially about interfaces. This function - must be call with a litte timeout wich may give zebra the time to do his job, - making these inits have sense. */ -static int -babel_init_routing_process(struct thread *thread) -{ - myseqno = (random() & 0xFFFF); - babel_get_myid(); - babel_load_state_file(); - debugf(BABEL_DEBUG_COMMON, "My ID is : %s.", format_eui64(myid)); - babel_initial_noise(); - babel_main_loop(thread);/* this function self-add to the t_update thread */ - return 0; -} - -/* fill "myid" with an unique id (only if myid != {0}). */ -static void -babel_get_myid(void) -{ - struct interface *ifp = NULL; - struct listnode *linklist_node = NULL; - int rc; - int i; - - /* if we already have an id (from state file), we return. */ - if (memcmp(myid, zeroes, 8) != 0) { - return; - } - - FOR_ALL_INTERFACES(ifp, linklist_node) { - /* ifp->ifindex is not necessarily valid at this point */ - int ifindex = if_nametoindex(ifp->name); - if(ifindex > 0) { - unsigned char eui[8]; - rc = if_eui64(ifp->name, ifindex, eui); - if(rc < 0) - continue; - memcpy(myid, eui, 8); - return; - } - } - - /* We failed to get a global EUI64 from the interfaces we were given. - Let's try to find an interface with a MAC address. */ - for(i = 1; i < 256; i++) { - char buf[IF_NAMESIZE], *ifname; - unsigned char eui[8]; - ifname = if_indextoname(i, buf); - if(ifname == NULL) - continue; - rc = if_eui64(ifname, i, eui); - if(rc < 0) - continue; - memcpy(myid, eui, 8); - return; - } - - zlog_err("Warning: couldn't find router id -- using random value."); - - rc = read_random_bytes(myid, 8); - if(rc < 0) { - zlog_err("read(random): %s (cannot assign an ID)",safe_strerror(errno)); - exit(1); - } - /* Clear group and global bits */ - myid[0] &= ~3; -} - -/* Make some noise so that others notice us, and send retractions in - case we were restarted recently */ -static void -babel_initial_noise(void) -{ - struct interface *ifp = NULL; - struct listnode *linklist_node = NULL; - - FOR_ALL_INTERFACES(ifp, linklist_node) { - if(!if_up(ifp)) - continue; - /* Apply jitter before we send the first message. */ - usleep(roughly(10000)); - gettime(&babel_now); - send_hello(ifp); - send_wildcard_retraction(ifp); - } - - FOR_ALL_INTERFACES(ifp, linklist_node) { - if(!if_up(ifp)) - continue; - usleep(roughly(10000)); - gettime(&babel_now); - send_hello(ifp); - send_wildcard_retraction(ifp); - send_self_update(ifp); - send_request(ifp, NULL, 0); - flushupdates(ifp); - flushbuf(ifp); - } -} - -/* Delete all the added babel routes, make babeld only speak to zebra. */ -static void -babel_clean_routing_process() -{ - flush_all_routes(); - babel_interface_close_all(); - - /* cancel threads */ - if (babel_routing_process->t_read != NULL) { - thread_cancel(babel_routing_process->t_read); - } - if (babel_routing_process->t_update != NULL) { - thread_cancel(babel_routing_process->t_update); - } - - XFREE(MTYPE_BABEL, babel_routing_process); - babel_routing_process = NULL; -} - -/* Function used with timeout. */ -static int -babel_main_loop(struct thread *thread) -{ - struct timeval tv; - struct interface *ifp = NULL; - struct listnode *linklist_node = NULL; - - while(1) { - gettime(&babel_now); - - /* timeouts --------------------------------------------------------- */ - /* get the next timeout */ - babel_fill_with_next_timeout(&tv); - /* if there is no timeout, we must wait. */ - if(timeval_compare(&tv, &babel_now) > 0) { - timeval_minus(&tv, &tv, &babel_now); - debugf(BABEL_DEBUG_TIMEOUT, "babel main loop : timeout: %ld msecs", - tv.tv_sec * 1000 + tv.tv_usec / 1000); - /* it happens often to have less than 1 ms, it's bad. */ - timeval_add_msec(&tv, &tv, 300); - babel_set_timer(&tv); - return 0; - } - - gettime(&babel_now); - - /* update database -------------------------------------------------- */ - if(timeval_compare(&check_neighbours_timeout, &babel_now) < 0) { - int msecs; - msecs = check_neighbours(); - msecs = MAX(msecs, 10); - schedule_neighbours_check(msecs, 1); - } - - if(babel_now.tv_sec >= expiry_time) { - expire_routes(); - expire_resend(); - expiry_time = babel_now.tv_sec + roughly(30); - } - - if(babel_now.tv_sec >= source_expiry_time) { - expire_sources(); - source_expiry_time = babel_now.tv_sec + roughly(300); - } - - FOR_ALL_INTERFACES(ifp, linklist_node) { - babel_interface_nfo *babel_ifp = NULL; - if(!if_up(ifp)) - continue; - babel_ifp = babel_get_if_nfo(ifp); - if(timeval_compare(&babel_now, &babel_ifp->hello_timeout) >= 0) - send_hello(ifp); - if(timeval_compare(&babel_now, &babel_ifp->update_timeout) >= 0) - send_update(ifp, 0, NULL, 0); - if(timeval_compare(&babel_now, - &babel_ifp->update_flush_timeout) >= 0) - flushupdates(ifp); - } - - if(resend_time.tv_sec != 0) { - if(timeval_compare(&babel_now, &resend_time) >= 0) - do_resend(); - } - - if(unicast_flush_timeout.tv_sec != 0) { - if(timeval_compare(&babel_now, &unicast_flush_timeout) >= 0) - flush_unicast(1); - } - - FOR_ALL_INTERFACES(ifp, linklist_node) { - babel_interface_nfo *babel_ifp = NULL; - if(!if_up(ifp)) - continue; - babel_ifp = babel_get_if_nfo(ifp); - if(babel_ifp->flush_timeout.tv_sec != 0) { - if(timeval_compare(&babel_now, &babel_ifp->flush_timeout) >= 0) - flushbuf(ifp); - } - } - } - - assert(0); /* this line should never be reach */ -} - -static void -printIfMin(struct timeval *tv, int cmd, const char *tag, const char *ifname) -{ - static struct timeval curr_tv; - static char buffer[200]; - static const char *curr_tag = NULL; - - switch (cmd) { - case 0: /* reset timeval */ - curr_tv = *tv; - if(ifname != NULL) { - snprintf(buffer, 200L, "interface: %s; %s", ifname, tag); - curr_tag = buffer; - } else { - curr_tag = tag; - } - break; - case 1: /* take the min */ - if (tv->tv_sec == 0 && tv->tv_usec == 0) { /* if (tv == ∞) */ - break; - } - if (tv->tv_sec < curr_tv.tv_sec ||(tv->tv_sec == curr_tv.tv_sec && - tv->tv_usec < curr_tv.tv_usec)) { - curr_tv = *tv; - if(ifname != NULL) { - snprintf(buffer, 200L, "interface: %s; %s", ifname, tag); - curr_tag = buffer; - } else { - curr_tag = tag; - } - } - break; - case 2: /* print message */ - debugf(BABEL_DEBUG_TIMEOUT, "next timeout due to: %s", curr_tag); - break; - default: - break; - } -} - -static void -babel_fill_with_next_timeout(struct timeval *tv) -{ -#if (defined NO_DEBUG) -#define printIfMin(a,b,c,d) -#else -#define printIfMin(a,b,c,d) \ - if (UNLIKELY(debug & BABEL_DEBUG_TIMEOUT)) {printIfMin(a,b,c,d);} - - struct interface *ifp = NULL; - struct listnode *linklist_node = NULL; - - *tv = check_neighbours_timeout; - printIfMin(tv, 0, "check_neighbours_timeout", NULL); - timeval_min_sec(tv, expiry_time); - printIfMin(tv, 1, "expiry_time", NULL); - timeval_min_sec(tv, source_expiry_time); - printIfMin(tv, 1, "source_expiry_time", NULL); - timeval_min(tv, &resend_time); - printIfMin(tv, 1, "resend_time", NULL); - FOR_ALL_INTERFACES(ifp, linklist_node) { - babel_interface_nfo *babel_ifp = NULL; - if(!if_up(ifp)) - continue; - babel_ifp = babel_get_if_nfo(ifp); - timeval_min(tv, &babel_ifp->flush_timeout); - printIfMin(tv, 1, "flush_timeout", ifp->name); - timeval_min(tv, &babel_ifp->hello_timeout); - printIfMin(tv, 1, "hello_timeout", ifp->name); - timeval_min(tv, &babel_ifp->update_timeout); - printIfMin(tv, 1, "update_timeout", ifp->name); - timeval_min(tv, &babel_ifp->update_flush_timeout); - printIfMin(tv, 1, "update_flush_timeout",ifp->name); - } - timeval_min(tv, &unicast_flush_timeout); - printIfMin(tv, 1, "unicast_flush_timeout", NULL); - printIfMin(tv, 2, NULL, NULL); -#undef printIfMin -#endif -} - -/* set the t_update thread of the babel routing process to be launch in - 'timeout' (approximate at the milisecond) */ -static void -babel_set_timer(struct timeval *timeout) -{ - long msecs = timeout->tv_sec * 1000 + timeout->tv_usec / 1000; - if (babel_routing_process->t_update != NULL) { - thread_cancel(babel_routing_process->t_update); - } - babel_routing_process->t_update = - thread_add_timer_msec(master, &babel_main_loop, NULL, msecs); -} - -/* Schedule a neighbours check after roughly 3/2 times msecs have elapsed. */ -void -schedule_neighbours_check(int msecs, int override) -{ - struct timeval timeout; - - timeval_add_msec(&timeout, &babel_now, roughly(msecs * 3 / 2)); - if(override) - check_neighbours_timeout = timeout; - else - timeval_min(&check_neighbours_timeout, &timeout); -} - -int -resize_receive_buffer(int size) -{ - if(size <= receive_buffer_size) - return 0; - - if(receive_buffer == NULL) { - receive_buffer = malloc(size); - if(receive_buffer == NULL) { - zlog_err("malloc(receive_buffer): %s", safe_strerror(errno)); - return -1; - } - receive_buffer_size = size; - } else { - unsigned char *new; - new = realloc(receive_buffer, size); - if(new == NULL) { - zlog_err("realloc(receive_buffer): %s", safe_strerror(errno)); - return -1; - } - receive_buffer = new; - receive_buffer_size = size; - } - return 1; -} - -static void -babel_distribute_update (struct distribute *dist) -{ - struct interface *ifp; - babel_interface_nfo *babel_ifp; - struct access_list *alist; - struct prefix_list *plist; - - if (! dist->ifname) - return; - - ifp = if_lookup_by_name (dist->ifname); - if (ifp == NULL) - return; - - babel_ifp = babel_get_if_nfo(ifp); - - if (dist->list[DISTRIBUTE_IN]) { - alist = access_list_lookup (AFI_IP6, dist->list[DISTRIBUTE_IN]); - if (alist) - babel_ifp->list[BABEL_FILTER_IN] = alist; - else - babel_ifp->list[BABEL_FILTER_IN] = NULL; - } else { - babel_ifp->list[BABEL_FILTER_IN] = NULL; - } - - if (dist->list[DISTRIBUTE_OUT]) { - alist = access_list_lookup (AFI_IP6, dist->list[DISTRIBUTE_OUT]); - if (alist) - babel_ifp->list[BABEL_FILTER_OUT] = alist; - else - babel_ifp->list[BABEL_FILTER_OUT] = NULL; - } else { - babel_ifp->list[BABEL_FILTER_OUT] = NULL; - } - - if (dist->prefix[DISTRIBUTE_IN]) { - plist = prefix_list_lookup (AFI_IP6, dist->prefix[DISTRIBUTE_IN]); - if (plist) - babel_ifp->prefix[BABEL_FILTER_IN] = plist; - else - babel_ifp->prefix[BABEL_FILTER_IN] = NULL; - } else { - babel_ifp->prefix[BABEL_FILTER_IN] = NULL; - } - - if (dist->prefix[DISTRIBUTE_OUT]) { - plist = prefix_list_lookup (AFI_IP6, dist->prefix[DISTRIBUTE_OUT]); - if (plist) - babel_ifp->prefix[BABEL_FILTER_OUT] = plist; - else - babel_ifp->prefix[BABEL_FILTER_OUT] = NULL; - } else { - babel_ifp->prefix[BABEL_FILTER_OUT] = NULL; - } -} - -static void -babel_distribute_update_interface (struct interface *ifp) -{ - struct distribute *dist; - - dist = distribute_lookup (ifp->name); - if (dist) - babel_distribute_update (dist); -} - -/* Update all interface's distribute list. */ -static void -babel_distribute_update_all (struct prefix_list *notused) -{ - struct interface *ifp; - struct listnode *node; - - for (ALL_LIST_ELEMENTS_RO (iflist, node, ifp)) - babel_distribute_update_interface (ifp); -} - -static void -babel_distribute_update_all_wrapper (struct access_list *notused) -{ - babel_distribute_update_all(NULL); -} - - -/* [Command] */ -DEFUN (router_babel, - router_babel_cmd, - "router babel", - "Enable a routing process\n" - "Make Babel instance command\n" - "No attributes\n") -{ - int ret; - - vty->node = BABEL_NODE; - - if (!babel_routing_process) { - ret = babel_create_routing_process (); - - /* Notice to user we couldn't create Babel. */ - if (ret < 0) { - zlog_warn ("can't create Babel"); - return CMD_WARNING; - } - } - - return CMD_SUCCESS; -} - -/* [Command] */ -DEFUN (no_router_babel, - no_router_babel_cmd, - "no router babel", - NO_STR - "Disable a routing process\n" - "Remove Babel instance command\n" - "No attributes\n") -{ - if(babel_routing_process) - babel_clean_routing_process(); - return CMD_SUCCESS; -} - -/* [Babel Command] */ -DEFUN (babel_set_resend_delay, - babel_set_resend_delay_cmd, - "babel resend-delay <20-655340>", - "Babel commands\n" - "Time before resending a message\n" - "Milliseconds\n") -{ - int interval; - - VTY_GET_INTEGER_RANGE("milliseconds", interval, argv[0], 20, 10 * 0xFFFE); - - resend_delay = interval; - return CMD_SUCCESS; -} - -void -babeld_quagga_init(void) -{ - - install_node(&cmd_babel_node, &babel_config_write); - - install_element(CONFIG_NODE, &router_babel_cmd); - install_element(CONFIG_NODE, &no_router_babel_cmd); - - install_default(BABEL_NODE); - install_element(BABEL_NODE, &babel_set_resend_delay_cmd); - - babel_if_init(); - - /* Access list install. */ - access_list_init (); - access_list_add_hook (babel_distribute_update_all_wrapper); - access_list_delete_hook (babel_distribute_update_all_wrapper); - - /* Prefix list initialize.*/ - prefix_list_init (); - prefix_list_add_hook (babel_distribute_update_all); - prefix_list_delete_hook (babel_distribute_update_all); - - /* Distribute list install. */ - distribute_list_init (BABEL_NODE); - distribute_list_add_hook (babel_distribute_update); - distribute_list_delete_hook (babel_distribute_update); -} - -/* Stubs to adapt Babel's filtering calls to Quagga's infrastructure. */ - -int -input_filter(const unsigned char *id, - const unsigned char *prefix, unsigned short plen, - const unsigned char *neigh, unsigned int ifindex) -{ - return babel_filter(0, prefix, plen, ifindex); -} - -int -output_filter(const unsigned char *id, const unsigned char *prefix, - unsigned short plen, unsigned int ifindex) -{ - return babel_filter(1, prefix, plen, ifindex); -} - -/* There's no redistribute filter in Quagga -- the zebra daemon does its - own filtering. */ -int -redistribute_filter(const unsigned char *prefix, unsigned short plen, - unsigned int ifindex, int proto) -{ - return 0; -} - diff --git a/babeld/babeld.conf.sample b/babeld/babeld.conf.sample deleted file mode 100644 index a4924ec7b..000000000 --- a/babeld/babeld.conf.sample +++ /dev/null @@ -1,30 +0,0 @@ -debug babel common -!debug babel kernel -!debug babel filter -!debug babel timeout -!debug babel interface -!debug babel route -!debug babel all - -router babel -! network wlan0 -! network eth0 -! redistribute kernel -! no redistribute static - -! The defaults are fine for a wireless interface - -!interface wlan0 - -! A few optimisation tweaks are optional but recommended on a wired interface -! Disable link quality estimation, enable split horizon processing, and -! increase the hello and update intervals. - -!interface eth0 -! babel wired -! babel split-horizon -! babel hello-interval 12000 -! babel update-interval 36000 - -! log file /var/log/quagga/babeld.log -log stdout diff --git a/babeld/babeld.h b/babeld/babeld.h deleted file mode 100644 index b19ae0f2d..000000000 --- a/babeld/babeld.h +++ /dev/null @@ -1,141 +0,0 @@ -/* - * This file is free software: you may copy, redistribute 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 file 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, see . - * - * This file incorporates work covered by the following copyright and - * permission notice: - * -Copyright (c) 2007, 2008 by Juliusz Chroboczek -Copyright 2011 by Matthieu Boutier and Juliusz Chroboczek - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -*/ - -#ifndef BABEL_BABELD_H -#define BABEL_BABELD_H - -#include -#include "vty.h" - -#define INFINITY ((unsigned short)(~0)) - -#ifndef RTPROT_BABEL -#define RTPROT_BABEL 42 -#endif - -#define RTPROT_BABEL_LOCAL -2 - -#undef MAX -#undef MIN - -#define MAX(x,y) ((x)<=(y)?(y):(x)) -#define MIN(x,y) ((x)<=(y)?(x):(y)) - -#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L -/* nothing */ -#elif defined(__GNUC__) -#define inline __inline -#if (__GNUC__ >= 3) -#define restrict __restrict -#else -#define restrict /**/ -#endif -#else -#define inline /**/ -#define restrict /**/ -#endif - -#if defined(__GNUC__) && (__GNUC__ >= 3) -#define ATTRIBUTE(x) __attribute__ (x) -#define LIKELY(_x) __builtin_expect(!!(_x), 1) -#define UNLIKELY(_x) __builtin_expect(!!(_x), 0) -#else -#define ATTRIBUTE(x) /**/ -#define LIKELY(_x) !!(_x) -#define UNLIKELY(_x) !!(_x) -#endif - -#if defined(__GNUC__) && (__GNUC__ >= 4) && (__GNUC_MINOR__ >= 3) -#define COLD __attribute__ ((cold)) -#else -#define COLD /**/ -#endif - -#ifndef IF_NAMESIZE -#include -#include -#endif - -#ifdef HAVE_VALGRIND -#include -#else -#ifndef VALGRIND_MAKE_MEM_UNDEFINED -#define VALGRIND_MAKE_MEM_UNDEFINED(a, b) do {} while(0) -#endif -#ifndef VALGRIND_CHECK_MEM_IS_DEFINED -#define VALGRIND_CHECK_MEM_IS_DEFINED(a, b) do {} while(0) -#endif -#endif - - -#define BABEL_VTY_PORT 2609 -#define BABEL_DEFAULT_CONFIG "babeld.conf" -#define BABEL_VERSION "0.1 for quagga" - -/* Values in milliseconds */ -#define BABEL_DEFAULT_HELLO_INTERVAL 4000 -#define BABEL_DEFAULT_UPDATE_INTERVAL 16000 -#define BABEL_DEFAULT_RESEND_DELAY 2000 - - -/* Babel socket. */ -extern int protocol_socket; - -/* Babel structure. */ -struct babel -{ - /* Babel threads. */ - struct thread *t_read; /* on Babel protocol's socket */ - struct thread *t_update; /* timers */ -}; - - -extern void babeld_quagga_init(void); -extern int input_filter(const unsigned char *id, - const unsigned char *prefix, unsigned short plen, - const unsigned char *neigh, unsigned int ifindex); -extern int output_filter(const unsigned char *id, const unsigned char *prefix, - unsigned short plen, unsigned int ifindex); -extern int redistribute_filter(const unsigned char *prefix, unsigned short plen, - unsigned int ifindex, int proto); -extern int resize_receive_buffer(int size); -extern void schedule_neighbours_check(int msecs, int override); - - -#endif /* BABEL_BABELD_H */ diff --git a/babeld/kernel.c b/babeld/kernel.c deleted file mode 100644 index c31f617b8..000000000 --- a/babeld/kernel.c +++ /dev/null @@ -1,76 +0,0 @@ -/* - * This file is free software: you may copy, redistribute 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 file 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, see . - * - * This file incorporates work covered by the following copyright and - * permission notice: - * - -Copyright 2007, 2008 by Grégoire Henry, Julien Cristau and Juliusz Chroboczek - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -*/ - -#include -#include -#include - -#include "babeld.h" - -#include "kernel_zebra.c" - -/* Like gettimeofday, but returns monotonic time. If POSIX clocks are not - available, falls back to gettimeofday but enforces monotonicity. */ -int -gettime(struct timeval *tv) -{ - return quagga_gettime(QUAGGA_CLK_MONOTONIC, tv); -} - -/* If /dev/urandom doesn't exist, this will fail with ENOENT, which the - caller will deal with gracefully. */ - -int -read_random_bytes(void *buf, size_t len) -{ - int fd; - int rc; - - fd = open("/dev/urandom", O_RDONLY); - if(fd < 0) { - rc = -1; - } else { - rc = read(fd, buf, len); - if(rc < 0 || (unsigned) rc < len) - rc = -1; - close(fd); - } - return rc; -} - diff --git a/babeld/kernel.h b/babeld/kernel.h deleted file mode 100644 index e8c8f9b7c..000000000 --- a/babeld/kernel.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * This file is free software: you may copy, redistribute 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 file 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, see . - * - * This file incorporates work covered by the following copyright and - * permission notice: - * -Copyright (c) 2007, 2008 by Juliusz Chroboczek - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -*/ - -#include -#include "babel_main.h" -#include "if.h" - -#define KERNEL_INFINITY 0xFFFF - -struct kernel_route { - unsigned char prefix[16]; - int plen; - int metric; - unsigned int ifindex; - int proto; - unsigned char gw[16]; -}; - -#define ROUTE_FLUSH 0 -#define ROUTE_ADD 1 -#define ROUTE_MODIFY 2 - -extern int export_table, import_table; - -int kernel_interface_operational(struct interface *interface); -int kernel_interface_mtu(struct interface *interface); -int kernel_interface_wireless(struct interface *interface); -int kernel_route(int operation, const unsigned char *dest, unsigned short plen, - const unsigned char *gate, int ifindex, unsigned int metric, - const unsigned char *newgate, int newifindex, - unsigned int newmetric); -int if_eui64(char *ifname, int ifindex, unsigned char *eui); -int gettime(struct timeval *tv); -int read_random_bytes(void *buf, size_t len); diff --git a/babeld/kernel_zebra.c b/babeld/kernel_zebra.c deleted file mode 100644 index db7d0b398..000000000 --- a/babeld/kernel_zebra.c +++ /dev/null @@ -1,275 +0,0 @@ -/* - * This file is free software: you may copy, redistribute 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 file 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, see . - * - * This file incorporates work covered by the following copyright and - * permission notice: - * -Copyright 2011 by Matthieu Boutier and Juliusz Chroboczek - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -*/ - -#include -#include -#include -#include -#include - -#include -#include "prefix.h" -#include "zclient.h" -#include "kernel.h" -#include "privs.h" -#include "command.h" -#include "vty.h" -#include "memory.h" -#include "thread.h" - -#include "util.h" -#include "babel_interface.h" -#include "babel_zebra.h" - - -static int -kernel_route_v4(int add, const unsigned char *pref, unsigned short plen, - const unsigned char *gate, int ifindex, - unsigned int metric); -static int -kernel_route_v6(int add, const unsigned char *pref, unsigned short plen, - const unsigned char *gate, int ifindex, - unsigned int metric); - -int -kernel_interface_operational(struct interface *interface) -{ - return if_is_operative(interface); -} - -int -kernel_interface_mtu(struct interface *interface) -{ - return MIN(interface->mtu, interface->mtu6); -} - -int -kernel_interface_wireless(struct interface *interface) -{ - return 0; -} - -int -kernel_route(int operation, const unsigned char *pref, unsigned short plen, - const unsigned char *gate, int ifindex, unsigned int metric, - const unsigned char *newgate, int newifindex, - unsigned int newmetric) -{ - int rc; - int ipv4; - - /* Check that the protocol family is consistent. */ - if(plen >= 96 && v4mapped(pref)) { - if(!v4mapped(gate)) { - errno = EINVAL; - return -1; - } - ipv4 = 1; - } else { - if(v4mapped(gate)) { - errno = EINVAL; - return -1; - } - ipv4 = 0; - } - - switch (operation) { - case ROUTE_ADD: - return ipv4 ? - kernel_route_v4(1, pref, plen, gate, ifindex, metric): - kernel_route_v6(1, pref, plen, gate, ifindex, metric); - break; - case ROUTE_FLUSH: - return ipv4 ? - kernel_route_v4(0, pref, plen, gate, ifindex, metric): - kernel_route_v6(0, pref, plen, gate, ifindex, metric); - break; - case ROUTE_MODIFY: - if(newmetric == metric && memcmp(newgate, gate, 16) == 0 && - newifindex == ifindex) - return 0; - debugf(BABEL_DEBUG_ROUTE, "Modify route: delete old; add new."); - rc = ipv4 ? - kernel_route_v4(0, pref, plen, gate, ifindex, metric): - kernel_route_v6(0, pref, plen, gate, ifindex, metric); - - if (rc < 0) - return -1; - - rc = ipv4 ? - kernel_route_v4(1, pref, plen, newgate, newifindex, newmetric): - kernel_route_v6(1, pref, plen, newgate, newifindex, newmetric); - - return rc; - break; - default: - zlog_err("this should never appens (false value - kernel_route)"); - assert(0); - exit(1); - break; - } -} - -static int -kernel_route_v4(int add, - const unsigned char *pref, unsigned short plen, - const unsigned char *gate, int ifindex, unsigned int metric) -{ - struct zapi_ipv4 api; /* quagga's communication system */ - struct prefix_ipv4 quagga_prefix; /* quagga's prefix */ - struct in_addr babel_prefix_addr; /* babeld's prefix addr */ - struct in_addr nexthop; /* next router to go */ - struct in_addr *nexthop_pointer = &nexthop; /* it's an array! */ - - /* convert to be understandable by quagga */ - /* convert given addresses */ - uchar_to_inaddr(&babel_prefix_addr, pref); - uchar_to_inaddr(&nexthop, gate); - - /* make prefix structure */ - memset (&quagga_prefix, 0, sizeof(quagga_prefix)); - quagga_prefix.family = AF_INET; - IPV4_ADDR_COPY (&quagga_prefix.prefix, &babel_prefix_addr); - quagga_prefix.prefixlen = plen - 96; /* our plen is for v4mapped's addr */ - apply_mask_ipv4(&quagga_prefix); - - api.type = ZEBRA_ROUTE_BABEL; - api.flags = 0; - api.message = 0; - api.safi = SAFI_UNICAST; - - /* Unlike the native Linux and BSD interfaces, Quagga doesn't like - there to be both and IPv4 nexthop and an ifindex. Omit the - ifindex, and assume that the connected prefixes be set up - correctly. */ - - SET_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP); - api.ifindex_num = 0; - if(metric >= KERNEL_INFINITY) { - api.flags = ZEBRA_FLAG_BLACKHOLE; - api.nexthop_num = 0; - } else { - api.nexthop_num = 1; - api.nexthop = &nexthop_pointer; - SET_FLAG(api.message, ZAPI_MESSAGE_METRIC); - api.metric = metric; - } - - debugf(BABEL_DEBUG_ROUTE, "%s route (ipv4) to zebra", - add ? "adding" : "removing" ); - return zapi_ipv4_route (add ? ZEBRA_IPV4_ROUTE_ADD : - ZEBRA_IPV4_ROUTE_DELETE, - zclient, &quagga_prefix, &api); -} - -static int -kernel_route_v6(int add, const unsigned char *pref, unsigned short plen, - const unsigned char *gate, int ifindex, unsigned int metric) -{ - unsigned int tmp_ifindex = ifindex; /* (for typing) */ - struct zapi_ipv6 api; /* quagga's communication system */ - struct prefix_ipv6 quagga_prefix; /* quagga's prefix */ - struct in6_addr babel_prefix_addr; /* babeld's prefix addr */ - struct in6_addr nexthop; /* next router to go */ - struct in6_addr *nexthop_pointer = &nexthop; - - /* convert to be understandable by quagga */ - /* convert given addresses */ - uchar_to_in6addr(&babel_prefix_addr, pref); - uchar_to_in6addr(&nexthop, gate); - - /* make prefix structure */ - memset (&quagga_prefix, 0, sizeof(quagga_prefix)); - quagga_prefix.family = AF_INET6; - IPV6_ADDR_COPY (&quagga_prefix.prefix, &babel_prefix_addr); - quagga_prefix.prefixlen = plen; - apply_mask_ipv6(&quagga_prefix); - - api.type = ZEBRA_ROUTE_BABEL; - api.flags = 0; - api.message = 0; - api.safi = SAFI_UNICAST; - SET_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP); - if(metric >= KERNEL_INFINITY) { - api.flags = ZEBRA_FLAG_BLACKHOLE; - api.nexthop_num = 0; - api.ifindex_num = 0; - } else { - api.nexthop_num = 1; - api.nexthop = &nexthop_pointer; - SET_FLAG(api.message, ZAPI_MESSAGE_IFINDEX); - api.ifindex_num = 1; - api.ifindex = &tmp_ifindex; - SET_FLAG(api.message, ZAPI_MESSAGE_METRIC); - api.metric = metric; - } - - debugf(BABEL_DEBUG_ROUTE, "%s route (ipv6) to zebra", - add ? "adding" : "removing" ); - return zapi_ipv6_route (add ? ZEBRA_IPV6_ROUTE_ADD : - ZEBRA_IPV6_ROUTE_DELETE, - zclient, &quagga_prefix, &api); -} - -int -if_eui64(char *ifname, int ifindex, unsigned char *eui) -{ - struct interface *ifp = if_lookup_by_index(ifindex); - if (ifp == NULL) { - return -1; - } -#ifdef HAVE_STRUCT_SOCKADDR_DL - u_char len = ifp->sdl.sdl_alen; - char *tmp = ifp->sdl.sdl_data + ifp->sdl.sdl_nlen; -#else - u_char len = (u_char) ifp->hw_addr_len; - char *tmp = (void*) ifp->hw_addr; -#endif - if (len == 8) { - memcpy(eui, tmp, 8); - eui[0] ^= 2; - } else if (len == 6) { - memcpy(eui, tmp, 3); - eui[3] = 0xFF; - eui[4] = 0xFE; - memcpy(eui+5, tmp+3, 3); - } else { - return -1; - } - return 0; -} diff --git a/babeld/message.c b/babeld/message.c deleted file mode 100644 index 9dcfc6771..000000000 --- a/babeld/message.c +++ /dev/null @@ -1,1561 +0,0 @@ -/* - * This file is free software: you may copy, redistribute 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 file 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, see . - * - * This file incorporates work covered by the following copyright and - * permission notice: - * - -Copyright (c) 2007, 2008 by Juliusz Chroboczek - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -*/ - -#include -#include "if.h" - -#include "babeld.h" -#include "util.h" -#include "net.h" -#include "babel_interface.h" -#include "source.h" -#include "neighbour.h" -#include "route.h" -#include "xroute.h" -#include "resend.h" -#include "message.h" -#include "kernel.h" - -unsigned char packet_header[4] = {42, 2}; - -int split_horizon = 1; - -unsigned short myseqno = 0; -struct timeval seqno_time = {0, 0}; - -#define UNICAST_BUFSIZE 1024 -int unicast_buffered = 0; -unsigned char *unicast_buffer = NULL; -struct neighbour *unicast_neighbour = NULL; -struct timeval unicast_flush_timeout = {0, 0}; - -static const unsigned char v4prefix[16] = - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 0, 0, 0, 0 }; - -/* Parse a network prefix, encoded in the somewhat baroque compressed - representation used by Babel. Return the number of bytes parsed. */ -static int -network_prefix(int ae, int plen, unsigned int omitted, - const unsigned char *p, const unsigned char *dp, - unsigned int len, unsigned char *p_r) -{ - unsigned pb; - unsigned char prefix[16]; - int ret = -1; - - if(plen >= 0) - pb = (plen + 7) / 8; - else if(ae == 1) - pb = 4; - else - pb = 16; - - if(pb > 16) - return -1; - - memset(prefix, 0, 16); - - switch(ae) { - case 0: - ret = 0; - break; - case 1: - if(omitted > 4 || pb > 4 || (pb > omitted && len < pb - omitted)) - return -1; - memcpy(prefix, v4prefix, 12); - if(omitted) { - if (dp == NULL || !v4mapped(dp)) return -1; - memcpy(prefix, dp, 12 + omitted); - } - if(pb > omitted) memcpy(prefix + 12 + omitted, p, pb - omitted); - ret = pb - omitted; - break; - case 2: - if(omitted > 16 || (pb > omitted && len < pb - omitted)) return -1; - if(omitted) { - if (dp == NULL || v4mapped(dp)) return -1; - memcpy(prefix, dp, omitted); - } - if(pb > omitted) memcpy(prefix + omitted, p, pb - omitted); - ret = pb - omitted; - break; - case 3: - if(pb > 8 && len < pb - 8) return -1; - prefix[0] = 0xfe; - prefix[1] = 0x80; - if(pb > 8) memcpy(prefix + 8, p, pb - 8); - ret = pb - 8; - break; - default: - return -1; - } - - mask_prefix(p_r, prefix, plen < 0 ? 128 : ae == 1 ? plen + 96 : plen); - return ret; -} - -static void -parse_route_attributes(const unsigned char *a, int alen, - unsigned char *channels) -{ - int type, len, i = 0; - - while(i < alen) { - type = a[i]; - if(type == 0) { - i++; - continue; - } - - if(i + 1 > alen) { - fprintf(stderr, "Received truncated attributes.\n"); - return; - } - len = a[i + 1]; - if(i + len > alen) { - fprintf(stderr, "Received truncated attributes.\n"); - return; - } - - if(type == 1) { - /* Nothing. */ - } else if(type == 2) { - if(len > DIVERSITY_HOPS) { - fprintf(stderr, - "Received overlong channel information (%d > %d).\n", - len, DIVERSITY_HOPS); - len = DIVERSITY_HOPS; - } - if(memchr(a + i + 2, 0, len) != NULL) { - /* 0 is reserved. */ - fprintf(stderr, "Channel information contains 0!"); - return; - } - memset(channels, 0, DIVERSITY_HOPS); - memcpy(channels, a + i + 2, len); - } else { - fprintf(stderr, "Received unknown route attribute %d.\n", type); - } - - i += len + 2; - } -} - -static int -network_address(int ae, const unsigned char *a, unsigned int len, - unsigned char *a_r) -{ - return network_prefix(ae, -1, 0, a, NULL, len, a_r); -} - -static int -channels_len(unsigned char *channels) -{ - unsigned char *p = memchr(channels, 0, DIVERSITY_HOPS); - return p ? (p - channels) : DIVERSITY_HOPS; -} - -void -parse_packet(const unsigned char *from, struct interface *ifp, - const unsigned char *packet, int packetlen) -{ - int i; - const unsigned char *message; - unsigned char type, len; - int bodylen; - struct neighbour *neigh; - int have_router_id = 0, have_v4_prefix = 0, have_v6_prefix = 0, - have_v4_nh = 0, have_v6_nh = 0; - unsigned char router_id[8], v4_prefix[16], v6_prefix[16], - v4_nh[16], v6_nh[16]; - - if(!linklocal(from)) { - zlog_err("Received packet from non-local address %s.", - format_address(from)); - return; - } - - if(packet[0] != 42) { - zlog_err("Received malformed packet on %s from %s.", - ifp->name, format_address(from)); - return; - } - - if(packet[1] != 2) { - zlog_err("Received packet with unknown version %d on %s from %s.", - packet[1], ifp->name, format_address(from)); - return; - } - - neigh = find_neighbour(from, ifp); - if(neigh == NULL) { - zlog_err("Couldn't allocate neighbour."); - return; - } - - DO_NTOHS(bodylen, packet + 2); - - if(bodylen + 4 > packetlen) { - zlog_err("Received truncated packet (%d + 4 > %d).", - bodylen, packetlen); - bodylen = packetlen - 4; - } - - i = 0; - while(i < bodylen) { - message = packet + 4 + i; - type = message[0]; - if(type == MESSAGE_PAD1) { - debugf(BABEL_DEBUG_COMMON,"Received pad1 from %s on %s.", - format_address(from), ifp->name); - i++; - continue; - } - if(i + 1 > bodylen) { - zlog_err("Received truncated message."); - break; - } - len = message[1]; - if(i + len > bodylen) { - zlog_err("Received truncated message."); - break; - } - - if(type == MESSAGE_PADN) { - debugf(BABEL_DEBUG_COMMON,"Received pad%d from %s on %s.", - len, format_address(from), ifp->name); - } else if(type == MESSAGE_ACK_REQ) { - unsigned short nonce, interval; - if(len < 6) goto fail; - DO_NTOHS(nonce, message + 4); - DO_NTOHS(interval, message + 6); - debugf(BABEL_DEBUG_COMMON,"Received ack-req (%04X %d) from %s on %s.", - nonce, interval, format_address(from), ifp->name); - send_ack(neigh, nonce, interval); - } else if(type == MESSAGE_ACK) { - debugf(BABEL_DEBUG_COMMON,"Received ack from %s on %s.", - format_address(from), ifp->name); - /* Nothing right now */ - } else if(type == MESSAGE_HELLO) { - unsigned short seqno, interval; - int changed; - if(len < 6) goto fail; - DO_NTOHS(seqno, message + 4); - DO_NTOHS(interval, message + 6); - debugf(BABEL_DEBUG_COMMON,"Received hello %d (%d) from %s on %s.", - seqno, interval, - format_address(from), ifp->name); - changed = update_neighbour(neigh, seqno, interval); - update_neighbour_metric(neigh, changed); - if(interval > 0) - schedule_neighbours_check(interval * 10, 0); - } else if(type == MESSAGE_IHU) { - unsigned short txcost, interval; - unsigned char address[16]; - int rc; - if(len < 6) goto fail; - DO_NTOHS(txcost, message + 4); - DO_NTOHS(interval, message + 6); - rc = network_address(message[2], message + 8, len - 6, address); - if(rc < 0) goto fail; - debugf(BABEL_DEBUG_COMMON,"Received ihu %d (%d) from %s on %s for %s.", - txcost, interval, - format_address(from), ifp->name, - format_address(address)); - if(message[2] == 0 || is_interface_ll_address(ifp, address)) { - int changed = txcost != neigh->txcost; - neigh->txcost = txcost; - neigh->ihu_time = babel_now; - neigh->ihu_interval = interval; - update_neighbour_metric(neigh, changed); - if(interval > 0) - schedule_neighbours_check(interval * 10 * 3, 0); - } - } else if(type == MESSAGE_ROUTER_ID) { - if(len < 10) { - have_router_id = 0; - goto fail; - } - memcpy(router_id, message + 4, 8); - have_router_id = 1; - debugf(BABEL_DEBUG_COMMON,"Received router-id %s from %s on %s.", - format_eui64(router_id), format_address(from), ifp->name); - } else if(type == MESSAGE_NH) { - unsigned char nh[16]; - int rc; - if(len < 2) { - have_v4_nh = 0; - have_v6_nh = 0; - goto fail; - } - rc = network_address(message[2], message + 4, len - 2, - nh); - if(rc < 0) { - have_v4_nh = 0; - have_v6_nh = 0; - goto fail; - } - debugf(BABEL_DEBUG_COMMON,"Received nh %s (%d) from %s on %s.", - format_address(nh), message[2], - format_address(from), ifp->name); - if(message[2] == 1) { - memcpy(v4_nh, nh, 16); - have_v4_nh = 1; - } else { - memcpy(v6_nh, nh, 16); - have_v6_nh = 1; - } - } else if(type == MESSAGE_UPDATE) { - unsigned char prefix[16], *nh; - unsigned char plen; - unsigned char channels[DIVERSITY_HOPS]; - unsigned short interval, seqno, metric; - int rc, parsed_len; - if(len < 10) { - if(len < 2 || message[3] & 0x80) - have_v4_prefix = have_v6_prefix = 0; - goto fail; - } - DO_NTOHS(interval, message + 6); - DO_NTOHS(seqno, message + 8); - DO_NTOHS(metric, message + 10); - if(message[5] == 0 || - (message[3] == 1 ? have_v4_prefix : have_v6_prefix)) - rc = network_prefix(message[2], message[4], message[5], - message + 12, - message[2] == 1 ? v4_prefix : v6_prefix, - len - 10, prefix); - else - rc = -1; - if(rc < 0) { - if(message[3] & 0x80) - have_v4_prefix = have_v6_prefix = 0; - goto fail; - } - parsed_len = 10 + rc; - - plen = message[4] + (message[2] == 1 ? 96 : 0); - - if(message[3] & 0x80) { - if(message[2] == 1) { - memcpy(v4_prefix, prefix, 16); - have_v4_prefix = 1; - } else { - memcpy(v6_prefix, prefix, 16); - have_v6_prefix = 1; - } - } - if(message[3] & 0x40) { - if(message[2] == 1) { - memset(router_id, 0, 4); - memcpy(router_id + 4, prefix + 12, 4); - } else { - memcpy(router_id, prefix + 8, 8); - } - have_router_id = 1; - } - if(!have_router_id && message[2] != 0) { - zlog_err("Received prefix with no router id."); - goto fail; - } - debugf(BABEL_DEBUG_COMMON,"Received update%s%s for %s from %s on %s.", - (message[3] & 0x80) ? "/prefix" : "", - (message[3] & 0x40) ? "/id" : "", - format_prefix(prefix, plen), - format_address(from), ifp->name); - - if(message[2] == 0) { - if(metric < 0xFFFF) { - zlog_err("Received wildcard update with finite metric."); - goto done; - } - retract_neighbour_routes(neigh); - goto done; - } else if(message[2] == 1) { - if(!have_v4_nh) - goto fail; - nh = v4_nh; - } else if(have_v6_nh) { - nh = v6_nh; - } else { - nh = neigh->address; - } - - if(message[2] == 1) { - if(!babel_get_if_nfo(ifp)->ipv4) - goto done; - } - - if((ifp->flags & BABEL_IF_FARAWAY)) { - channels[0] = 0; - } else { - /* This will be overwritten by parse_route_attributes below. */ - if(metric < 256) { - /* Assume non-interfering (wired) link. */ - channels[0] = 0; - } else { - /* Assume interfering. */ - channels[0] = BABEL_IF_CHANNEL_INTERFERING; - channels[1] = 0; - } - - if(parsed_len < len) - parse_route_attributes(message + 2 + parsed_len, - len - parsed_len, channels); - } - - update_route(router_id, prefix, plen, seqno, metric, interval, - neigh, nh, - channels, channels_len(channels)); - } else if(type == MESSAGE_REQUEST) { - unsigned char prefix[16], plen; - int rc; - if(len < 2) goto fail; - rc = network_prefix(message[2], message[3], 0, - message + 4, NULL, len - 2, prefix); - if(rc < 0) goto fail; - plen = message[3] + (message[2] == 1 ? 96 : 0); - debugf(BABEL_DEBUG_COMMON,"Received request for %s from %s on %s.", - message[2] == 0 ? "any" : format_prefix(prefix, plen), - format_address(from), ifp->name); - if(message[2] == 0) { - struct babel_interface *babel_ifp =babel_get_if_nfo(neigh->ifp); - /* If a neighbour is requesting a full route dump from us, - we might as well send it an IHU. */ - send_ihu(neigh, NULL); - /* Since nodes send wildcard requests on boot, booting - a large number of nodes at the same time may cause an - update storm. Ignore a wildcard request that happens - shortly after we sent a full update. */ - if(babel_ifp->last_update_time < - (time_t)(babel_now.tv_sec - - MAX(babel_ifp->hello_interval / 100, 1))) - send_update(neigh->ifp, 0, NULL, 0); - } else { - send_update(neigh->ifp, 0, prefix, plen); - } - } else if(type == MESSAGE_MH_REQUEST) { - unsigned char prefix[16], plen; - unsigned short seqno; - int rc; - if(len < 14) goto fail; - DO_NTOHS(seqno, message + 4); - rc = network_prefix(message[2], message[3], 0, - message + 16, NULL, len - 14, prefix); - if(rc < 0) goto fail; - plen = message[3] + (message[2] == 1 ? 96 : 0); - debugf(BABEL_DEBUG_COMMON,"Received request (%d) for %s from %s on %s (%s, %d).", - message[6], - format_prefix(prefix, plen), - format_address(from), ifp->name, - format_eui64(message + 8), seqno); - handle_request(neigh, prefix, plen, message[6], - seqno, message + 8); - } else { - debugf(BABEL_DEBUG_COMMON,"Received unknown packet type %d from %s on %s.", - type, format_address(from), ifp->name); - } - done: - i += len + 2; - continue; - - fail: - zlog_err("Couldn't parse packet (%d, %d) from %s on %s.", - message[0], message[1], format_address(from), ifp->name); - goto done; - } - return; -} - -/* Under normal circumstances, there are enough moderation mechanisms - elsewhere in the protocol to make sure that this last-ditch check - should never trigger. But I'm superstitious. */ - -static int -check_bucket(struct interface *ifp) -{ - babel_interface_nfo *babel_ifp = babel_get_if_nfo(ifp); - if(babel_ifp->bucket <= 0) { - int seconds = babel_now.tv_sec - babel_ifp->bucket_time; - if(seconds > 0) { - babel_ifp->bucket = MIN(BUCKET_TOKENS_MAX, - seconds * BUCKET_TOKENS_PER_SEC); - } - /* Reset bucket time unconditionally, in case clock is stepped. */ - babel_ifp->bucket_time = babel_now.tv_sec; - } - - if(babel_ifp->bucket > 0) { - babel_ifp->bucket--; - return 1; - } else { - return 0; - } -} - -void -flushbuf(struct interface *ifp) -{ - int rc; - struct sockaddr_in6 sin6; - babel_interface_nfo *babel_ifp = babel_get_if_nfo(ifp); - - assert(babel_ifp->buffered <= babel_ifp->bufsize); - - flushupdates(ifp); - - if(babel_ifp->buffered > 0) { - debugf(BABEL_DEBUG_COMMON," (flushing %d buffered bytes on %s)", - babel_ifp->buffered, ifp->name); - if(check_bucket(ifp)) { - memset(&sin6, 0, sizeof(sin6)); - sin6.sin6_family = AF_INET6; - memcpy(&sin6.sin6_addr, protocol_group, 16); - sin6.sin6_port = htons(protocol_port); - sin6.sin6_scope_id = ifp->ifindex; - DO_HTONS(packet_header + 2, babel_ifp->buffered); - rc = babel_send(protocol_socket, - packet_header, sizeof(packet_header), - babel_ifp->sendbuf, babel_ifp->buffered, - (struct sockaddr*)&sin6, sizeof(sin6)); - if(rc < 0) - zlog_err("send: %s", safe_strerror(errno)); - } else { - zlog_err("Warning: bucket full, dropping packet to %s.", - ifp->name); - } - } - VALGRIND_MAKE_MEM_UNDEFINED(babel_ifp->sendbuf, babel_ifp->bufsize); - babel_ifp->buffered = 0; - babel_ifp->have_buffered_hello = 0; - babel_ifp->have_buffered_id = 0; - babel_ifp->have_buffered_nh = 0; - babel_ifp->have_buffered_prefix = 0; - babel_ifp->flush_timeout.tv_sec = 0; - babel_ifp->flush_timeout.tv_usec = 0; -} - -static void -schedule_flush(struct interface *ifp) -{ - babel_interface_nfo *babel_ifp = babel_get_if_nfo(ifp); - unsigned msecs = jitter(babel_ifp, 0); - if(babel_ifp->flush_timeout.tv_sec != 0 && - timeval_minus_msec(&babel_ifp->flush_timeout, &babel_now) < msecs) - return; - set_timeout(&babel_ifp->flush_timeout, msecs); -} - -static void -schedule_flush_now(struct interface *ifp) -{ - babel_interface_nfo *babel_ifp = babel_get_if_nfo(ifp); - /* Almost now */ - unsigned msecs = roughly(10); - if(babel_ifp->flush_timeout.tv_sec != 0 && - timeval_minus_msec(&babel_ifp->flush_timeout, &babel_now) < msecs) - return; - set_timeout(&babel_ifp->flush_timeout, msecs); -} - -static void -schedule_unicast_flush(unsigned msecs) -{ - if(!unicast_neighbour) - return; - if(unicast_flush_timeout.tv_sec != 0 && - timeval_minus_msec(&unicast_flush_timeout, &babel_now) < msecs) - return; - unicast_flush_timeout.tv_usec = (babel_now.tv_usec + msecs * 1000) %1000000; - unicast_flush_timeout.tv_sec = - babel_now.tv_sec + (babel_now.tv_usec / 1000 + msecs) / 1000; -} - -static void -ensure_space(struct interface *ifp, int space) -{ - babel_interface_nfo *babel_ifp = babel_get_if_nfo(ifp); - if(babel_ifp->bufsize - babel_ifp->buffered < space) - flushbuf(ifp); -} - -static void -start_message(struct interface *ifp, int type, int len) -{ - babel_interface_nfo *babel_ifp = babel_get_if_nfo(ifp); - if(babel_ifp->bufsize - babel_ifp->buffered < len + 2) - flushbuf(ifp); - babel_ifp->sendbuf[babel_ifp->buffered++] = type; - babel_ifp->sendbuf[babel_ifp->buffered++] = len; -} - -static void -end_message(struct interface *ifp, int type, int bytes) -{ - babel_interface_nfo *babel_ifp = babel_get_if_nfo(ifp); - assert(babel_ifp->buffered >= bytes + 2 && - babel_ifp->sendbuf[babel_ifp->buffered - bytes - 2] == type && - babel_ifp->sendbuf[babel_ifp->buffered - bytes - 1] == bytes); - schedule_flush(ifp); -} - -static void -accumulate_byte(struct interface *ifp, unsigned char value) -{ - babel_interface_nfo *babel_ifp = babel_get_if_nfo(ifp); - babel_ifp->sendbuf[babel_ifp->buffered++] = value; -} - -static void -accumulate_short(struct interface *ifp, unsigned short value) -{ - babel_interface_nfo *babel_ifp = babel_get_if_nfo(ifp); - DO_HTONS(babel_ifp->sendbuf + babel_ifp->buffered, value); - babel_ifp->buffered += 2; -} - -static void -accumulate_bytes(struct interface *ifp, - const unsigned char *value, unsigned len) -{ - babel_interface_nfo *babel_ifp = babel_get_if_nfo(ifp); - memcpy(babel_ifp->sendbuf + babel_ifp->buffered, value, len); - babel_ifp->buffered += len; -} - -static int -start_unicast_message(struct neighbour *neigh, int type, int len) -{ - if(unicast_neighbour) { - if(neigh != unicast_neighbour || - unicast_buffered + len + 2 >= - MIN(UNICAST_BUFSIZE, babel_get_if_nfo(neigh->ifp)->bufsize)) - flush_unicast(0); - } - if(!unicast_buffer) - unicast_buffer = malloc(UNICAST_BUFSIZE); - if(!unicast_buffer) { - zlog_err("malloc(unicast_buffer): %s", safe_strerror(errno)); - return -1; - } - - unicast_neighbour = neigh; - - unicast_buffer[unicast_buffered++] = type; - unicast_buffer[unicast_buffered++] = len; - return 1; -} - -static void -end_unicast_message(struct neighbour *neigh, int type, int bytes) -{ - assert(unicast_neighbour == neigh && unicast_buffered >= bytes + 2 && - unicast_buffer[unicast_buffered - bytes - 2] == type && - unicast_buffer[unicast_buffered - bytes - 1] == bytes); - schedule_unicast_flush(jitter(babel_get_if_nfo(neigh->ifp), 0)); -} - -static void -accumulate_unicast_byte(struct neighbour *neigh, unsigned char value) -{ - unicast_buffer[unicast_buffered++] = value; -} - -static void -accumulate_unicast_short(struct neighbour *neigh, unsigned short value) -{ - DO_HTONS(unicast_buffer + unicast_buffered, value); - unicast_buffered += 2; -} - -static void -accumulate_unicast_bytes(struct neighbour *neigh, - const unsigned char *value, unsigned len) -{ - memcpy(unicast_buffer + unicast_buffered, value, len); - unicast_buffered += len; -} - -void -send_ack(struct neighbour *neigh, unsigned short nonce, unsigned short interval) -{ - int rc; - debugf(BABEL_DEBUG_COMMON,"Sending ack (%04x) to %s on %s.", - nonce, format_address(neigh->address), neigh->ifp->name); - rc = start_unicast_message(neigh, MESSAGE_ACK, 2); if(rc < 0) return; - accumulate_unicast_short(neigh, nonce); - end_unicast_message(neigh, MESSAGE_ACK, 2); - /* Roughly yields a value no larger than 3/2, so this meets the deadline */ - schedule_unicast_flush(roughly(interval * 6)); -} - -void -send_hello_noupdate(struct interface *ifp, unsigned interval) -{ - babel_interface_nfo *babel_ifp = babel_get_if_nfo(ifp); - /* This avoids sending multiple hellos in a single packet, which breaks - link quality estimation. */ - if(babel_ifp->have_buffered_hello) - flushbuf(ifp); - - babel_ifp->hello_seqno = seqno_plus(babel_ifp->hello_seqno, 1); - set_timeout(&babel_ifp->hello_timeout, babel_ifp->hello_interval); - - if(!if_up(ifp)) - return; - - debugf(BABEL_DEBUG_COMMON,"Sending hello %d (%d) to %s.", - babel_ifp->hello_seqno, interval, ifp->name); - - start_message(ifp, MESSAGE_HELLO, 6); - accumulate_short(ifp, 0); - accumulate_short(ifp, babel_ifp->hello_seqno); - accumulate_short(ifp, interval > 0xFFFF ? 0xFFFF : interval); - end_message(ifp, MESSAGE_HELLO, 6); - babel_ifp->have_buffered_hello = 1; -} - -void -send_hello(struct interface *ifp) -{ - babel_interface_nfo *babel_ifp = babel_get_if_nfo(ifp); - send_hello_noupdate(ifp, (babel_ifp->hello_interval + 9) / 10); - /* Send full IHU every 3 hellos, and marginal IHU each time */ - if(babel_ifp->hello_seqno % 3 == 0) - send_ihu(NULL, ifp); - else - send_marginal_ihu(ifp); -} - -void -flush_unicast(int dofree) -{ - struct sockaddr_in6 sin6; - int rc; - - if(unicast_buffered == 0) - goto done; - - if(!if_up(unicast_neighbour->ifp)) - goto done; - - /* Preserve ordering of messages */ - flushbuf(unicast_neighbour->ifp); - - if(check_bucket(unicast_neighbour->ifp)) { - memset(&sin6, 0, sizeof(sin6)); - sin6.sin6_family = AF_INET6; - memcpy(&sin6.sin6_addr, unicast_neighbour->address, 16); - sin6.sin6_port = htons(protocol_port); - sin6.sin6_scope_id = unicast_neighbour->ifp->ifindex; - DO_HTONS(packet_header + 2, unicast_buffered); - rc = babel_send(protocol_socket, - packet_header, sizeof(packet_header), - unicast_buffer, unicast_buffered, - (struct sockaddr*)&sin6, sizeof(sin6)); - if(rc < 0) - zlog_err("send(unicast): %s", safe_strerror(errno)); - } else { - zlog_err("Warning: bucket full, dropping unicast packet to %s if %s.", - format_address(unicast_neighbour->address), - unicast_neighbour->ifp->name); - } - - done: - VALGRIND_MAKE_MEM_UNDEFINED(unicast_buffer, UNICAST_BUFSIZE); - unicast_buffered = 0; - if(dofree && unicast_buffer) { - free(unicast_buffer); - unicast_buffer = NULL; - } - unicast_neighbour = NULL; - unicast_flush_timeout.tv_sec = 0; - unicast_flush_timeout.tv_usec = 0; -} - -static void -really_send_update(struct interface *ifp, - const unsigned char *id, - const unsigned char *prefix, unsigned char plen, - unsigned short seqno, unsigned short metric, - unsigned char *channels, int channels_len) -{ - babel_interface_nfo *babel_ifp = babel_get_if_nfo(ifp); - int add_metric, v4, real_plen, omit = 0; - const unsigned char *real_prefix; - unsigned short flags = 0; - int channels_size; - - if(diversity_kind != DIVERSITY_CHANNEL) - channels_len = -1; - - channels_size = channels_len >= 0 ? channels_len + 2 : 0; - - if(!if_up(ifp)) - return; - - add_metric = output_filter(id, prefix, plen, ifp->ifindex); - if(add_metric >= INFINITY) - return; - - metric = MIN(metric + add_metric, INFINITY); - /* Worst case */ - ensure_space(ifp, 20 + 12 + 28); - - v4 = plen >= 96 && v4mapped(prefix); - - if(v4) { - if(!babel_ifp->ipv4) - return; - if(!babel_ifp->have_buffered_nh || - memcmp(babel_ifp->buffered_nh, babel_ifp->ipv4, 4) != 0) { - start_message(ifp, MESSAGE_NH, 6); - accumulate_byte(ifp, 1); - accumulate_byte(ifp, 0); - accumulate_bytes(ifp, babel_ifp->ipv4, 4); - end_message(ifp, MESSAGE_NH, 6); - memcpy(babel_ifp->buffered_nh, babel_ifp->ipv4, 4); - babel_ifp->have_buffered_nh = 1; - } - - real_prefix = prefix + 12; - real_plen = plen - 96; - } else { - if(babel_ifp->have_buffered_prefix) { - while(omit < plen / 8 && - babel_ifp->buffered_prefix[omit] == prefix[omit]) - omit++; - } - if(!babel_ifp->have_buffered_prefix || plen >= 48) - flags |= 0x80; - real_prefix = prefix; - real_plen = plen; - } - - if(!babel_ifp->have_buffered_id - || memcmp(id, babel_ifp->buffered_id, 8) != 0) { - if(real_plen == 128 && memcmp(real_prefix + 8, id, 8) == 0) { - flags |= 0x40; - } else { - start_message(ifp, MESSAGE_ROUTER_ID, 10); - accumulate_short(ifp, 0); - accumulate_bytes(ifp, id, 8); - end_message(ifp, MESSAGE_ROUTER_ID, 10); - } - memcpy(babel_ifp->buffered_id, id, 16); - babel_ifp->have_buffered_id = 1; - } - - start_message(ifp, MESSAGE_UPDATE, 10 + (real_plen + 7) / 8 - omit + - channels_size); - accumulate_byte(ifp, v4 ? 1 : 2); - accumulate_byte(ifp, flags); - accumulate_byte(ifp, real_plen); - accumulate_byte(ifp, omit); - accumulate_short(ifp, (babel_ifp->update_interval + 5) / 10); - accumulate_short(ifp, seqno); - accumulate_short(ifp, metric); - accumulate_bytes(ifp, real_prefix + omit, (real_plen + 7) / 8 - omit); - /* Note that an empty channels TLV is different from no such TLV. */ - if(channels_len >= 0) { - accumulate_byte(ifp, 2); - accumulate_byte(ifp, channels_len); - accumulate_bytes(ifp, channels, channels_len); - } - end_message(ifp, MESSAGE_UPDATE, 10 + (real_plen + 7) / 8 - omit + - channels_size); - - if(flags & 0x80) { - memcpy(babel_ifp->buffered_prefix, prefix, 16); - babel_ifp->have_buffered_prefix = 1; - } -} - -static int -compare_buffered_updates(const void *av, const void *bv) -{ - const struct buffered_update *a = av, *b = bv; - int rc, v4a, v4b, ma, mb; - - rc = memcmp(a->id, b->id, 8); - if(rc != 0) - return rc; - - v4a = (a->plen >= 96 && v4mapped(a->prefix)); - v4b = (b->plen >= 96 && v4mapped(b->prefix)); - - if(v4a > v4b) - return 1; - else if(v4a < v4b) - return -1; - - ma = (!v4a && a->plen == 128 && memcmp(a->prefix + 8, a->id, 8) == 0); - mb = (!v4b && b->plen == 128 && memcmp(b->prefix + 8, b->id, 8) == 0); - - if(ma > mb) - return -1; - else if(mb > ma) - return 1; - - if(a->plen < b->plen) - return 1; - else if(a->plen > b->plen) - return -1; - - return memcmp(a->prefix, b->prefix, 16); -} - -void -flushupdates(struct interface *ifp) -{ - babel_interface_nfo *babel_ifp = NULL; - struct xroute *xroute; - struct babel_route *route; - const unsigned char *last_prefix = NULL; - unsigned char last_plen = 0xFF; - int i; - - if(ifp == NULL) { - struct interface *ifp_aux; - struct listnode *linklist_node = NULL; - FOR_ALL_INTERFACES(ifp_aux, linklist_node) - flushupdates(ifp_aux); - return; - } - - babel_ifp = babel_get_if_nfo(ifp); - if(babel_ifp->num_buffered_updates > 0) { - struct buffered_update *b = babel_ifp->buffered_updates; - int n = babel_ifp->num_buffered_updates; - - babel_ifp->buffered_updates = NULL; - babel_ifp->update_bufsize = 0; - babel_ifp->num_buffered_updates = 0; - - if(!if_up(ifp)) - goto done; - - debugf(BABEL_DEBUG_COMMON," (flushing %d buffered updates on %s (%d))", - n, ifp->name, ifp->ifindex); - - /* In order to send fewer update messages, we want to send updates - with the same router-id together, with IPv6 going out before IPv4. */ - - for(i = 0; i < n; i++) { - route = find_installed_route(b[i].prefix, b[i].plen); - if(route) - memcpy(b[i].id, route->src->id, 8); - else - memcpy(b[i].id, myid, 8); - } - - qsort(b, n, sizeof(struct buffered_update), compare_buffered_updates); - - for(i = 0; i < n; i++) { - /* The same update may be scheduled multiple times before it is - sent out. Since our buffer is now sorted, it is enough to - compare with the previous update. */ - - if(last_prefix) { - if(b[i].plen == last_plen && - memcmp(b[i].prefix, last_prefix, 16) == 0) - continue; - } - - xroute = find_xroute(b[i].prefix, b[i].plen); - route = find_installed_route(b[i].prefix, b[i].plen); - - if(xroute && (!route || xroute->metric <= kernel_metric)) { - really_send_update(ifp, myid, - xroute->prefix, xroute->plen, - myseqno, xroute->metric, - NULL, 0); - last_prefix = xroute->prefix; - last_plen = xroute->plen; - } else if(route) { - unsigned char channels[DIVERSITY_HOPS]; - int chlen; - struct interface *route_ifp = route->neigh->ifp; - struct babel_interface *babel_route_ifp = NULL; - unsigned short metric; - unsigned short seqno; - - seqno = route->seqno; - metric = - route_interferes(route, ifp) ? - route_metric(route) : - route_metric_noninterfering(route); - - if(metric < INFINITY) - satisfy_request(route->src->prefix, route->src->plen, - seqno, route->src->id, ifp); - if((babel_ifp->flags & BABEL_IF_SPLIT_HORIZON) && - route->neigh->ifp == ifp) - continue; - - babel_route_ifp = babel_get_if_nfo(route_ifp); - if(babel_route_ifp->channel ==BABEL_IF_CHANNEL_NONINTERFERING) { - memcpy(channels, route->channels, DIVERSITY_HOPS); - } else { - if(babel_route_ifp->channel == BABEL_IF_CHANNEL_UNKNOWN) - channels[0] = BABEL_IF_CHANNEL_INTERFERING; - else { - assert(babel_route_ifp->channel > 0 && - babel_route_ifp->channel <= 255); - channels[0] = babel_route_ifp->channel; - } - memcpy(channels + 1, route->channels, DIVERSITY_HOPS - 1); - } - - chlen = channels_len(channels); - really_send_update(ifp, route->src->id, - route->src->prefix, - route->src->plen, - seqno, metric, - channels, chlen); - update_source(route->src, seqno, metric); - last_prefix = route->src->prefix; - last_plen = route->src->plen; - } else { - /* There's no route for this prefix. This can happen shortly - after an xroute has been retracted, so send a retraction. */ - really_send_update(ifp, myid, b[i].prefix, b[i].plen, - myseqno, INFINITY, NULL, -1); - } - } - schedule_flush_now(ifp); - done: - free(b); - } - babel_ifp->update_flush_timeout.tv_sec = 0; - babel_ifp->update_flush_timeout.tv_usec = 0; -} - -static void -schedule_update_flush(struct interface *ifp, int urgent) -{ - babel_interface_nfo *babel_ifp = babel_get_if_nfo(ifp); - unsigned msecs; - msecs = update_jitter(babel_ifp, urgent); - if(babel_ifp->update_flush_timeout.tv_sec != 0 && - timeval_minus_msec(&babel_ifp->update_flush_timeout, &babel_now) < msecs) - return; - set_timeout(&babel_ifp->update_flush_timeout, msecs); -} - -static void -buffer_update(struct interface *ifp, - const unsigned char *prefix, unsigned char plen) -{ - babel_interface_nfo *babel_ifp = babel_get_if_nfo(ifp); - if(babel_ifp->num_buffered_updates > 0 && - babel_ifp->num_buffered_updates >= babel_ifp->update_bufsize) - flushupdates(ifp); - - if(babel_ifp->update_bufsize == 0) { - int n; - assert(babel_ifp->buffered_updates == NULL); - /* Allocate enough space to hold a full update. Since the - number of installed routes will grow over time, make sure we - have enough space to send a full-ish frame. */ - n = installed_routes_estimate() + xroutes_estimate() + 4; - n = MAX(n, babel_ifp->bufsize / 16); - again: - babel_ifp->buffered_updates = malloc(n *sizeof(struct buffered_update)); - if(babel_ifp->buffered_updates == NULL) { - zlog_err("malloc(buffered_updates): %s", safe_strerror(errno)); - if(n > 4) { - /* Try again with a tiny buffer. */ - n = 4; - goto again; - } - return; - } - babel_ifp->update_bufsize = n; - babel_ifp->num_buffered_updates = 0; - } - - memcpy(babel_ifp->buffered_updates[babel_ifp->num_buffered_updates].prefix, - prefix, 16); - babel_ifp->buffered_updates[babel_ifp->num_buffered_updates].plen = plen; - babel_ifp->num_buffered_updates++; -} - -static void -buffer_update_callback(struct babel_route *route, void *closure) -{ - buffer_update((struct interface*)closure, - route->src->prefix, route->src->plen); -} - -void -send_update(struct interface *ifp, int urgent, - const unsigned char *prefix, unsigned char plen) -{ - babel_interface_nfo *babel_ifp = NULL; - - if(ifp == NULL) { - struct interface *ifp_aux; - struct listnode *linklist_node = NULL; - struct babel_route *route; - FOR_ALL_INTERFACES(ifp_aux, linklist_node) - send_update(ifp_aux, urgent, prefix, plen); - if(prefix) { - /* Since flushupdates only deals with non-wildcard interfaces, we - need to do this now. */ - route = find_installed_route(prefix, plen); - if(route && route_metric(route) < INFINITY) - satisfy_request(prefix, plen, route->src->seqno, route->src->id, - NULL); - } - return; - } - - if(!if_up(ifp)) - return; - - babel_ifp = babel_get_if_nfo(ifp); - if(prefix) { - debugf(BABEL_DEBUG_COMMON,"Sending update to %s for %s.", - ifp->name, format_prefix(prefix, plen)); - buffer_update(ifp, prefix, plen); - } else { - send_self_update(ifp); - debugf(BABEL_DEBUG_COMMON,"Sending update to %s for any.", ifp->name); - for_all_installed_routes(buffer_update_callback, ifp); - set_timeout(&babel_ifp->update_timeout, babel_ifp->update_interval); - babel_ifp->last_update_time = babel_now.tv_sec; - } - schedule_update_flush(ifp, urgent); -} - -void -send_update_resend(struct interface *ifp, - const unsigned char *prefix, unsigned char plen) -{ - assert(prefix != NULL); - - send_update(ifp, 1, prefix, plen); - record_resend(RESEND_UPDATE, prefix, plen, 0, 0, NULL, resend_delay); -} - -void -send_wildcard_retraction(struct interface *ifp) -{ - babel_interface_nfo *babel_ifp = NULL; - if(ifp == NULL) { - struct interface *ifp_aux; - struct listnode *linklist_node = NULL; - FOR_ALL_INTERFACES(ifp_aux, linklist_node) - send_wildcard_retraction(ifp_aux); - return; - } - - if(!if_up(ifp)) - return; - - babel_ifp = babel_get_if_nfo(ifp); - start_message(ifp, MESSAGE_UPDATE, 10); - accumulate_byte(ifp, 0); - accumulate_byte(ifp, 0x40); - accumulate_byte(ifp, 0); - accumulate_byte(ifp, 0); - accumulate_short(ifp, 0xFFFF); - accumulate_short(ifp, myseqno); - accumulate_short(ifp, 0xFFFF); - end_message(ifp, MESSAGE_UPDATE, 10); - - babel_ifp->have_buffered_id = 0; -} - -void -update_myseqno() -{ - myseqno = seqno_plus(myseqno, 1); - seqno_time = babel_now; -} - -static void -send_xroute_update_callback(struct xroute *xroute, void *closure) -{ - struct interface *ifp = (struct interface*)closure; - send_update(ifp, 0, xroute->prefix, xroute->plen); -} - -void -send_self_update(struct interface *ifp) -{ - if(ifp == NULL) { - struct interface *ifp_aux; - struct listnode *linklist_node = NULL; - FOR_ALL_INTERFACES(ifp_aux, linklist_node) { - if(!if_up(ifp_aux)) - continue; - send_self_update(ifp_aux); - } - return; - } - - debugf(BABEL_DEBUG_COMMON,"Sending self update to %s.", ifp->name); - for_all_xroutes(send_xroute_update_callback, ifp); -} - -void -send_ihu(struct neighbour *neigh, struct interface *ifp) -{ - babel_interface_nfo *babel_ifp = NULL; - int rxcost, interval; - int ll; - - if(neigh == NULL && ifp == NULL) { - struct interface *ifp_aux; - struct listnode *linklist_node = NULL; - FOR_ALL_INTERFACES(ifp_aux, linklist_node) { - if(if_up(ifp_aux)) - continue; - send_ihu(NULL, ifp_aux); - } - return; - } - - if(neigh == NULL) { - struct neighbour *ngh; - FOR_ALL_NEIGHBOURS(ngh) { - if(ngh->ifp == ifp) - send_ihu(ngh, ifp); - } - return; - } - - - if(ifp && neigh->ifp != ifp) - return; - - ifp = neigh->ifp; - babel_ifp = babel_get_if_nfo(ifp); - if(!if_up(ifp)) - return; - - rxcost = neighbour_rxcost(neigh); - interval = (babel_ifp->hello_interval * 3 + 9) / 10; - - /* Conceptually, an IHU is a unicast message. We usually send them as - multicast, since this allows aggregation into a single packet and - avoids an ARP exchange. If we already have a unicast message queued - for this neighbour, however, we might as well piggyback the IHU. */ - debugf(BABEL_DEBUG_COMMON,"Sending %sihu %d on %s to %s.", - unicast_neighbour == neigh ? "unicast " : "", - rxcost, - neigh->ifp->name, - format_address(neigh->address)); - - ll = linklocal(neigh->address); - - if(unicast_neighbour != neigh) { - start_message(ifp, MESSAGE_IHU, ll ? 14 : 22); - accumulate_byte(ifp, ll ? 3 : 2); - accumulate_byte(ifp, 0); - accumulate_short(ifp, rxcost); - accumulate_short(ifp, interval); - if(ll) - accumulate_bytes(ifp, neigh->address + 8, 8); - else - accumulate_bytes(ifp, neigh->address, 16); - end_message(ifp, MESSAGE_IHU, ll ? 14 : 22); - } else { - int rc; - rc = start_unicast_message(neigh, MESSAGE_IHU, ll ? 14 : 22); - if(rc < 0) return; - accumulate_unicast_byte(neigh, ll ? 3 : 2); - accumulate_unicast_byte(neigh, 0); - accumulate_unicast_short(neigh, rxcost); - accumulate_unicast_short(neigh, interval); - if(ll) - accumulate_unicast_bytes(neigh, neigh->address + 8, 8); - else - accumulate_unicast_bytes(neigh, neigh->address, 16); - end_unicast_message(neigh, MESSAGE_IHU, ll ? 14 : 22); - } -} - -/* Send IHUs to all marginal neighbours */ -void -send_marginal_ihu(struct interface *ifp) -{ - struct neighbour *neigh; - FOR_ALL_NEIGHBOURS(neigh) { - if(ifp && neigh->ifp != ifp) - continue; - if(neigh->txcost >= 384 || (neigh->reach & 0xF000) != 0xF000) - send_ihu(neigh, ifp); - } -} - -void -send_request(struct interface *ifp, - const unsigned char *prefix, unsigned char plen) -{ - int v4, len; - - if(ifp == NULL) { - struct interface *ifp_aux; - struct listnode *linklist_node = NULL; - FOR_ALL_INTERFACES(ifp_aux, linklist_node) { - if(if_up(ifp_aux)) - continue; - send_request(ifp_aux, prefix, plen); - } - return; - } - - /* make sure any buffered updates go out before this request. */ - flushupdates(ifp); - - if(!if_up(ifp)) - return; - - debugf(BABEL_DEBUG_COMMON,"sending request to %s for %s.", - ifp->name, prefix ? format_prefix(prefix, plen) : "any"); - v4 = plen >= 96 && v4mapped(prefix); - len = !prefix ? 2 : v4 ? 6 : 18; - - start_message(ifp, MESSAGE_REQUEST, len); - accumulate_byte(ifp, !prefix ? 0 : v4 ? 1 : 2); - accumulate_byte(ifp, !prefix ? 0 : v4 ? plen - 96 : plen); - if(prefix) { - if(v4) - accumulate_bytes(ifp, prefix + 12, 4); - else - accumulate_bytes(ifp, prefix, 16); - } - end_message(ifp, MESSAGE_REQUEST, len); -} - -void -send_unicast_request(struct neighbour *neigh, - const unsigned char *prefix, unsigned char plen) -{ - int rc, v4, len; - - /* make sure any buffered updates go out before this request. */ - flushupdates(neigh->ifp); - - debugf(BABEL_DEBUG_COMMON,"sending unicast request to %s for %s.", - format_address(neigh->address), - prefix ? format_prefix(prefix, plen) : "any"); - v4 = plen >= 96 && v4mapped(prefix); - len = !prefix ? 2 : v4 ? 6 : 18; - - rc = start_unicast_message(neigh, MESSAGE_REQUEST, len); - if(rc < 0) return; - accumulate_unicast_byte(neigh, !prefix ? 0 : v4 ? 1 : 2); - accumulate_unicast_byte(neigh, !prefix ? 0 : v4 ? plen - 96 : plen); - if(prefix) { - if(v4) - accumulate_unicast_bytes(neigh, prefix + 12, 4); - else - accumulate_unicast_bytes(neigh, prefix, 16); - } - end_unicast_message(neigh, MESSAGE_REQUEST, len); -} - -void -send_multihop_request(struct interface *ifp, - const unsigned char *prefix, unsigned char plen, - unsigned short seqno, const unsigned char *id, - unsigned short hop_count) -{ - int v4, pb, len; - - /* Make sure any buffered updates go out before this request. */ - flushupdates(ifp); - - if(ifp == NULL) { - struct interface *ifp_aux; - struct listnode *linklist_node = NULL; - FOR_ALL_INTERFACES(ifp_aux, linklist_node) { - if(!if_up(ifp_aux)) - continue; - send_multihop_request(ifp_aux, prefix, plen, seqno, id, hop_count); - } - return; - } - - if(!if_up(ifp)) - return; - - debugf(BABEL_DEBUG_COMMON,"Sending request (%d) on %s for %s.", - hop_count, ifp->name, format_prefix(prefix, plen)); - v4 = plen >= 96 && v4mapped(prefix); - pb = v4 ? ((plen - 96) + 7) / 8 : (plen + 7) / 8; - len = 6 + 8 + pb; - - start_message(ifp, MESSAGE_MH_REQUEST, len); - accumulate_byte(ifp, v4 ? 1 : 2); - accumulate_byte(ifp, v4 ? plen - 96 : plen); - accumulate_short(ifp, seqno); - accumulate_byte(ifp, hop_count); - accumulate_byte(ifp, 0); - accumulate_bytes(ifp, id, 8); - if(prefix) { - if(v4) - accumulate_bytes(ifp, prefix + 12, pb); - else - accumulate_bytes(ifp, prefix, pb); - } - end_message(ifp, MESSAGE_MH_REQUEST, len); -} - -void -send_unicast_multihop_request(struct neighbour *neigh, - const unsigned char *prefix, unsigned char plen, - unsigned short seqno, const unsigned char *id, - unsigned short hop_count) -{ - int rc, v4, pb, len; - - /* Make sure any buffered updates go out before this request. */ - flushupdates(neigh->ifp); - - debugf(BABEL_DEBUG_COMMON,"Sending multi-hop request to %s for %s (%d hops).", - format_address(neigh->address), - format_prefix(prefix, plen), hop_count); - v4 = plen >= 96 && v4mapped(prefix); - pb = v4 ? ((plen - 96) + 7) / 8 : (plen + 7) / 8; - len = 6 + 8 + pb; - - rc = start_unicast_message(neigh, MESSAGE_MH_REQUEST, len); - if(rc < 0) return; - accumulate_unicast_byte(neigh, v4 ? 1 : 2); - accumulate_unicast_byte(neigh, v4 ? plen - 96 : plen); - accumulate_unicast_short(neigh, seqno); - accumulate_unicast_byte(neigh, hop_count); - accumulate_unicast_byte(neigh, 0); - accumulate_unicast_bytes(neigh, id, 8); - if(prefix) { - if(v4) - accumulate_unicast_bytes(neigh, prefix + 12, pb); - else - accumulate_unicast_bytes(neigh, prefix, pb); - } - end_unicast_message(neigh, MESSAGE_MH_REQUEST, len); -} - -void -send_request_resend(struct neighbour *neigh, - const unsigned char *prefix, unsigned char plen, - unsigned short seqno, unsigned char *id) -{ - if(neigh) - send_unicast_multihop_request(neigh, prefix, plen, seqno, id, 127); - else - send_multihop_request(NULL, prefix, plen, seqno, id, 127); - - record_resend(RESEND_REQUEST, prefix, plen, seqno, id, - neigh ? neigh->ifp : NULL, resend_delay); -} - -void -handle_request(struct neighbour *neigh, const unsigned char *prefix, - unsigned char plen, unsigned char hop_count, - unsigned short seqno, const unsigned char *id) -{ - struct xroute *xroute; - struct babel_route *route; - struct neighbour *successor = NULL; - - xroute = find_xroute(prefix, plen); - route = find_installed_route(prefix, plen); - - if(xroute && (!route || xroute->metric <= kernel_metric)) { - if(hop_count > 0 && memcmp(id, myid, 8) == 0) { - if(seqno_compare(seqno, myseqno) > 0) { - if(seqno_minus(seqno, myseqno) > 100) { - /* Hopelessly out-of-date request */ - return; - } - update_myseqno(); - } - } - send_update(neigh->ifp, 1, prefix, plen); - return; - } - - if(route && - (memcmp(id, route->src->id, 8) != 0 || - seqno_compare(seqno, route->seqno) <= 0)) { - send_update(neigh->ifp, 1, prefix, plen); - return; - } - - if(hop_count <= 1) - return; - - if(route && memcmp(id, route->src->id, 8) == 0 && - seqno_minus(seqno, route->seqno) > 100) { - /* Hopelessly out-of-date */ - return; - } - - if(request_redundant(neigh->ifp, prefix, plen, seqno, id)) - return; - - /* Let's try to forward this request. */ - if(route && route_metric(route) < INFINITY) - successor = route->neigh; - - if(!successor || successor == neigh) { - /* We were about to forward a request to its requestor. Try to - find a different neighbour to forward the request to. */ - struct babel_route *other_route; - - other_route = find_best_route(prefix, plen, 0, neigh); - if(other_route && route_metric(other_route) < INFINITY) - successor = other_route->neigh; - } - - if(!successor || successor == neigh) - /* Give up */ - return; - - send_unicast_multihop_request(successor, prefix, plen, seqno, id, - hop_count - 1); - record_resend(RESEND_REQUEST, prefix, plen, seqno, id, - neigh->ifp, 0); -} diff --git a/babeld/message.h b/babeld/message.h deleted file mode 100644 index 6a9aa1046..000000000 --- a/babeld/message.h +++ /dev/null @@ -1,111 +0,0 @@ -/* - * This file is free software: you may copy, redistribute 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 file 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, see . - * - * This file incorporates work covered by the following copyright and - * permission notice: - * -Copyright (c) 2007, 2008 by Juliusz Chroboczek - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -*/ - -#ifndef BABEL_MESSAGE_H -#define BABEL_MESSAGE_H - -#include "babel_interface.h" - -#define MAX_BUFFERED_UPDATES 200 - -#define BUCKET_TOKENS_MAX 200 -#define BUCKET_TOKENS_PER_SEC 40 - -#define MESSAGE_PAD1 0 -#define MESSAGE_PADN 1 -#define MESSAGE_ACK_REQ 2 -#define MESSAGE_ACK 3 -#define MESSAGE_HELLO 4 -#define MESSAGE_IHU 5 -#define MESSAGE_ROUTER_ID 6 -#define MESSAGE_NH 7 -#define MESSAGE_UPDATE 8 -#define MESSAGE_REQUEST 9 -#define MESSAGE_MH_REQUEST 10 - - -extern unsigned short myseqno; -extern struct timeval seqno_time; - -extern int broadcast_ihu; -extern int split_horizon; - -extern unsigned char packet_header[4]; - -extern struct neighbour *unicast_neighbour; -extern struct timeval unicast_flush_timeout; - -void parse_packet(const unsigned char *from, struct interface *ifp, - const unsigned char *packet, int packetlen); -void flushbuf(struct interface *ifp); -void flushupdates(struct interface *ifp); -void send_ack(struct neighbour *neigh, unsigned short nonce, - unsigned short interval); -void send_hello_noupdate(struct interface *ifp, unsigned interval); -void send_hello(struct interface *ifp); -void flush_unicast(int dofree); -void send_update(struct interface *ifp, int urgent, - const unsigned char *prefix, unsigned char plen); -void send_update_resend(struct interface *ifp, - const unsigned char *prefix, unsigned char plen); -void send_wildcard_retraction(struct interface *ifp); -void update_myseqno(void); -void send_self_update(struct interface *ifp); -void send_ihu(struct neighbour *neigh, struct interface *ifp); -void send_marginal_ihu(struct interface *ifp); -void send_request(struct interface *ifp, - const unsigned char *prefix, unsigned char plen); -void send_unicast_request(struct neighbour *neigh, - const unsigned char *prefix, unsigned char plen); -void send_multihop_request(struct interface *ifp, - const unsigned char *prefix, unsigned char plen, - unsigned short seqno, const unsigned char *id, - unsigned short hop_count); -void -send_unicast_multihop_request(struct neighbour *neigh, - const unsigned char *prefix, unsigned char plen, - unsigned short seqno, const unsigned char *id, - unsigned short hop_count); -void send_request_resend(struct neighbour *neigh, - const unsigned char *prefix, unsigned char plen, - unsigned short seqno, unsigned char *id); -void handle_request(struct neighbour *neigh, const unsigned char *prefix, - unsigned char plen, unsigned char hop_count, - unsigned short seqno, const unsigned char *id); - -#endif diff --git a/babeld/neighbour.c b/babeld/neighbour.c deleted file mode 100644 index 5a327dfe9..000000000 --- a/babeld/neighbour.c +++ /dev/null @@ -1,343 +0,0 @@ -/* - * This file is free software: you may copy, redistribute 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 file 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, see . - * - * This file incorporates work covered by the following copyright and - * permission notice: - * -Copyright (c) 2007, 2008 by Juliusz Chroboczek - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -*/ - -#include -#include -#include -#include -#include - -#include -#include "if.h" - -#include "babel_main.h" -#include "babeld.h" -#include "util.h" -#include "babel_interface.h" -#include "neighbour.h" -#include "source.h" -#include "route.h" -#include "message.h" -#include "resend.h" - -struct neighbour *neighs = NULL; - -static struct neighbour * -find_neighbour_nocreate(const unsigned char *address, struct interface *ifp) -{ - struct neighbour *neigh; - FOR_ALL_NEIGHBOURS(neigh) { - if(memcmp(address, neigh->address, 16) == 0 && - neigh->ifp == ifp) - return neigh; - } - return NULL; -} - -void -flush_neighbour(struct neighbour *neigh) -{ - flush_neighbour_routes(neigh); - if(unicast_neighbour == neigh) - flush_unicast(1); - flush_resends(neigh); - - if(neighs == neigh) { - neighs = neigh->next; - } else { - struct neighbour *previous = neighs; - while(previous->next != neigh) - previous = previous->next; - previous->next = neigh->next; - } - free(neigh); -} - -struct neighbour * -find_neighbour(const unsigned char *address, struct interface *ifp) -{ - struct neighbour *neigh; - const struct timeval zero = {0, 0}; - - neigh = find_neighbour_nocreate(address, ifp); - if(neigh) - return neigh; - - debugf(BABEL_DEBUG_COMMON,"Creating neighbour %s on %s.", - format_address(address), ifp->name); - - neigh = malloc(sizeof(struct neighbour)); - if(neigh == NULL) { - zlog_err("malloc(neighbour): %s", safe_strerror(errno)); - return NULL; - } - - neigh->hello_seqno = -1; - memcpy(neigh->address, address, 16); - neigh->reach = 0; - neigh->txcost = INFINITY; - neigh->ihu_time = babel_now; - neigh->hello_time = zero; - neigh->hello_interval = 0; - neigh->ihu_interval = 0; - neigh->ifp = ifp; - neigh->next = neighs; - neighs = neigh; - send_hello(ifp); - return neigh; -} - -/* Recompute a neighbour's rxcost. Return true if anything changed. - This does not call local_notify_neighbour, see update_neighbour_metric. */ -int -update_neighbour(struct neighbour *neigh, int hello, int hello_interval) -{ - int missed_hellos; - int rc = 0; - - if(hello < 0) { - if(neigh->hello_interval <= 0) - return rc; - missed_hellos = - ((int)timeval_minus_msec(&babel_now, &neigh->hello_time) - - neigh->hello_interval * 7) / - (neigh->hello_interval * 10); - if(missed_hellos <= 0) - return rc; - timeval_add_msec(&neigh->hello_time, &neigh->hello_time, - missed_hellos * neigh->hello_interval * 10); - } else { - if(neigh->hello_seqno >= 0 && neigh->reach > 0) { - missed_hellos = seqno_minus(hello, neigh->hello_seqno) - 1; - if(missed_hellos < -8) { - /* Probably a neighbour that rebooted and lost its seqno. - Reboot the universe. */ - neigh->reach = 0; - missed_hellos = 0; - rc = 1; - } else if(missed_hellos < 0) { - if(hello_interval > neigh->hello_interval) { - /* This neighbour has increased its hello interval, - and we didn't notice. */ - neigh->reach <<= -missed_hellos; - missed_hellos = 0; - } else { - /* Late hello. Probably due to the link layer buffering - packets during a link outage. Ignore it, but reset - the expected seqno. */ - neigh->hello_seqno = hello; - hello = -1; - missed_hellos = 0; - } - rc = 1; - } - } else { - missed_hellos = 0; - } - neigh->hello_time = babel_now; - neigh->hello_interval = hello_interval; - } - - if(missed_hellos > 0) { - neigh->reach >>= missed_hellos; - neigh->hello_seqno = seqno_plus(neigh->hello_seqno, missed_hellos); - missed_hellos = 0; - rc = 1; - } - - if(hello >= 0) { - neigh->hello_seqno = hello; - neigh->reach >>= 1; - neigh->reach |= 0x8000; - if((neigh->reach & 0xFC00) != 0xFC00) - rc = 1; - } - - /* Make sure to give neighbours some feedback early after association */ - if((neigh->reach & 0xBF00) == 0x8000) { - /* A new neighbour */ - send_hello(neigh->ifp); - } else { - /* Don't send hellos, in order to avoid a positive feedback loop. */ - int a = (neigh->reach & 0xC000); - int b = (neigh->reach & 0x3000); - if((a == 0xC000 && b == 0) || (a == 0 && b == 0x3000)) { - /* Reachability is either 1100 or 0011 */ - send_self_update(neigh->ifp); - } - } - - if((neigh->reach & 0xFC00) == 0xC000) { - /* This is a newish neighbour, let's request a full route dump. - We ought to avoid this when the network is dense */ - send_unicast_request(neigh, NULL, 0); - send_ihu(neigh, NULL); - } - return rc; -} - -static int -reset_txcost(struct neighbour *neigh) -{ - unsigned delay; - - delay = timeval_minus_msec(&babel_now, &neigh->ihu_time); - - if(neigh->ihu_interval > 0 && delay < neigh->ihu_interval * 10U * 3U) - return 0; - - /* If we're losing a lot of packets, we probably lost an IHU too */ - if(delay >= 180000 || (neigh->reach & 0xFFF0) == 0 || - (neigh->ihu_interval > 0 && - delay >= neigh->ihu_interval * 10U * 10U)) { - neigh->txcost = INFINITY; - neigh->ihu_time = babel_now; - return 1; - } - - return 0; -} - -unsigned -neighbour_txcost(struct neighbour *neigh) -{ - return neigh->txcost; -} - -unsigned -check_neighbours() -{ - struct neighbour *neigh; - int changed, rc; - unsigned msecs = 50000; - - debugf(BABEL_DEBUG_COMMON,"Checking neighbours."); - - neigh = neighs; - while(neigh) { - changed = update_neighbour(neigh, -1, 0); - - if(neigh->reach == 0 || - neigh->hello_time.tv_sec > babel_now.tv_sec || /* clock stepped */ - timeval_minus_msec(&babel_now, &neigh->hello_time) > 300000) { - struct neighbour *old = neigh; - neigh = neigh->next; - flush_neighbour(old); - continue; - } - - rc = reset_txcost(neigh); - changed = changed || rc; - - update_neighbour_metric(neigh, changed); - - if(neigh->hello_interval > 0) - msecs = MIN(msecs, neigh->hello_interval * 10U); - if(neigh->ihu_interval > 0) - msecs = MIN(msecs, neigh->ihu_interval * 10U); - neigh = neigh->next; - } - - return msecs; -} - -unsigned -neighbour_rxcost(struct neighbour *neigh) -{ - unsigned delay; - unsigned short reach = neigh->reach; - - delay = timeval_minus_msec(&babel_now, &neigh->hello_time); - - if((reach & 0xFFF0) == 0 || delay >= 180000) { - return INFINITY; - } else if(babel_get_if_nfo(neigh->ifp)->flags & BABEL_IF_LQ) { - int sreach = - ((reach & 0x8000) >> 2) + - ((reach & 0x4000) >> 1) + - (reach & 0x3FFF); - /* 0 <= sreach <= 0x7FFF */ - int cost = (0x8000 * babel_get_if_nfo(neigh->ifp)->cost) / (sreach + 1); - /* cost >= interface->cost */ - if(delay >= 40000) - cost = (cost * (delay - 20000) + 10000) / 20000; - return MIN(cost, INFINITY); - } else { - /* To lose one hello is a misfortune, to lose two is carelessness. */ - if((reach & 0xC000) == 0xC000) - return babel_get_if_nfo(neigh->ifp)->cost; - else if((reach & 0xC000) == 0) - return INFINITY; - else if((reach & 0x2000)) - return babel_get_if_nfo(neigh->ifp)->cost; - else - return INFINITY; - } -} - -unsigned -neighbour_cost(struct neighbour *neigh) -{ - unsigned a, b; - - if(!if_up(neigh->ifp)) - return INFINITY; - - a = neighbour_txcost(neigh); - - if(a >= INFINITY) - return INFINITY; - - b = neighbour_rxcost(neigh); - if(b >= INFINITY) - return INFINITY; - - if(!(babel_get_if_nfo(neigh->ifp)->flags & BABEL_IF_LQ) - || (a < 256 && b < 256)) { - return a; - } else { - /* a = 256/alpha, b = 256/beta, where alpha and beta are the expected - probabilities of a packet getting through in the direct and reverse - directions. */ - a = MAX(a, 256); - b = MAX(b, 256); - /* 1/(alpha * beta), which is just plain ETX. */ - /* Since a and b are capped to 16 bits, overflow is impossible. */ - return (a * b + 128) >> 8; - } -} diff --git a/babeld/neighbour.h b/babeld/neighbour.h deleted file mode 100644 index cf8c0f0b2..000000000 --- a/babeld/neighbour.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - * This file is free software: you may copy, redistribute 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 file 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, see . - * - * This file incorporates work covered by the following copyright and - * permission notice: - * -Copyright (c) 2007, 2008 by Juliusz Chroboczek - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -*/ - -struct neighbour { - struct neighbour *next; - /* This is -1 when unknown, so don't make it unsigned */ - int hello_seqno; - unsigned char address[16]; - unsigned short reach; - unsigned short txcost; - struct timeval hello_time; - struct timeval ihu_time; - unsigned short hello_interval; /* in centiseconds */ - unsigned short ihu_interval; /* in centiseconds */ - struct interface *ifp; -}; - -extern struct neighbour *neighs; - -#define FOR_ALL_NEIGHBOURS(_neigh) \ - for(_neigh = neighs; _neigh; _neigh = _neigh->next) - -int neighbour_valid(struct neighbour *neigh); -void flush_neighbour(struct neighbour *neigh); -struct neighbour *find_neighbour(const unsigned char *address, - struct interface *ifp); -int update_neighbour(struct neighbour *neigh, int hello, int hello_interval); -unsigned check_neighbours(void); -unsigned neighbour_txcost(struct neighbour *neigh); -unsigned neighbour_rxcost(struct neighbour *neigh); -unsigned neighbour_cost(struct neighbour *neigh); diff --git a/babeld/net.c b/babeld/net.c deleted file mode 100644 index 5e0200b0a..000000000 --- a/babeld/net.c +++ /dev/null @@ -1,239 +0,0 @@ -/* - * This file is free software: you may copy, redistribute 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 file 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, see . - * - * This file incorporates work covered by the following copyright and - * permission notice: - * -Copyright (c) 2007, 2008 by Juliusz Chroboczek - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -*/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "babeld.h" -#include "util.h" -#include "net.h" - -int -babel_socket(int port) -{ - struct sockaddr_in6 sin6; - int s, rc; - int saved_errno; - int one = 1, zero = 0; - const int ds = 0xc0; /* CS6 - Network Control */ - - s = socket(PF_INET6, SOCK_DGRAM, 0); - if(s < 0) - return -1; - - rc = setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &one, sizeof(one)); - if(rc < 0) - goto fail; - - rc = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); - if(rc < 0) - goto fail; - - rc = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, - &zero, sizeof(zero)); - if(rc < 0) - goto fail; - - rc = setsockopt(s, IPPROTO_IPV6, IPV6_UNICAST_HOPS, - &one, sizeof(one)); - if(rc < 0) - goto fail; - - rc = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, - &one, sizeof(one)); - if(rc < 0) - goto fail; - -#ifdef IPV6_TCLASS - rc = setsockopt(s, IPPROTO_IPV6, IPV6_TCLASS, &ds, sizeof(ds)); -#else - rc = -1; - errno = ENOSYS; -#endif - if(rc < 0) - perror("Couldn't set traffic class"); - - rc = fcntl(s, F_GETFL, 0); - if(rc < 0) - goto fail; - - rc = fcntl(s, F_SETFL, (rc | O_NONBLOCK)); - if(rc < 0) - goto fail; - - rc = fcntl(s, F_GETFD, 0); - if(rc < 0) - goto fail; - - rc = fcntl(s, F_SETFD, rc | FD_CLOEXEC); - if(rc < 0) - goto fail; - - memset(&sin6, 0, sizeof(sin6)); - sin6.sin6_family = AF_INET6; - sin6.sin6_port = htons(port); - rc = bind(s, (struct sockaddr*)&sin6, sizeof(sin6)); - if(rc < 0) - goto fail; - - return s; - - fail: - saved_errno = errno; - close(s); - errno = saved_errno; - return -1; -} - -int -babel_recv(int s, void *buf, int buflen, struct sockaddr *sin, int slen) -{ - struct iovec iovec; - struct msghdr msg; - int rc; - - memset(&msg, 0, sizeof(msg)); - iovec.iov_base = buf; - iovec.iov_len = buflen; - msg.msg_name = sin; - msg.msg_namelen = slen; - msg.msg_iov = &iovec; - msg.msg_iovlen = 1; - - rc = recvmsg(s, &msg, 0); - return rc; -} - -int -babel_send(int s, - void *buf1, int buflen1, void *buf2, int buflen2, - struct sockaddr *sin, int slen) -{ - struct iovec iovec[2]; - struct msghdr msg; - int rc; - - iovec[0].iov_base = buf1; - iovec[0].iov_len = buflen1; - iovec[1].iov_base = buf2; - iovec[1].iov_len = buflen2; - memset(&msg, 0, sizeof(msg)); - msg.msg_name = (struct sockaddr*)sin; - msg.msg_namelen = slen; - msg.msg_iov = iovec; - msg.msg_iovlen = 2; - - again: - rc = sendmsg(s, &msg, 0); - if(rc < 0) { - if(errno == EINTR) - goto again; - else if(errno == EAGAIN) { - int rc2; - rc2 = wait_for_fd(1, s, 5); - if(rc2 > 0) - goto again; - errno = EAGAIN; - } - } - return rc; -} - -int -tcp_server_socket(int port, int local) -{ - struct sockaddr_in6 sin6; - int s, rc, saved_errno; - int one = 1; - - s = socket(PF_INET6, SOCK_STREAM, 0); - if(s < 0) - return -1; - - rc = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); - if(rc < 0) - goto fail; - - rc = fcntl(s, F_GETFL, 0); - if(rc < 0) - goto fail; - - rc = fcntl(s, F_SETFL, (rc | O_NONBLOCK)); - if(rc < 0) - goto fail; - - rc = fcntl(s, F_GETFD, 0); - if(rc < 0) - goto fail; - - rc = fcntl(s, F_SETFD, rc | FD_CLOEXEC); - if(rc < 0) - goto fail; - - memset(&sin6, 0, sizeof(sin6)); - sin6.sin6_family = AF_INET6; - sin6.sin6_port = htons(port); - if(local) { - rc = inet_pton(AF_INET6, "::1", &sin6.sin6_addr); - if(rc < 0) - goto fail; - } - rc = bind(s, (struct sockaddr*)&sin6, sizeof(sin6)); - if(rc < 0) - goto fail; - - rc = listen(s, 2); - if(rc < 0) - goto fail; - - return s; - - fail: - saved_errno = errno; - close(s); - errno = saved_errno; - return -1; -} diff --git a/babeld/net.h b/babeld/net.h deleted file mode 100644 index 4bd0f04f9..000000000 --- a/babeld/net.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * This file is free software: you may copy, redistribute 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 file 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, see . - * - * This file incorporates work covered by the following copyright and - * permission notice: - * -Copyright (c) 2007, 2008 by Juliusz Chroboczek - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -*/ - -int babel_socket(int port); -int babel_recv(int s, void *buf, int buflen, struct sockaddr *sin, int slen); -int babel_send(int s, - void *buf1, int buflen1, void *buf2, int buflen2, - struct sockaddr *sin, int slen); -int tcp_server_socket(int port, int local); diff --git a/babeld/resend.c b/babeld/resend.c deleted file mode 100644 index 1cc6290e7..000000000 --- a/babeld/resend.c +++ /dev/null @@ -1,330 +0,0 @@ -/* - * This file is free software: you may copy, redistribute 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 file 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, see . - * - * This file incorporates work covered by the following copyright and - * permission notice: - * -Copyright (c) 2007, 2008 by Juliusz Chroboczek - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -*/ - -#include -#include -#include -#include - -#include -#include "if.h" - -#include "babel_main.h" -#include "babeld.h" -#include "util.h" -#include "neighbour.h" -#include "resend.h" -#include "message.h" -#include "babel_interface.h" - -struct timeval resend_time = {0, 0}; -struct resend *to_resend = NULL; - -static int -resend_match(struct resend *resend, - int kind, const unsigned char *prefix, unsigned char plen) -{ - return (resend->kind == kind && - resend->plen == plen && memcmp(resend->prefix, prefix, 16) == 0); -} - -/* This is called by neigh.c when a neighbour is flushed */ - -void -flush_resends(struct neighbour *neigh) -{ - /* Nothing for now */ -} - -static struct resend * -find_resend(int kind, const unsigned char *prefix, unsigned char plen, - struct resend **previous_return) -{ - struct resend *current, *previous; - - previous = NULL; - current = to_resend; - while(current) { - if(resend_match(current, kind, prefix, plen)) { - if(previous_return) - *previous_return = previous; - return current; - } - previous = current; - current = current->next; - } - - return NULL; -} - -struct resend * -find_request(const unsigned char *prefix, unsigned char plen, - struct resend **previous_return) -{ - return find_resend(RESEND_REQUEST, prefix, plen, previous_return); -} - -int -record_resend(int kind, const unsigned char *prefix, unsigned char plen, - unsigned short seqno, const unsigned char *id, - struct interface *ifp, int delay) -{ - struct resend *resend; - unsigned int ifindex = ifp ? ifp->ifindex : 0; - - if((kind == RESEND_REQUEST && - input_filter(NULL, prefix, plen, NULL, ifindex) >= INFINITY) || - (kind == RESEND_UPDATE && - output_filter(NULL, prefix, plen, ifindex) >= INFINITY)) - return 0; - - if(delay >= 0xFFFF) - delay = 0xFFFF; - - resend = find_resend(kind, prefix, plen, NULL); - if(resend) { - if(resend->delay && delay) - resend->delay = MIN(resend->delay, delay); - else if(delay) - resend->delay = delay; - resend->time = babel_now; - resend->max = RESEND_MAX; - if(id && memcmp(resend->id, id, 8) == 0 && - seqno_compare(resend->seqno, seqno) > 0) { - return 0; - } - if(id) - memcpy(resend->id, id, 8); - else - memset(resend->id, 0, 8); - resend->seqno = seqno; - if(resend->ifp != ifp) - resend->ifp = NULL; - } else { - resend = malloc(sizeof(struct resend)); - if(resend == NULL) - return -1; - resend->kind = kind; - resend->max = RESEND_MAX; - resend->delay = delay; - memcpy(resend->prefix, prefix, 16); - resend->plen = plen; - resend->seqno = seqno; - if(id) - memcpy(resend->id, id, 8); - else - memset(resend->id, 0, 8); - resend->ifp = ifp; - resend->time = babel_now; - resend->next = to_resend; - to_resend = resend; - } - - if(resend->delay) { - struct timeval timeout; - timeval_add_msec(&timeout, &resend->time, resend->delay); - timeval_min(&resend_time, &timeout); - } - return 1; -} - -static int -resend_expired(struct resend *resend) -{ - switch(resend->kind) { - case RESEND_REQUEST: - return timeval_minus_msec(&babel_now, &resend->time) >= REQUEST_TIMEOUT; - default: - return resend->max <= 0; - } -} - -int -unsatisfied_request(const unsigned char *prefix, unsigned char plen, - unsigned short seqno, const unsigned char *id) -{ - struct resend *request; - - request = find_request(prefix, plen, NULL); - if(request == NULL || resend_expired(request)) - return 0; - - if(memcmp(request->id, id, 8) != 0 || - seqno_compare(request->seqno, seqno) <= 0) - return 1; - - return 0; -} - -/* Determine whether a given request should be forwarded. */ -int -request_redundant(struct interface *ifp, - const unsigned char *prefix, unsigned char plen, - unsigned short seqno, const unsigned char *id) -{ - struct resend *request; - - request = find_request(prefix, plen, NULL); - if(request == NULL || resend_expired(request)) - return 0; - - if(memcmp(request->id, id, 8) == 0 && - seqno_compare(request->seqno, seqno) > 0) - return 0; - - if(request->ifp != NULL && request->ifp != ifp) - return 0; - - if(request->max > 0) - /* Will be resent. */ - return 1; - - if(timeval_minus_msec(&babel_now, &request->time) < - (ifp ? MIN(babel_get_if_nfo(ifp)->hello_interval, 1000) : 1000)) - /* Fairly recent. */ - return 1; - - return 0; -} - -int -satisfy_request(const unsigned char *prefix, unsigned char plen, - unsigned short seqno, const unsigned char *id, - struct interface *ifp) -{ - struct resend *request, *previous; - - request = find_request(prefix, plen, &previous); - if(request == NULL) - return 0; - - if(ifp != NULL && request->ifp != ifp) - return 0; - - if(memcmp(request->id, id, 8) != 0 || - seqno_compare(request->seqno, seqno) <= 0) { - /* We cannot remove the request, as we may be walking the list right - now. Mark it as expired, so that expire_resend will remove it. */ - request->max = 0; - request->time.tv_sec = 0; - recompute_resend_time(); - return 1; - } - - return 0; -} - -void -expire_resend() -{ - struct resend *current, *previous; - int recompute = 0; - - previous = NULL; - current = to_resend; - while(current) { - if(resend_expired(current)) { - if(previous == NULL) { - to_resend = current->next; - free(current); - current = to_resend; - } else { - previous->next = current->next; - free(current); - current = previous->next; - } - recompute = 1; - } else { - previous = current; - current = current->next; - } - } - if(recompute) - recompute_resend_time(); -} - -void -recompute_resend_time() -{ - struct resend *request; - struct timeval resend = {0, 0}; - - request = to_resend; - while(request) { - if(!resend_expired(request) && request->delay > 0 && request->max > 0) { - struct timeval timeout; - timeval_add_msec(&timeout, &request->time, request->delay); - timeval_min(&resend, &timeout); - } - request = request->next; - } - - resend_time = resend; -} - -void -do_resend() -{ - struct resend *resend; - - resend = to_resend; - while(resend) { - if(!resend_expired(resend) && resend->delay > 0 && resend->max > 0) { - struct timeval timeout; - timeval_add_msec(&timeout, &resend->time, resend->delay); - if(timeval_compare(&babel_now, &timeout) >= 0) { - switch(resend->kind) { - case RESEND_REQUEST: - send_multihop_request(resend->ifp, - resend->prefix, resend->plen, - resend->seqno, resend->id, 127); - break; - case RESEND_UPDATE: - send_update(resend->ifp, 1, - resend->prefix, resend->plen); - break; - default: abort(); - } - resend->delay = MIN(0xFFFF, resend->delay * 2); - resend->max--; - } - } - resend = resend->next; - } - recompute_resend_time(); -} diff --git a/babeld/resend.h b/babeld/resend.h deleted file mode 100644 index a6755c0eb..000000000 --- a/babeld/resend.h +++ /dev/null @@ -1,77 +0,0 @@ -/* - * This file is free software: you may copy, redistribute 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 file 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, see . - * - * This file incorporates work covered by the following copyright and - * permission notice: - * -Copyright (c) 2007, 2008 by Juliusz Chroboczek - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -*/ - -#define REQUEST_TIMEOUT 65000 -#define RESEND_MAX 3 - -#define RESEND_REQUEST 1 -#define RESEND_UPDATE 2 - -struct resend { - unsigned char kind; - unsigned char max; - unsigned short delay; - struct timeval time; - unsigned char prefix[16]; - unsigned char plen; - unsigned short seqno; - unsigned char id[8]; - struct interface *ifp; - struct resend *next; -}; - -extern struct timeval resend_time; - -struct resend *find_request(const unsigned char *prefix, unsigned char plen, - struct resend **previous_return); -void flush_resends(struct neighbour *neigh); -int record_resend(int kind, const unsigned char *prefix, unsigned char plen, - unsigned short seqno, const unsigned char *id, - struct interface *ifp, int delay); -int unsatisfied_request(const unsigned char *prefix, unsigned char plen, - unsigned short seqno, const unsigned char *id); -int request_redundant(struct interface *ifp, - const unsigned char *prefix, unsigned char plen, - unsigned short seqno, const unsigned char *id); -int satisfy_request(const unsigned char *prefix, unsigned char plen, - unsigned short seqno, const unsigned char *id, - struct interface *ifp); - -void expire_resend(void); -void recompute_resend_time(void); -void do_resend(void); diff --git a/babeld/route.c b/babeld/route.c deleted file mode 100644 index fe2b9cebb..000000000 --- a/babeld/route.c +++ /dev/null @@ -1,1019 +0,0 @@ -/* - * This file is free software: you may copy, redistribute 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 file 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, see . - * - * This file incorporates work covered by the following copyright and - * permission notice: - * -Copyright (c) 2007, 2008 by Juliusz Chroboczek -Copyright 2011 by Matthieu Boutier and Juliusz Chroboczek - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -*/ - -#include -#include "if.h" - -#include "babeld.h" -#include "util.h" -#include "kernel.h" -#include "babel_interface.h" -#include "source.h" -#include "neighbour.h" -#include "route.h" -#include "xroute.h" -#include "message.h" -#include "resend.h" - -static void consider_route(struct babel_route *route); - -struct babel_route **routes = NULL; -static int route_slots = 0, max_route_slots = 0; -int kernel_metric = 0; -int allow_duplicates = -1; -int diversity_kind = DIVERSITY_NONE; -int diversity_factor = 256; /* in units of 1/256 */ -int keep_unfeasible = 0; - -/* We maintain a list of "slots", ordered by prefix. Every slot - contains a linked list of the routes to this prefix, with the - installed route, if any, at the head of the list. */ - -static int -route_compare(const unsigned char *prefix, unsigned char plen, - struct babel_route *route) -{ - int i = memcmp(prefix, route->src->prefix, 16); - if(i != 0) - return i; - - if(plen < route->src->plen) - return -1; - else if(plen > route->src->plen) - return 1; - else - return 0; -} - -/* Performs binary search, returns -1 in case of failure. In the latter - case, new_return is the place where to insert the new element. */ - -static int -find_route_slot(const unsigned char *prefix, unsigned char plen, - int *new_return) -{ - int p, m, g, c; - - if(route_slots < 1) { - if(new_return) - *new_return = 0; - return -1; - } - - p = 0; g = route_slots - 1; - - do { - m = (p + g) / 2; - c = route_compare(prefix, plen, routes[m]); - if(c == 0) - return m; - else if(c < 0) - g = m - 1; - else - p = m + 1; - } while(p <= g); - - if(new_return) - *new_return = p; - - return -1; -} - -struct babel_route * -find_route(const unsigned char *prefix, unsigned char plen, - struct neighbour *neigh, const unsigned char *nexthop) -{ - struct babel_route *route; - int i = find_route_slot(prefix, plen, NULL); - - if(i < 0) - return NULL; - - route = routes[i]; - - while(route) { - if(route->neigh == neigh && memcmp(route->nexthop, nexthop, 16) == 0) - return route; - route = route->next; - } - - return NULL; -} - -struct babel_route * -find_installed_route(const unsigned char *prefix, unsigned char plen) -{ - int i = find_route_slot(prefix, plen, NULL); - - if(i >= 0 && routes[i]->installed) - return routes[i]; - - return NULL; -} - -/* Returns an overestimate of the number of installed routes. */ -int -installed_routes_estimate(void) -{ - return route_slots; -} - -static int -resize_route_table(int new_slots) -{ - struct babel_route **new_routes; - assert(new_slots >= route_slots); - - if(new_slots == 0) { - new_routes = NULL; - free(routes); - } else { - new_routes = realloc(routes, new_slots * sizeof(struct babel_route*)); - if(new_routes == NULL) - return -1; - } - - max_route_slots = new_slots; - routes = new_routes; - return 1; -} - -/* Insert a route into the table. If successful, retains the route. - On failure, caller must free the route. */ -static struct babel_route * -insert_route(struct babel_route *route) -{ - int i, n; - - assert(!route->installed); - - i = find_route_slot(route->src->prefix, route->src->plen, &n); - - if(i < 0) { - if(route_slots >= max_route_slots) - resize_route_table(max_route_slots < 1 ? 8 : 2 * max_route_slots); - if(route_slots >= max_route_slots) - return NULL; - route->next = NULL; - if(n < route_slots) - memmove(routes + n + 1, routes + n, - (route_slots - n) * sizeof(struct babel_route*)); - route_slots++; - routes[n] = route; - } else { - struct babel_route *r; - r = routes[i]; - while(r->next) - r = r->next; - r->next = route; - route->next = NULL; - } - - return route; -} - -void -flush_route(struct babel_route *route) -{ - int i; - struct source *src; - unsigned oldmetric; - int lost = 0; - - oldmetric = route_metric(route); - src = route->src; - - if(route->installed) { - uninstall_route(route); - lost = 1; - } - - i = find_route_slot(route->src->prefix, route->src->plen, NULL); - assert(i >= 0 && i < route_slots); - - if(route == routes[i]) { - routes[i] = route->next; - route->next = NULL; - free(route); - - if(routes[i] == NULL) { - if(i < route_slots - 1) - memmove(routes + i, routes + i + 1, - (route_slots - i - 1) * sizeof(struct babel_route*)); - routes[route_slots - 1] = NULL; - route_slots--; - } - - if(route_slots == 0) - resize_route_table(0); - else if(max_route_slots > 8 && route_slots < max_route_slots / 4) - resize_route_table(max_route_slots / 2); - } else { - struct babel_route *r = routes[i]; - while(r->next != route) - r = r->next; - r->next = route->next; - route->next = NULL; - free(route); - } - - if(lost) - route_lost(src, oldmetric); - - release_source(src); -} - -void -flush_all_routes() -{ - int i; - - /* Start from the end, to avoid shifting the table. */ - i = route_slots - 1; - while(i >= 0) { - while(i < route_slots) { - /* Uninstall first, to avoid calling route_lost. */ - if(routes[i]->installed) - uninstall_route(routes[0]); - flush_route(routes[i]); - } - i--; - } - - check_sources_released(); -} - -void -flush_neighbour_routes(struct neighbour *neigh) -{ - int i; - - i = 0; - while(i < route_slots) { - struct babel_route *r; - r = routes[i]; - while(r) { - if(r->neigh == neigh) { - flush_route(r); - goto again; - } - r = r->next; - } - i++; - again: - ; - } -} - -void -flush_interface_routes(struct interface *ifp, int v4only) -{ - int i; - - i = 0; - while(i < route_slots) { - struct babel_route *r; - r = routes[i]; - while(r) { - if(r->neigh->ifp == ifp && - (!v4only || v4mapped(r->nexthop))) { - flush_route(r); - goto again; - } - r = r->next; - } - i++; - again: - ; - } -} - -/* Iterate a function over all routes. */ -void -for_all_routes(void (*f)(struct babel_route*, void*), void *closure) -{ - int i; - - for(i = 0; i < route_slots; i++) { - struct babel_route *r = routes[i]; - while(r) { - (*f)(r, closure); - r = r->next; - } - } -} - -void -for_all_installed_routes(void (*f)(struct babel_route*, void*), void *closure) -{ - int i; - - for(i = 0; i < route_slots; i++) { - if(routes[i]->installed) - (*f)(routes[i], closure); - } -} - -static int -metric_to_kernel(int metric) -{ - return metric < INFINITY ? kernel_metric : KERNEL_INFINITY; -} - -/* This is used to maintain the invariant that the installed route is at - the head of the list. */ -static void -move_installed_route(struct babel_route *route, int i) -{ - assert(i >= 0 && i < route_slots); - assert(route->installed); - - if(route != routes[i]) { - struct babel_route *r = routes[i]; - while(r->next != route) - r = r->next; - r->next = route->next; - route->next = routes[i]; - routes[i] = route; - } -} - -void -install_route(struct babel_route *route) -{ - int i, rc; - - if(route->installed) - return; - - if(!route_feasible(route)) - zlog_err("WARNING: installing unfeasible route " - "(this shouldn't happen)."); - - i = find_route_slot(route->src->prefix, route->src->plen, NULL); - assert(i >= 0 && i < route_slots); - - if(routes[i] != route && routes[i]->installed) { - fprintf(stderr, "WARNING: attempting to install duplicate route " - "(this shouldn't happen)."); - return; - } - - rc = kernel_route(ROUTE_ADD, route->src->prefix, route->src->plen, - route->nexthop, - route->neigh->ifp->ifindex, - metric_to_kernel(route_metric(route)), NULL, 0, 0); - if(rc < 0) { - int save = errno; - zlog_err("kernel_route(ADD): %s", safe_strerror(errno)); - if(save != EEXIST) - return; - } - route->installed = 1; - move_installed_route(route, i); - -} - -void -uninstall_route(struct babel_route *route) -{ - int rc; - - if(!route->installed) - return; - - rc = kernel_route(ROUTE_FLUSH, route->src->prefix, route->src->plen, - route->nexthop, - route->neigh->ifp->ifindex, - metric_to_kernel(route_metric(route)), NULL, 0, 0); - if(rc < 0) - zlog_err("kernel_route(FLUSH): %s", safe_strerror(errno)); - - route->installed = 0; -} - -/* This is equivalent to uninstall_route followed with install_route, - but without the race condition. The destination of both routes - must be the same. */ - -static void -switch_routes(struct babel_route *old, struct babel_route *new) -{ - int rc; - - if(!old) { - install_route(new); - return; - } - - if(!old->installed) - return; - - if(!route_feasible(new)) - zlog_err("WARNING: switching to unfeasible route " - "(this shouldn't happen)."); - - rc = kernel_route(ROUTE_MODIFY, old->src->prefix, old->src->plen, - old->nexthop, old->neigh->ifp->ifindex, - metric_to_kernel(route_metric(old)), - new->nexthop, new->neigh->ifp->ifindex, - metric_to_kernel(route_metric(new))); - if(rc < 0) { - zlog_err("kernel_route(MODIFY): %s", safe_strerror(errno)); - return; - } - - old->installed = 0; - new->installed = 1; - move_installed_route(new, find_route_slot(new->src->prefix, new->src->plen, - NULL)); -} - -static void -change_route_metric(struct babel_route *route, - unsigned refmetric, unsigned cost, unsigned add) -{ - int old, new; - int newmetric = MIN(refmetric + cost + add, INFINITY); - - old = metric_to_kernel(route_metric(route)); - new = metric_to_kernel(newmetric); - - if(route->installed && old != new) { - int rc; - rc = kernel_route(ROUTE_MODIFY, route->src->prefix, route->src->plen, - route->nexthop, route->neigh->ifp->ifindex, - old, - route->nexthop, route->neigh->ifp->ifindex, - new); - if(rc < 0) { - zlog_err("kernel_route(MODIFY metric): %s", safe_strerror(errno)); - return; - } - } - - route->refmetric = refmetric; - route->cost = cost; - route->add_metric = add; -} - -static void -retract_route(struct babel_route *route) -{ - change_route_metric(route, INFINITY, INFINITY, 0); -} - -int -route_feasible(struct babel_route *route) -{ - return update_feasible(route->src, route->seqno, route->refmetric); -} - -int -route_old(struct babel_route *route) -{ - return route->time < babel_now.tv_sec - route->hold_time * 7 / 8; -} - -int -route_expired(struct babel_route *route) -{ - return route->time < babel_now.tv_sec - route->hold_time; -} - -static int -channels_interfere(int ch1, int ch2) -{ - if(ch1 == BABEL_IF_CHANNEL_NONINTERFERING - || ch2 == BABEL_IF_CHANNEL_NONINTERFERING) - return 0; - if(ch1 == BABEL_IF_CHANNEL_INTERFERING - || ch2 == BABEL_IF_CHANNEL_INTERFERING) - return 1; - return ch1 == ch2; -} - -int -route_interferes(struct babel_route *route, struct interface *ifp) -{ - struct babel_interface *babel_ifp = NULL; - switch(diversity_kind) { - case DIVERSITY_NONE: - return 1; - case DIVERSITY_INTERFACE_1: - return route->neigh->ifp == ifp; - case DIVERSITY_CHANNEL_1: - case DIVERSITY_CHANNEL: - if(route->neigh->ifp == ifp) - return 1; - babel_ifp = babel_get_if_nfo(ifp); - if(channels_interfere(babel_ifp->channel, - babel_get_if_nfo(route->neigh->ifp)->channel)) - return 1; - if(diversity_kind == DIVERSITY_CHANNEL) { - int i; - for(i = 0; i < DIVERSITY_HOPS; i++) { - if(route->channels[i] == 0) - break; - if(channels_interfere(babel_ifp->channel, route->channels[i])) - return 1; - } - } - return 0; - default: - fprintf(stderr, "Unknown kind of diversity.\n"); - return 1; - } -} - -int -update_feasible(struct source *src, - unsigned short seqno, unsigned short refmetric) -{ - if(src == NULL) - return 1; - - if(src->time < babel_now.tv_sec - SOURCE_GC_TIME) - /* Never mind what is probably stale data */ - return 1; - - if(refmetric >= INFINITY) - /* Retractions are always feasible */ - return 1; - - return (seqno_compare(seqno, src->seqno) > 0 || - (src->seqno == seqno && refmetric < src->metric)); -} - -/* This returns the feasible route with the smallest metric. */ -struct babel_route * -find_best_route(const unsigned char *prefix, unsigned char plen, int feasible, - struct neighbour *exclude) -{ - struct babel_route *route = NULL, *r = NULL; - int i = find_route_slot(prefix, plen, NULL); - - if(i < 0) - return NULL; - - route = routes[i]; - - r = route->next; - while(r) { - if(!route_expired(r) && - (!feasible || route_feasible(r)) && - (!exclude || r->neigh != exclude) && - (route_metric(r) < route_metric(route))) - route = r; - r = r->next; - } - return route; -} - -void -update_route_metric(struct babel_route *route) -{ - int oldmetric = route_metric(route); - - if(route_expired(route)) { - if(route->refmetric < INFINITY) { - route->seqno = seqno_plus(route->src->seqno, 1); - retract_route(route); - if(oldmetric < INFINITY) - route_changed(route, route->src, oldmetric); - } - } else { - struct neighbour *neigh = route->neigh; - int add_metric = input_filter(route->src->id, - route->src->prefix, route->src->plen, - neigh->address, - neigh->ifp->ifindex); - change_route_metric(route, route->refmetric, - neighbour_cost(route->neigh), add_metric); - if(route_metric(route) != oldmetric) - route_changed(route, route->src, oldmetric); - } -} - -/* Called whenever a neighbour's cost changes, to update the metric of - all routes through that neighbour. Calls local_notify_neighbour. */ -void -update_neighbour_metric(struct neighbour *neigh, int changed) -{ - - if(changed) { - int i; - - for(i = 0; i < route_slots; i++) { - struct babel_route *r = routes[i]; - while(r) { - if(r->neigh == neigh) - update_route_metric(r); - r = r->next; - } - } - } -} - -void -update_interface_metric(struct interface *ifp) -{ - int i; - - for(i = 0; i < route_slots; i++) { - struct babel_route *r = routes[i]; - while(r) { - if(r->neigh->ifp == ifp) - update_route_metric(r); - r = r->next; - } - } -} - -/* This is called whenever we receive an update. */ -struct babel_route * -update_route(const unsigned char *router_id, - const unsigned char *prefix, unsigned char plen, - unsigned short seqno, unsigned short refmetric, - unsigned short interval, - struct neighbour *neigh, const unsigned char *nexthop, - const unsigned char *channels, int channels_len) -{ - struct babel_route *route; - struct source *src; - int metric, feasible; - int add_metric; - int hold_time = MAX((4 * interval) / 100 + interval / 50, 15); - - if(memcmp(router_id, myid, 8) == 0) - return NULL; - - if(martian_prefix(prefix, plen)) { - zlog_err("Rejecting martian route to %s through %s.", - format_prefix(prefix, plen), format_address(router_id)); - return NULL; - } - - add_metric = input_filter(router_id, prefix, plen, - neigh->address, neigh->ifp->ifindex); - if(add_metric >= INFINITY) - return NULL; - - route = find_route(prefix, plen, neigh, nexthop); - - if(route && memcmp(route->src->id, router_id, 8) == 0) - /* Avoid scanning the source table. */ - src = route->src; - else - src = find_source(router_id, prefix, plen, 1, seqno); - - if(src == NULL) - return NULL; - - feasible = update_feasible(src, seqno, refmetric); - metric = MIN((int)refmetric + neighbour_cost(neigh) + add_metric, INFINITY); - - if(route) { - struct source *oldsrc; - unsigned short oldmetric; - int lost = 0; - - oldsrc = route->src; - oldmetric = route_metric(route); - - /* If a successor switches sources, we must accept his update even - if it makes a route unfeasible in order to break any routing loops - in a timely manner. If the source remains the same, we ignore - the update. */ - if(!feasible && route->installed) { - debugf(BABEL_DEBUG_COMMON,"Unfeasible update for installed route to %s " - "(%s %d %d -> %s %d %d).", - format_prefix(src->prefix, src->plen), - format_address(route->src->id), - route->seqno, route->refmetric, - format_address(src->id), seqno, refmetric); - if(src != route->src) { - uninstall_route(route); - lost = 1; - } - } - - route->src = retain_source(src); - if((feasible || keep_unfeasible) && refmetric < INFINITY) - route->time = babel_now.tv_sec; - route->seqno = seqno; - change_route_metric(route, - refmetric, neighbour_cost(neigh), add_metric); - route->hold_time = hold_time; - - route_changed(route, oldsrc, oldmetric); - if(lost) - route_lost(oldsrc, oldmetric); - - if(!feasible) - send_unfeasible_request(neigh, route->installed && route_old(route), - seqno, metric, src); - release_source(oldsrc); - } else { - struct babel_route *new_route; - - if(refmetric >= INFINITY) - /* Somebody's retracting a route we never saw. */ - return NULL; - if(!feasible) { - send_unfeasible_request(neigh, 0, seqno, metric, src); - if(!keep_unfeasible) - return NULL; - } - - route = malloc(sizeof(struct babel_route)); - if(route == NULL) { - perror("malloc(route)"); - return NULL; - } - - route->src = retain_source(src); - route->refmetric = refmetric; - route->cost = neighbour_cost(neigh); - route->add_metric = add_metric; - route->seqno = seqno; - route->neigh = neigh; - memcpy(route->nexthop, nexthop, 16); - route->time = babel_now.tv_sec; - route->hold_time = hold_time; - route->installed = 0; - memset(&route->channels, 0, sizeof(route->channels)); - if(channels_len > 0) - memcpy(&route->channels, channels, - MIN(channels_len, DIVERSITY_HOPS)); - route->next = NULL; - new_route = insert_route(route); - if(new_route == NULL) { - fprintf(stderr, "Couldn't insert route.\n"); - free(route); - return NULL; - } - consider_route(route); - } - return route; -} - -/* We just received an unfeasible update. If it's any good, send - a request for a new seqno. */ -void -send_unfeasible_request(struct neighbour *neigh, int force, - unsigned short seqno, unsigned short metric, - struct source *src) -{ - struct babel_route *route = find_installed_route(src->prefix, src->plen); - - if(seqno_minus(src->seqno, seqno) > 100) { - /* Probably a source that lost its seqno. Let it time-out. */ - return; - } - - if(force || !route || route_metric(route) >= metric + 512) { - send_unicast_multihop_request(neigh, src->prefix, src->plen, - src->metric >= INFINITY ? - src->seqno : - seqno_plus(src->seqno, 1), - src->id, 127); - } -} - -/* This takes a feasible route and decides whether to install it. */ -static void -consider_route(struct babel_route *route) -{ - struct babel_route *installed; - struct xroute *xroute; - - if(route->installed) - return; - - if(!route_feasible(route)) - return; - - xroute = find_xroute(route->src->prefix, route->src->plen); - if(xroute && (allow_duplicates < 0 || xroute->metric >= allow_duplicates)) - return; - - installed = find_installed_route(route->src->prefix, route->src->plen); - - if(installed == NULL) - goto install; - - if(route_metric(route) >= INFINITY) - return; - - if(route_metric(installed) >= INFINITY) - goto install; - - if(route_metric(installed) >= route_metric(route) + 64) - goto install; - - return; - - install: - switch_routes(installed, route); - if(installed && route->installed) - send_triggered_update(route, installed->src, route_metric(installed)); - else - send_update(NULL, 1, route->src->prefix, route->src->plen); - return; -} - -void -retract_neighbour_routes(struct neighbour *neigh) -{ - int i; - - for(i = 0; i < route_slots; i++) { - struct babel_route *r = routes[i]; - while(r) { - if(r->neigh == neigh) { - if(r->refmetric != INFINITY) { - unsigned short oldmetric = route_metric(r); - retract_route(r); - if(oldmetric != INFINITY) - route_changed(r, r->src, oldmetric); - } - } - r = r->next; - } - i++; - } -} - -void -send_triggered_update(struct babel_route *route, struct source *oldsrc, - unsigned oldmetric) -{ - unsigned newmetric, diff; - /* 1 means send speedily, 2 means resend */ - int urgent; - - if(!route->installed) - return; - - newmetric = route_metric(route); - diff = - newmetric >= oldmetric ? newmetric - oldmetric : oldmetric - newmetric; - - if(route->src != oldsrc || (oldmetric < INFINITY && newmetric >= INFINITY)) - /* Switching sources can cause transient routing loops. - Retractions can cause blackholes. */ - urgent = 2; - else if(newmetric > oldmetric && oldmetric < 6 * 256 && diff >= 512) - /* Route getting significantly worse */ - urgent = 1; - else if(unsatisfied_request(route->src->prefix, route->src->plen, - route->seqno, route->src->id)) - /* Make sure that requests are satisfied speedily */ - urgent = 1; - else if(oldmetric >= INFINITY && newmetric < INFINITY) - /* New route */ - urgent = 0; - else if(newmetric < oldmetric && diff < 1024) - /* Route getting better. This may be a transient fluctuation, so - don't advertise it to avoid making routes unfeasible later on. */ - return; - else if(diff < 384) - /* Don't fret about trivialities */ - return; - else - urgent = 0; - - if(urgent >= 2) - send_update_resend(NULL, route->src->prefix, route->src->plen); - else - send_update(NULL, urgent, route->src->prefix, route->src->plen); - - if(oldmetric < INFINITY) { - if(newmetric >= oldmetric + 512) { - send_request_resend(NULL, route->src->prefix, route->src->plen, - route->src->metric >= INFINITY ? - route->src->seqno : - seqno_plus(route->src->seqno, 1), - route->src->id); - } else if(newmetric >= oldmetric + 288) { - send_request(NULL, route->src->prefix, route->src->plen); - } - } -} - -/* A route has just changed. Decide whether to switch to a different route or - send an update. */ -void -route_changed(struct babel_route *route, - struct source *oldsrc, unsigned short oldmetric) -{ - if(route->installed) { - if(route_metric(route) > oldmetric) { - struct babel_route *better_route; - better_route = - find_best_route(route->src->prefix, route->src->plen, 1, NULL); - if(better_route && - route_metric(better_route) <= route_metric(route) - 96) - consider_route(better_route); - } - - if(route->installed) - /* We didn't change routes after all. */ - send_triggered_update(route, oldsrc, oldmetric); - } else { - /* Reconsider routes even when their metric didn't decrease, - they may not have been feasible before. */ - consider_route(route); - } -} - -/* We just lost the installed route to a given destination. */ -void -route_lost(struct source *src, unsigned oldmetric) -{ - struct babel_route *new_route; - new_route = find_best_route(src->prefix, src->plen, 1, NULL); - if(new_route) { - consider_route(new_route); - } else if(oldmetric < INFINITY) { - /* Complain loudly. */ - send_update_resend(NULL, src->prefix, src->plen); - send_request_resend(NULL, src->prefix, src->plen, - src->metric >= INFINITY ? - src->seqno : seqno_plus(src->seqno, 1), - src->id); - } -} - -/* This is called periodically to flush old routes. It will also send - requests for routes that are about to expire. */ -void -expire_routes(void) -{ - struct babel_route *r; - int i; - - debugf(BABEL_DEBUG_COMMON,"Expiring old routes."); - - i = 0; - while(i < route_slots) { - r = routes[i]; - while(r) { - /* Protect against clock being stepped. */ - if(r->time > babel_now.tv_sec || route_old(r)) { - flush_route(r); - goto again; - } - - update_route_metric(r); - - if(r->installed && r->refmetric < INFINITY) { - if(route_old(r)) - /* Route about to expire, send a request. */ - send_unicast_request(r->neigh, - r->src->prefix, r->src->plen); - } - r = r->next; - } - i++; - again: - ; - } -} diff --git a/babeld/route.h b/babeld/route.h deleted file mode 100644 index b6d2d2946..000000000 --- a/babeld/route.h +++ /dev/null @@ -1,135 +0,0 @@ -/* - * This file is free software: you may copy, redistribute 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 file 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, see . - * - * This file incorporates work covered by the following copyright and - * permission notice: - * -Copyright (c) 2007, 2008 by Juliusz Chroboczek -Copyright 2011 by Matthieu Boutier and Juliusz Chroboczek - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -*/ - -#ifndef BABEL_ROUTE_H -#define BABEL_ROUTE_H - -#include "babel_interface.h" -#include "source.h" - -#define DIVERSITY_NONE 0 -#define DIVERSITY_INTERFACE_1 1 -#define DIVERSITY_CHANNEL_1 2 -#define DIVERSITY_CHANNEL 3 - -#define DIVERSITY_HOPS 8 - -struct babel_route { - struct source *src; - unsigned short refmetric; - unsigned short cost; - unsigned short add_metric; - unsigned short seqno; - struct neighbour *neigh; - unsigned char nexthop[16]; - time_t time; - unsigned short hold_time; /* in seconds */ - short installed; - unsigned char channels[DIVERSITY_HOPS]; - struct babel_route *next; -}; - -extern struct babel_route **routes; -extern int kernel_metric, allow_duplicates; -extern int diversity_kind, diversity_factor; -extern int keep_unfeasible; - -static inline int -route_metric(const struct babel_route *route) -{ - int m = (int)route->refmetric + route->cost + route->add_metric; - return MIN(m, INFINITY); -} - -static inline int -route_metric_noninterfering(const struct babel_route *route) -{ - int m = - (int)route->refmetric + - (diversity_factor * route->cost + 128) / 256 + - route->add_metric; - m = MAX(m, route->refmetric + 1); - return MIN(m, INFINITY); -} - -struct babel_route *find_route(const unsigned char *prefix, unsigned char plen, - struct neighbour *neigh, const unsigned char *nexthop); -struct babel_route *find_installed_route(const unsigned char *prefix, - unsigned char plen); -int installed_routes_estimate(void); -void flush_route(struct babel_route *route); -void flush_all_routes(void); -void flush_neighbour_routes(struct neighbour *neigh); -void flush_interface_routes(struct interface *ifp, int v4only); -void for_all_routes(void (*f)(struct babel_route*, void*), void *closure); -void for_all_installed_routes(void (*f)(struct babel_route*, void*), void *closure); -void install_route(struct babel_route *route); -void uninstall_route(struct babel_route *route); -void switch_route(struct babel_route *old, struct babel_route *new); -int route_feasible(struct babel_route *route); -int route_old(struct babel_route *route); -int route_expired(struct babel_route *route); -int route_interferes(struct babel_route *route, struct interface *ifp); -int update_feasible(struct source *src, - unsigned short seqno, unsigned short refmetric); -struct babel_route *find_best_route(const unsigned char *prefix, unsigned char plen, - int feasible, struct neighbour *exclude); -struct babel_route *install_best_route(const unsigned char prefix[16], - unsigned char plen); -void update_neighbour_metric(struct neighbour *neigh, int change); -void update_interface_metric(struct interface *ifp); -void update_route_metric(struct babel_route *route); -struct babel_route *update_route(const unsigned char *id, - const unsigned char *prefix, unsigned char plen, - unsigned short seqno, unsigned short refmetric, - unsigned short interval, struct neighbour *neigh, - const unsigned char *nexthop, - const unsigned char *channels, int channels_len); -void retract_neighbour_routes(struct neighbour *neigh); -void send_unfeasible_request(struct neighbour *neigh, int force, - unsigned short seqno, unsigned short metric, - struct source *src); -void send_triggered_update(struct babel_route *route, - struct source *oldsrc, unsigned oldmetric); -void route_changed(struct babel_route *route, - struct source *oldsrc, unsigned short oldmetric); -void route_lost(struct source *src, unsigned oldmetric); -void expire_routes(void); - -#endif diff --git a/babeld/source.c b/babeld/source.c deleted file mode 100644 index 772112d4d..000000000 --- a/babeld/source.c +++ /dev/null @@ -1,180 +0,0 @@ -/* - * This file is free software: you may copy, redistribute 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 file 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, see . - * - * This file incorporates work covered by the following copyright and - * permission notice: - * -Copyright (c) 2007, 2008 by Juliusz Chroboczek - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -*/ - -#include -#include -#include -#include - -#include "babel_main.h" -#include "babeld.h" -#include "util.h" -#include "source.h" -#include "babel_interface.h" -#include "route.h" - -struct source *srcs = NULL; - -struct source* -find_source(const unsigned char *id, const unsigned char *p, unsigned char plen, - int create, unsigned short seqno) -{ - struct source *src; - - for(src = srcs; src; src = src->next) { - /* This should really be a hash table. For now, check the - last byte first. */ - if(src->id[7] != id[7]) - continue; - if(memcmp(src->id, id, 8) != 0) - continue; - if(src->plen != plen) - continue; - if(memcmp(src->prefix, p, 16) == 0) - return src; - } - - if(!create) - return NULL; - - src = malloc(sizeof(struct source)); - if(src == NULL) { - zlog_err("malloc(source): %s", safe_strerror(errno)); - return NULL; - } - - memcpy(src->id, id, 8); - memcpy(src->prefix, p, 16); - src->plen = plen; - src->seqno = seqno; - src->metric = INFINITY; - src->time = babel_now.tv_sec; - src->route_count = 0; - src->next = srcs; - srcs = src; - return src; -} - -struct source * -retain_source(struct source *src) -{ - assert(src->route_count < 0xffff); - src->route_count++; - return src; -} - -void -release_source(struct source *src) -{ - assert(src->route_count > 0); - src->route_count--; -} - -int -flush_source(struct source *src) -{ - if(src->route_count > 0) - /* The source is in use by a route. */ - return 0; - - if(srcs == src) { - srcs = src->next; - } else { - struct source *previous = srcs; - while(previous->next != src) - previous = previous->next; - previous->next = src->next; - } - - free(src); - return 1; -} - -void -update_source(struct source *src, - unsigned short seqno, unsigned short metric) -{ - if(metric >= INFINITY) - return; - - /* If a source is expired, pretend that it doesn't exist and update - it unconditionally. This makes ensures that old data will - eventually be overridden, and prevents us from getting stuck if - a router loses its sequence number. */ - if(src->time < babel_now.tv_sec - SOURCE_GC_TIME || - seqno_compare(src->seqno, seqno) < 0 || - (src->seqno == seqno && src->metric > metric)) { - src->seqno = seqno; - src->metric = metric; - } - src->time = babel_now.tv_sec; -} - -void -expire_sources() -{ - struct source *src; - - src = srcs; - while(src) { - if(src->time > babel_now.tv_sec) - /* clock stepped */ - src->time = babel_now.tv_sec; - if(src->time < babel_now.tv_sec - SOURCE_GC_TIME) { - struct source *old = src; - src = src->next; - flush_source(old); - continue; - } - src = src->next; - } -} - -void -check_sources_released(void) -{ - struct source *src; - - for(src = srcs; src; src = src->next) { - if(src->route_count != 0) - fprintf(stderr, "Warning: source %s %s has refcount %d.\n", - format_eui64(src->id), - format_prefix(src->prefix, src->plen), - (int)src->route_count); - } -} diff --git a/babeld/source.h b/babeld/source.h deleted file mode 100644 index 62a7e1eea..000000000 --- a/babeld/source.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * This file is free software: you may copy, redistribute 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 file 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, see . - * - * This file incorporates work covered by the following copyright and - * permission notice: - * -Copyright (c) 2007, 2008 by Juliusz Chroboczek - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -*/ - -#ifndef BABEL_SOURCE_H -#define BABEL_SOURCE_H - -#define SOURCE_GC_TIME 200 - -struct source { - struct source *next; - unsigned char id[8]; - unsigned char prefix[16]; - unsigned char plen; - unsigned short seqno; - unsigned short metric; - unsigned short route_count; - time_t time; -}; - -struct source *find_source(const unsigned char *id, - const unsigned char *p, - unsigned char plen, - int create, unsigned short seqno); -struct source *retain_source(struct source *src); -void release_source(struct source *src); -int flush_source(struct source *src); -void update_source(struct source *src, - unsigned short seqno, unsigned short metric); -void expire_sources(void); -void check_sources_released(void); - -#endif diff --git a/babeld/util.c b/babeld/util.c deleted file mode 100644 index 011f3824e..000000000 --- a/babeld/util.c +++ /dev/null @@ -1,445 +0,0 @@ -/* - * This file is free software: you may copy, redistribute 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 file 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, see . - * - * This file incorporates work covered by the following copyright and - * permission notice: - * -Copyright (c) 2007, 2008 by Juliusz Chroboczek -Copyright 2011 by Matthieu Boutier and Juliusz Chroboczek - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -*/ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "babel_main.h" -#include "babeld.h" -#include "util.h" - -unsigned -roughly(unsigned value) -{ - return value * 3 / 4 + random() % (value / 2); -} - -/* d = s1 - s2 */ -void -timeval_minus(struct timeval *d, - const struct timeval *s1, const struct timeval *s2) -{ - if(s1->tv_usec >= s2->tv_usec) { - d->tv_usec = s1->tv_usec - s2->tv_usec; - d->tv_sec = s1->tv_sec - s2->tv_sec; - } else { - d->tv_usec = s1->tv_usec + 1000000 - s2->tv_usec; - d->tv_sec = s1->tv_sec - s2->tv_sec - 1; - } -} - -unsigned -timeval_minus_msec(const struct timeval *s1, const struct timeval *s2) -{ - if(s1->tv_sec < s2->tv_sec) - return 0; - - /* Avoid overflow. */ - if(s1->tv_sec - s2->tv_sec > 2000000) - return 2000000000; - - if(s1->tv_sec > s2->tv_sec) - return - (unsigned)((unsigned)(s1->tv_sec - s2->tv_sec) * 1000 + - ((int)s1->tv_usec - s2->tv_usec) / 1000); - - if(s1->tv_usec <= s2->tv_usec) - return 0; - - return (unsigned)(s1->tv_usec - s2->tv_usec) / 1000u; -} - -/* d = s + msecs */ -void -timeval_add_msec(struct timeval *d, const struct timeval *s, const int msecs) -{ - int usecs; - d->tv_sec = s->tv_sec + msecs / 1000; - usecs = s->tv_usec + (msecs % 1000) * 1000; - if(usecs < 1000000) { - d->tv_usec = usecs; - } else { - d->tv_usec = usecs - 1000000; - d->tv_sec++; - } -} - -void -set_timeout(struct timeval *timeout, int msecs) -{ - timeval_add_msec(timeout, &babel_now, roughly(msecs)); -} - -/* returns <0 if "s1" < "s2", etc. */ -int -timeval_compare(const struct timeval *s1, const struct timeval *s2) -{ - if(s1->tv_sec < s2->tv_sec) - return -1; - else if(s1->tv_sec > s2->tv_sec) - return 1; - else if(s1->tv_usec < s2->tv_usec) - return -1; - else if(s1->tv_usec > s2->tv_usec) - return 1; - else - return 0; -} - -/* set d at min(d, s) */ -/* {0, 0} represents infinity */ -void -timeval_min(struct timeval *d, const struct timeval *s) -{ - if(s->tv_sec == 0) - return; - - if(d->tv_sec == 0 || timeval_compare(d, s) > 0) { - *d = *s; - } -} - -/* set d to min(d, x) with x in [secs, secs+1] */ -void -timeval_min_sec(struct timeval *d, time_t secs) -{ - if(d->tv_sec == 0 || d->tv_sec > secs) { - d->tv_sec = secs; - d->tv_usec = random() % 1000000; - } -} - -/* parse a float value in second and return the corresponding mili-seconds. - For example: - parse_msec("12.342345") returns 12342 */ -int -parse_msec(const char *string) -{ - unsigned int in, fl; - int i, j; - - in = fl = 0; - i = 0; - while(string[i] == ' ' || string[i] == '\t') - i++; - while(string[i] >= '0' && string[i] <= '9') { - in = in * 10 + string[i] - '0'; - i++; - } - if(string[i] == '.') { - i++; - j = 0; - while(string[i] >= '0' && string[i] <= '9') { - fl = fl * 10 + string[i] - '0'; - i++; - j++; - } - - while(j > 3) { - fl /= 10; - j--; - } - while(j < 3) { - fl *= 10; - j++; - } - } - - while(string[i] == ' ' || string[i] == '\t') - i++; - - if(string[i] == '\0') - return in * 1000 + fl; - - return -1; -} - -int -in_prefix(const unsigned char *restrict address, - const unsigned char *restrict prefix, unsigned char plen) -{ - unsigned char m; - - if(plen > 128) - plen = 128; - - if(memcmp(address, prefix, plen / 8) != 0) - return 0; - - if(plen % 8 == 0) - return 1; - - m = 0xFF << (8 - (plen % 8)); - - return ((address[plen / 8] & m) == (prefix[plen / 8] & m)); -} - -unsigned char * -mask_prefix(unsigned char *restrict ret, - const unsigned char *restrict prefix, unsigned char plen) -{ - if(plen >= 128) { - memcpy(ret, prefix, 16); - return ret; - } - - memset(ret, 0, 16); - memcpy(ret, prefix, plen / 8); - if(plen % 8 != 0) - ret[plen / 8] = - (prefix[plen / 8] & ((0xFF << (8 - (plen % 8))) & 0xFF)); - return ret; -} - -static const unsigned char v4prefix[16] = - {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 0, 0, 0, 0 }; - -static const unsigned char llprefix[16] = - {0xFE, 0x80}; - -const char * -format_address(const unsigned char *address) -{ - static char buf[4][INET6_ADDRSTRLEN]; - static int i = 0; - i = (i + 1) % 4; - if(v4mapped(address)) - inet_ntop(AF_INET, address + 12, buf[i], INET6_ADDRSTRLEN); - else - inet_ntop(AF_INET6, address, buf[i], INET6_ADDRSTRLEN); - return buf[i]; -} - -const char * -format_prefix(const unsigned char *prefix, unsigned char plen) -{ - static char buf[4][INET6_ADDRSTRLEN + 4]; - static int i = 0; - int n; - i = (i + 1) % 4; - if(plen >= 96 && v4mapped(prefix)) { - inet_ntop(AF_INET, prefix + 12, buf[i], INET6_ADDRSTRLEN); - n = strlen(buf[i]); - snprintf(buf[i] + n, INET6_ADDRSTRLEN + 4 - n, "/%d", plen - 96); - } else { - inet_ntop(AF_INET6, prefix, buf[i], INET6_ADDRSTRLEN); - n = strlen(buf[i]); - snprintf(buf[i] + n, INET6_ADDRSTRLEN + 4 - n, "/%d", plen); - } - return buf[i]; -} - -const char * -format_eui64(const unsigned char *eui) -{ - static char buf[4][28]; - static int i = 0; - i = (i + 1) % 4; - snprintf(buf[i], 28, "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", - eui[0], eui[1], eui[2], eui[3], - eui[4], eui[5], eui[6], eui[7]); - return buf[i]; -} - -const char *format_bool(const int b) { - return b ? "true" : "false"; -} - -int -parse_address(const char *address, unsigned char *addr_r, int *af_r) -{ - struct in_addr ina; - struct in6_addr ina6; - int rc; - - rc = inet_pton(AF_INET, address, &ina); - if(rc > 0) { - memcpy(addr_r, v4prefix, 12); - memcpy(addr_r + 12, &ina, 4); - if(af_r) *af_r = AF_INET; - return 0; - } - - rc = inet_pton(AF_INET6, address, &ina6); - if(rc > 0) { - memcpy(addr_r, &ina6, 16); - if(af_r) *af_r = AF_INET6; - return 0; - } - - return -1; -} - -int -parse_eui64(const char *eui, unsigned char *eui_r) -{ - int n; - n = sscanf(eui, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx", - &eui_r[0], &eui_r[1], &eui_r[2], &eui_r[3], - &eui_r[4], &eui_r[5], &eui_r[6], &eui_r[7]); - if(n == 8) - return 0; - - n = sscanf(eui, "%02hhx-%02hhx-%02hhx-%02hhx-%02hhx-%02hhx-%02hhx-%02hhx", - &eui_r[0], &eui_r[1], &eui_r[2], &eui_r[3], - &eui_r[4], &eui_r[5], &eui_r[6], &eui_r[7]); - if(n == 8) - return 0; - - n = sscanf(eui, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx", - &eui_r[0], &eui_r[1], &eui_r[2], - &eui_r[5], &eui_r[6], &eui_r[7]); - if(n == 6) { - eui_r[3] = 0xFF; - eui_r[4] = 0xFE; - return 0; - } - return -1; -} - -int -wait_for_fd(int direction, int fd, int msecs) -{ - fd_set fds; - int rc; - struct timeval tv; - - tv.tv_sec = msecs / 1000; - tv.tv_usec = (msecs % 1000) * 1000; - - FD_ZERO(&fds); - FD_SET(fd, &fds); - if(direction) - rc = select(fd + 1, NULL, &fds, NULL, &tv); - else - rc = select(fd + 1, &fds, NULL, NULL, &tv); - - return rc; -} - -int -martian_prefix(const unsigned char *prefix, int plen) -{ - return - (plen >= 8 && prefix[0] == 0xFF) || - (plen >= 10 && prefix[0] == 0xFE && (prefix[1] & 0xC0) == 0x80) || - (plen >= 128 && memcmp(prefix, zeroes, 15) == 0 && - (prefix[15] == 0 || prefix[15] == 1)) || - (plen >= 96 && v4mapped(prefix) && - ((plen >= 104 && (prefix[12] == 127 || prefix[12] == 0)) || - (plen >= 100 && (prefix[12] & 0xE0) == 0xE0))); -} - -int -linklocal(const unsigned char *address) -{ - return memcmp(address, llprefix, 8) == 0; -} - -int -v4mapped(const unsigned char *address) -{ - return memcmp(address, v4prefix, 12) == 0; -} - -void -v4tov6(unsigned char *dst, const unsigned char *src) -{ - memcpy(dst, v4prefix, 12); - memcpy(dst + 12, src, 4); -} - -void -inaddr_to_uchar(unsigned char *dest, const struct in_addr *src) -{ - memcpy(dest, v4prefix, 12); - memcpy(dest + 12, src, 4); - assert(v4mapped(dest)); -} - -void -uchar_to_inaddr(struct in_addr *dest, const unsigned char *src) -{ - assert(v4mapped(src)); - memcpy(dest, src + 12, 4); -} - -void -in6addr_to_uchar(unsigned char *dest, const struct in6_addr *src) -{ - memcpy(dest, src, 16); -} - -void -uchar_to_in6addr(struct in6_addr *dest, const unsigned char *src) -{ - memcpy(dest, src, 16); -} - -int -daemonise() -{ - int rc; - - fflush(stdout); - fflush(stderr); - - rc = fork(); - if(rc < 0) - return -1; - - if(rc > 0) - exit(0); - - rc = setsid(); - if(rc < 0) - return -1; - - return 1; -} diff --git a/babeld/util.h b/babeld/util.h deleted file mode 100644 index 5d9d2f5da..000000000 --- a/babeld/util.h +++ /dev/null @@ -1,165 +0,0 @@ -/* - * This file is free software: you may copy, redistribute 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 file 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, see . - * - * This file incorporates work covered by the following copyright and - * permission notice: - * -Copyright (c) 2007, 2008 by Juliusz Chroboczek -Copyright 2011 by Matthieu Boutier and Juliusz Chroboczek - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -*/ - -#include "babeld.h" -#include "babel_main.h" -#include "log.h" - -#if defined(i386) || defined(__mc68020__) || defined(__x86_64__) -#define DO_NTOHS(_d, _s) do{ _d = ntohs(*(const unsigned short*)(_s)); }while(0) -#define DO_NTOHL(_d, _s) do{ _d = ntohl(*(const unsigned*)(_s)); } while(0) -#define DO_HTONS(_d, _s) do{ *(unsigned short*)(_d) = htons(_s); } while(0) -#define DO_HTONL(_d, _s) do{ *(unsigned*)(_d) = htonl(_s); } while(0) -/* Some versions of gcc seem to be buggy, and ignore the packed attribute. - Disable this code until the issue is clarified. */ -/* #elif defined __GNUC__*/ -#elif 0 -struct __us { unsigned short x __attribute__((packed)); }; -#define DO_NTOHS(_d, _s) \ - do { _d = ntohs(((const struct __us*)(_s))->x); } while(0) -#define DO_HTONS(_d, _s) \ - do { ((struct __us*)(_d))->x = htons(_s); } while(0) -#else -#define DO_NTOHS(_d, _s) \ - do { short _dd; \ - memcpy(&(_dd), (_s), 2); \ - _d = ntohs(_dd); } while(0) -#define DO_HTONS(_d, _s) \ - do { unsigned short _dd; \ - _dd = htons(_s); \ - memcpy((_d), &(_dd), 2); } while(0) -#endif - -static inline int -seqno_compare(unsigned short s1, unsigned short s2) -{ - if(s1 == s2) - return 0; - else - return ((s2 - s1) & 0x8000) ? 1 : -1; -} - -static inline short -seqno_minus(unsigned short s1, unsigned short s2) -{ - return (short)((s1 - s2) & 0xFFFF); -} - -static inline unsigned short -seqno_plus(unsigned short s, int plus) -{ - return ((s + plus) & 0xFFFF); -} - -unsigned roughly(unsigned value); -void timeval_minus(struct timeval *d, - const struct timeval *s1, const struct timeval *s2); -unsigned timeval_minus_msec(const struct timeval *s1, const struct timeval *s2) - ATTRIBUTE ((pure)); -void timeval_add_msec(struct timeval *d, - const struct timeval *s, const int msecs); -void set_timeout (struct timeval *timeout, int msecs); -int timeval_compare(const struct timeval *s1, const struct timeval *s2) - ATTRIBUTE ((pure)); -void timeval_min(struct timeval *d, const struct timeval *s); -void timeval_min_sec(struct timeval *d, time_t secs); -int parse_msec(const char *string) ATTRIBUTE ((pure)); -int in_prefix(const unsigned char *restrict address, - const unsigned char *restrict prefix, unsigned char plen) - ATTRIBUTE ((pure)); -unsigned char *mask_prefix(unsigned char *restrict ret, - const unsigned char *restrict prefix, - unsigned char plen); -const char *format_address(const unsigned char *address); -const char *format_prefix(const unsigned char *address, unsigned char prefix); -const char *format_eui64(const unsigned char *eui); -const char *format_bool(const int b); -int parse_address(const char *address, unsigned char *addr_r, int *af_r); -int parse_eui64(const char *eui, unsigned char *eui_r); -int wait_for_fd(int direction, int fd, int msecs); -int martian_prefix(const unsigned char *prefix, int plen) ATTRIBUTE ((pure)); -int linklocal(const unsigned char *address) ATTRIBUTE ((pure)); -int v4mapped(const unsigned char *address) ATTRIBUTE ((pure)); -void v4tov6(unsigned char *dst, const unsigned char *src); -void inaddr_to_uchar(unsigned char *dest, const struct in_addr *src); -void uchar_to_inaddr(struct in_addr *dest, const unsigned char *src); -void in6addr_to_uchar(unsigned char *dest, const struct in6_addr *src); -void uchar_to_in6addr(struct in6_addr *dest, const unsigned char *src); -int daemonise(void); - -/* If debugging is disabled, we want to avoid calling format_address - for every omitted debugging message. So debug is a macro. But - vararg macros are not portable. */ -#if defined NO_DEBUG - -#if defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L -#define debugf(...) do {} while(0) -#elif defined __GNUC__ -#define debugf(_args...) do {} while(0) -#else -static inline void debugf(int level, const char *format, ...) { return; } -#endif - -#else /* NO_DEBUG */ - -/* some levels */ -#define BABEL_DEBUG_COMMON (1 << 0) -#define BABEL_DEBUG_KERNEL (1 << 1) -#define BABEL_DEBUG_FILTER (1 << 2) -#define BABEL_DEBUG_TIMEOUT (1 << 3) -#define BABEL_DEBUG_IF (1 << 4) -#define BABEL_DEBUG_ROUTE (1 << 5) -#define BABEL_DEBUG_ALL (0xFFFF) - -#if defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L -#define debugf(level, ...) \ -do { \ -if(UNLIKELY(debug & level)) zlog_debug(__VA_ARGS__); \ -} while(0) -#elif defined __GNUC__ -#define debugf(level, _args...) \ -do { \ -if(UNLIKELY(debug & level)) zlog_debug(_args); \ -} while(0) -#else -static inline void debugf(int level, const char *format, ...) { return; } -#endif - -#endif /* NO_DEBUG */ - diff --git a/babeld/xroute.c b/babeld/xroute.c deleted file mode 100644 index 806516718..000000000 --- a/babeld/xroute.c +++ /dev/null @@ -1,237 +0,0 @@ -/* - * This file is free software: you may copy, redistribute 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 file 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, see . - * - * This file incorporates work covered by the following copyright and - * permission notice: - * -Copyright (c) 2007, 2008 by Juliusz Chroboczek -Copyright 2011 by Matthieu Boutier and Juliusz Chroboczek - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -*/ - -#include -#include "if.h" -#include "log.h" - -#include "babeld.h" -#include "kernel.h" -#include "neighbour.h" -#include "message.h" -#include "route.h" -#include "xroute.h" -#include "util.h" -#include "babel_interface.h" - -static int xroute_add_new_route(unsigned char prefix[16], unsigned char plen, - unsigned short metric, unsigned int ifindex, - int proto, int send_updates); - -static struct xroute *xroutes; -static int numxroutes = 0, maxxroutes = 0; - -/* Add redistributed route to Babel table. */ -int -babel_ipv4_route_add (struct zapi_ipv4 *api, struct prefix_ipv4 *prefix, - unsigned int ifindex, struct in_addr *nexthop) -{ - unsigned char uchar_prefix[16]; - - inaddr_to_uchar(uchar_prefix, &prefix->prefix); - debugf(BABEL_DEBUG_ROUTE, "Adding new ipv4 route comming from Zebra."); - xroute_add_new_route(uchar_prefix, prefix->prefixlen + 96, - api->metric, ifindex, 0, 1); - return 0; -} - -/* Remove redistributed route from Babel table. */ -int -babel_ipv4_route_delete (struct zapi_ipv4 *api, struct prefix_ipv4 *prefix, - unsigned int ifindex) -{ - unsigned char uchar_prefix[16]; - struct xroute *xroute = NULL; - - inaddr_to_uchar(uchar_prefix, &prefix->prefix); - xroute = find_xroute(uchar_prefix, prefix->prefixlen + 96); - if (xroute != NULL) { - debugf(BABEL_DEBUG_ROUTE, "Removing ipv4 route (from zebra)."); - flush_xroute(xroute); - } - return 0; -} - -/* Add redistributed route to Babel table. */ -int -babel_ipv6_route_add (struct zapi_ipv6 *api, struct prefix_ipv6 *prefix, - unsigned int ifindex, struct in6_addr *nexthop) -{ - unsigned char uchar_prefix[16]; - - in6addr_to_uchar(uchar_prefix, &prefix->prefix); - debugf(BABEL_DEBUG_ROUTE, "Adding new route comming from Zebra."); - xroute_add_new_route(uchar_prefix, prefix->prefixlen, api->metric, ifindex, - 0, 1); - return 0; -} - -/* Remove redistributed route from Babel table. */ -int -babel_ipv6_route_delete (struct zapi_ipv6 *api, struct prefix_ipv6 *prefix, - unsigned int ifindex) -{ - unsigned char uchar_prefix[16]; - struct xroute *xroute = NULL; - - in6addr_to_uchar(uchar_prefix, &prefix->prefix); - xroute = find_xroute(uchar_prefix, prefix->prefixlen); - if (xroute != NULL) { - debugf(BABEL_DEBUG_ROUTE, "Removing route (from zebra)."); - flush_xroute(xroute); - } - return 0; -} - -struct xroute * -find_xroute(const unsigned char *prefix, unsigned char plen) -{ - int i; - for(i = 0; i < numxroutes; i++) { - if(xroutes[i].plen == plen && - memcmp(xroutes[i].prefix, prefix, 16) == 0) - return &xroutes[i]; - } - return NULL; -} - -void -flush_xroute(struct xroute *xroute) -{ - int i; - - i = xroute - xroutes; - assert(i >= 0 && i < numxroutes); - - if(i != numxroutes - 1) - memcpy(xroutes + i, xroutes + numxroutes - 1, sizeof(struct xroute)); - numxroutes--; - VALGRIND_MAKE_MEM_UNDEFINED(xroutes + numxroutes, sizeof(struct xroute)); - - if(numxroutes == 0) { - free(xroutes); - xroutes = NULL; - maxxroutes = 0; - } else if(maxxroutes > 8 && numxroutes < maxxroutes / 4) { - struct xroute *new_xroutes; - int n = maxxroutes / 2; - new_xroutes = realloc(xroutes, n * sizeof(struct xroute)); - if(new_xroutes == NULL) - return; - xroutes = new_xroutes; - maxxroutes = n; - } -} - -static int -add_xroute(unsigned char prefix[16], unsigned char plen, - unsigned short metric, unsigned int ifindex, int proto) -{ - struct xroute *xroute = find_xroute(prefix, plen); - if(xroute) { - if(xroute->metric <= metric) - return 0; - xroute->metric = metric; - return 1; - } - - if(numxroutes >= maxxroutes) { - struct xroute *new_xroutes; - int n = maxxroutes < 1 ? 8 : 2 * maxxroutes; - new_xroutes = xroutes == NULL ? - malloc(n * sizeof(struct xroute)) : - realloc(xroutes, n * sizeof(struct xroute)); - if(new_xroutes == NULL) - return -1; - maxxroutes = n; - xroutes = new_xroutes; - } - - memcpy(xroutes[numxroutes].prefix, prefix, 16); - xroutes[numxroutes].plen = plen; - xroutes[numxroutes].metric = metric; - xroutes[numxroutes].ifindex = ifindex; - xroutes[numxroutes].proto = proto; - numxroutes++; - return 1; -} - -/* Returns an overestimate of the number of xroutes. */ -int -xroutes_estimate() -{ - return numxroutes; -} - -void -for_all_xroutes(void (*f)(struct xroute*, void*), void *closure) -{ - int i; - - for(i = 0; i < numxroutes; i++) - (*f)(&xroutes[i], closure); -} - -/* add an xroute, verifying some conditions; return 0 if there is no changes */ -static int -xroute_add_new_route(unsigned char prefix[16], unsigned char plen, - unsigned short metric, unsigned int ifindex, - int proto, int send_updates) -{ - int rc; - if(martian_prefix(prefix, plen)) - return 0; - metric = redistribute_filter(prefix, plen, ifindex, proto); - if(metric < INFINITY) { - rc = add_xroute(prefix, plen, metric, ifindex, proto); - if(rc > 0) { - struct babel_route *route; - route = find_installed_route(prefix, plen); - if(route) { - if(allow_duplicates < 0 || - metric < allow_duplicates) - uninstall_route(route); - } - if(send_updates) - send_update(NULL, 0, prefix, plen); - return 1; - } - } - return 0; -} diff --git a/babeld/xroute.h b/babeld/xroute.h deleted file mode 100644 index 4d4ab99d0..000000000 --- a/babeld/xroute.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * This file is free software: you may copy, redistribute 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 file 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, see . - * - * This file incorporates work covered by the following copyright and - * permission notice: - * -Copyright (c) 2007, 2008 by Juliusz Chroboczek -Copyright 2011 by Matthieu Boutier and Juliusz Chroboczek - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -*/ - -struct xroute { - unsigned char prefix[16]; - unsigned char plen; - unsigned short metric; - unsigned int ifindex; - int proto; -}; - -struct xroute *find_xroute(const unsigned char *prefix, unsigned char plen); -void flush_xroute(struct xroute *xroute); -int babel_ipv4_route_add (struct zapi_ipv4 *api, struct prefix_ipv4 *prefix, - unsigned int ifindex, struct in_addr *nexthop); -int babel_ipv4_route_delete (struct zapi_ipv4 *api, struct prefix_ipv4 *prefix, - unsigned int ifindex); -int babel_ipv6_route_add (struct zapi_ipv6 *api, struct prefix_ipv6 *prefix, - unsigned int ifindex, struct in6_addr *nexthop); -int babel_ipv6_route_delete (struct zapi_ipv6 *api, struct prefix_ipv6 *prefix, - unsigned int ifindex); -int xroutes_estimate(void); -void for_all_xroutes(void (*f)(struct xroute*, void*), void *closure); diff --git a/bgpd/Makefile.am b/bgpd/Makefile.am index 1b17d3863..753b679f8 100644 --- a/bgpd/Makefile.am +++ b/bgpd/Makefile.am @@ -1,32 +1,39 @@ ## Process this file with automake to produce Makefile.in. -INCLUDES = @INCLUDES@ -I.. -I$(top_srcdir) -I$(top_srcdir)/lib @SNMP_INCLUDES@ +AM_CPPFLAGS = -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib DEFS = @DEFS@ -DSYSCONFDIR=\"$(sysconfdir)/\" INSTALL_SDATA=@INSTALL@ -m 600 -AM_CFLAGS = $(PICFLAGS) -AM_LDFLAGS = $(PILDFLAGS) +AM_CFLAGS = $(WERROR) noinst_LIBRARIES = libbgp.a sbin_PROGRAMS = bgpd +bin_PROGRAMS = bgp_btoa libbgp_a_SOURCES = \ bgpd.c bgp_fsm.c bgp_aspath.c bgp_community.c bgp_attr.c \ bgp_debug.c bgp_route.c bgp_zebra.c bgp_open.c bgp_routemap.c \ bgp_packet.c bgp_network.c bgp_filter.c bgp_regex.c bgp_clist.c \ - bgp_dump.c bgp_snmp.c bgp_ecommunity.c bgp_mplsvpn.c bgp_nexthop.c \ - bgp_damp.c bgp_table.c bgp_advertise.c bgp_vty.c + bgp_dump.c bgp_snmp.c bgp_ecommunity.c bgp_lcommunity.c \ + bgp_mplsvpn.c bgp_nexthop.c \ + bgp_damp.c bgp_table.c bgp_advertise.c bgp_vty.c bgp_mpath.c \ + bgp_encap.c bgp_encap_tlv.c bgp_nht.c noinst_HEADERS = \ bgp_aspath.h bgp_attr.h bgp_community.h bgp_debug.h bgp_fsm.h \ bgp_network.h bgp_open.h bgp_packet.h bgp_regex.h bgp_route.h \ bgpd.h bgp_filter.h bgp_clist.h bgp_dump.h bgp_zebra.h \ - bgp_ecommunity.h bgp_mplsvpn.h bgp_nexthop.h bgp_damp.h bgp_table.h \ - bgp_advertise.h bgp_snmp.h bgp_vty.h + bgp_ecommunity.h bgp_lcommunity.h \ + bgp_mplsvpn.h bgp_nexthop.h bgp_damp.h bgp_table.h \ + bgp_advertise.h bgp_snmp.h bgp_vty.h bgp_mpath.h \ + bgp_encap.h bgp_encap_tlv.h bgp_encap_types.h bgp_nht.h bgpd_SOURCES = bgp_main.c bgpd_LDADD = libbgp.a ../lib/libzebra.la @LIBCAP@ @LIBM@ +bgp_btoa_SOURCES = bgp_btoa.c +bgp_btoa_LDADD = libbgp.a ../lib/libzebra.la @LIBCAP@ @LIBM@ + examplesdir = $(exampledir) dist_examples_DATA = bgpd.conf.sample bgpd.conf.sample2 diff --git a/bgpd/bgp_advertise.c b/bgpd/bgp_advertise.c index 666218fa8..1b17b66f3 100644 --- a/bgpd/bgp_advertise.c +++ b/bgpd/bgp_advertise.c @@ -25,6 +25,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "prefix.h" #include "hash.h" #include "thread.h" +#include "filter.h" #include "bgpd/bgpd.h" #include "bgpd/bgp_table.h" @@ -35,7 +36,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 +80,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 +152,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) @@ -185,10 +186,12 @@ bgp_advertise_clean (struct peer *peer, struct bgp_adj_out *adj, struct bgp_advertise *adv; struct bgp_advertise_attr *baa; struct bgp_advertise *next; + struct bgp_advertise_fifo *fhead; adv = adj->adv; baa = adv->baa; next = NULL; + fhead = (struct bgp_advertise_fifo *)&peer->sync[afi][safi]->withdraw; if (baa) { @@ -200,10 +203,12 @@ bgp_advertise_clean (struct peer *peer, struct bgp_adj_out *adj, /* Unintern BGP advertise attribute. */ bgp_advertise_unintern (peer->hash[afi][safi], baa); + + fhead = (struct bgp_advertise_fifo *)&peer->sync[afi][safi]->update; } /* Unlink myself from advertisement FIFO. */ - FIFO_DEL (adv); + BGP_ADV_FIFO_DEL (fhead, adv); /* Free memory. */ bgp_advertise_free (adj->adv); @@ -263,7 +268,7 @@ bgp_adj_out_set (struct bgp_node *rn, struct peer *peer, struct prefix *p, /* Add new advertisement to advertisement attribute list. */ bgp_advertise_add (adv->baa, adv); - FIFO_ADD (&peer->sync[afi][safi]->update, &adv->fifo); + BGP_ADV_FIFO_ADD (&peer->sync[afi][safi]->update, &adv->fifo); } void @@ -297,7 +302,7 @@ bgp_adj_out_unset (struct bgp_node *rn, struct peer *peer, struct prefix *p, adv->adj = adj; /* Add to synchronization entry for withdraw announcement. */ - FIFO_ADD (&peer->sync[afi][safi]->withdraw, &adv->fifo); + BGP_ADV_FIFO_ADD (&peer->sync[afi][safi]->withdraw, &adv->fifo); /* Schedule packet write. */ BGP_WRITE_ON (peer->t_write, bgp_write, peer->fd); @@ -327,7 +332,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) { @@ -361,7 +366,7 @@ bgp_adj_in_remove (struct bgp_node *rn, struct bgp_adj_in *bai) XFREE (MTYPE_BGP_ADJ_IN, bai); } -void +int bgp_adj_in_unset (struct bgp_node *rn, struct peer *peer) { struct bgp_adj_in *adj; @@ -371,12 +376,13 @@ bgp_adj_in_unset (struct bgp_node *rn, struct peer *peer) break; if (! adj) - return; + return 0; bgp_adj_in_remove (rn, adj); bgp_unlock_node (rn); + return 1; } - + void bgp_sync_init (struct peer *peer) { @@ -389,9 +395,9 @@ bgp_sync_init (struct peer *peer) { sync = XCALLOC (MTYPE_BGP_SYNCHRONISE, sizeof (struct bgp_synchronize)); - FIFO_INIT (&sync->update); - FIFO_INIT (&sync->withdraw); - FIFO_INIT (&sync->withdraw_low); + BGP_ADV_FIFO_INIT (&sync->update); + BGP_ADV_FIFO_INIT (&sync->withdraw); + BGP_ADV_FIFO_INIT (&sync->withdraw_low); peer->sync[afi][safi] = sync; peer->hash[afi][safi] = hash_create (baa_hash_key, baa_hash_cmp); } diff --git a/bgpd/bgp_advertise.h b/bgpd/bgp_advertise.h index 4ebde907d..e2857a3bf 100644 --- a/bgpd/bgp_advertise.h +++ b/bgpd/bgp_advertise.h @@ -21,11 +21,14 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #ifndef _QUAGGA_BGP_ADVERTISE_H #define _QUAGGA_BGP_ADVERTISE_H +#include + /* BGP advertise FIFO. */ struct bgp_advertise_fifo { struct bgp_advertise *next; struct bgp_advertise *prev; + u_int32_t count; }; /* BGP advertise attribute. */ @@ -44,7 +47,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 +100,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 { \ @@ -127,6 +132,24 @@ struct bgp_synchronize #define BGP_ADJ_OUT_ADD(N,A) BGP_INFO_ADD(N,A,adj_out) #define BGP_ADJ_OUT_DEL(N,A) BGP_INFO_DEL(N,A,adj_out) +#define BGP_ADV_FIFO_ADD(F, N) \ + do { \ + FIFO_ADD((F), (N)); \ + (F)->count++; \ + } while (0) + +#define BGP_ADV_FIFO_DEL(F, N) \ + do { \ + FIFO_DEL((N)); \ + (F)->count--; \ + } while (0) + +#define BGP_ADV_FIFO_INIT(F) \ + do { \ + FIFO_INIT((F)); \ + (F)->count = 0; \ + } while (0) + /* Prototypes. */ extern void bgp_adj_out_set (struct bgp_node *, struct peer *, struct prefix *, struct attr *, afi_t, safi_t, struct bgp_info *); @@ -138,7 +161,7 @@ extern int bgp_adj_out_lookup (struct peer *, struct prefix *, afi_t, safi_t, struct bgp_node *); extern void bgp_adj_in_set (struct bgp_node *, struct peer *, struct attr *); -extern void bgp_adj_in_unset (struct bgp_node *, struct peer *); +extern int bgp_adj_in_unset (struct bgp_node *, struct peer *); extern void bgp_adj_in_remove (struct bgp_node *, struct bgp_adj_in *); extern struct bgp_advertise * diff --git a/bgpd/bgp_aspath.c b/bgpd/bgp_aspath.c index 776c7127e..d813bfbab 100644 --- a/bgpd/bgp_aspath.c +++ b/bgpd/bgp_aspath.c @@ -29,12 +29,13 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "log.h" #include "stream.h" #include "jhash.h" +#include "filter.h" #include "bgpd/bgpd.h" #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,17 +91,18 @@ 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) { - return (XCALLOC (MTYPE_AS_SEG_DATA, ASSEGMENT_DATA_SIZE (num, 1))); + return (XMALLOC (MTYPE_AS_SEG_DATA, ASSEGMENT_DATA_SIZE (num, 1))); } static void assegment_data_free (as_t *asdata) { - XFREE (MTYPE_AS_SEG_DATA,asdata); + XFREE (MTYPE_AS_SEG_DATA, asdata); } /* Get a new segment. Note that 0 is an allowed length, @@ -131,7 +133,7 @@ assegment_free (struct assegment *seg) return; if (seg->as) - XFREE (MTYPE_AS_SEG_DATA, seg->as); + assegment_data_free (seg->as); memset (seg, 0xfe, sizeof(struct assegment)); XFREE (MTYPE_AS_SEG, seg); @@ -191,6 +193,7 @@ static struct assegment * assegment_prepend_asns (struct assegment *seg, as_t asnum, int num) { as_t *newas; + int i; if (!num) return seg; @@ -198,23 +201,18 @@ assegment_prepend_asns (struct assegment *seg, as_t asnum, int num) if (num >= AS_SEGMENT_MAX) return seg; /* we don't do huge prepends */ - newas = assegment_data_new (seg->length + num); + if ((newas = assegment_data_new (seg->length + num)) == NULL) + return seg; - if (newas) - { - int i; - for (i = 0; i < num; i++) - newas[i] = asnum; - - memcpy (newas + num, seg->as, ASSEGMENT_DATA_SIZE (seg->length, 1)); - XFREE (MTYPE_AS_SEG_DATA, seg->as); - seg->as = newas; - seg->length += num; - return seg; - } + for (i = 0; i < num; i++) + newas[i] = asnum; - assegment_free_all (seg); - return NULL; + memcpy (newas + num, seg->as, ASSEGMENT_DATA_SIZE (seg->length, 1)); + assegment_data_free (seg->as); + seg->as = newas; + seg->length += num; + + return seg; } /* append given array of as numbers to the segment */ @@ -318,7 +316,7 @@ assegment_normalise (struct assegment *head) } return head; } - + static struct aspath * aspath_new (void) { @@ -429,7 +427,7 @@ aspath_count_confeds (struct aspath *aspath) } unsigned int -aspath_count_hops (struct aspath *aspath) +aspath_count_hops (const struct aspath *aspath) { int count = 0; struct assegment *seg = aspath->segments; @@ -477,15 +475,27 @@ aspath_highest (struct aspath *aspath) while (seg) { for (i = 0; i < seg->length; i++) - if (seg->as[i] > highest - && (seg->as[i] < BGP_PRIVATE_AS_MIN - || seg->as[i] > BGP_PRIVATE_AS_MAX)) + if (seg->as[i] > highest + && !BGP_AS_IS_PRIVATE(seg->as[i])) highest = seg->as[i]; seg = seg->next; } 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) @@ -504,7 +514,7 @@ aspath_has_as4 (struct aspath *aspath) } /* Convert aspath structure to string expression. */ -static char * +static void aspath_make_str_count (struct aspath *as) { struct assegment *seg; @@ -515,9 +525,10 @@ aspath_make_str_count (struct aspath *as) /* Empty aspath. */ if (!as->segments) { - str_buf = XMALLOC (MTYPE_AS_STR, 1); - str_buf[0] = '\0'; - return str_buf; + as->str = XMALLOC (MTYPE_AS_STR, 1); + as->str[0] = '\0'; + as->str_len = 0; + return; } seg = as->segments; @@ -554,7 +565,9 @@ aspath_make_str_count (struct aspath *as) break; default: XFREE (MTYPE_AS_STR, str_buf); - return NULL; + as->str = NULL; + as->str_len = 0; + return; } /* We might need to increase str_buf, particularly if path has @@ -601,8 +614,10 @@ aspath_make_str_count (struct aspath *as) assert (len < str_size); str_buf[len] = '\0'; + as->str = str_buf; + as->str_len = len; - return str_buf; + return; } static void @@ -610,7 +625,7 @@ aspath_str_update (struct aspath *as) { if (as->str) XFREE (MTYPE_AS_STR, as->str); - as->str = aspath_make_str_count (as); + aspath_make_str_count (as); } /* Intern allocated AS path. */ @@ -618,21 +633,19 @@ struct aspath * aspath_intern (struct aspath *aspath) { struct aspath *find; - - /* Assert this AS path structure is not interned. */ + + /* Assert this AS path structure is not interned and has the string + representation built. */ assert (aspath->refcnt == 0); + assert (aspath->str); /* Check AS path hash. */ find = hash_get (ashash, aspath, hash_alloc_intern); - if (find != aspath) aspath_free (aspath); find->refcnt++; - if (! find->str) - find->str = aspath_make_str_count (find); - return find; } @@ -641,16 +654,25 @@ aspath_intern (struct aspath *aspath) struct aspath * aspath_dup (struct aspath *aspath) { + unsigned short buflen = aspath->str_len + 1; struct aspath *new; new = XCALLOC (MTYPE_AS_PATH, sizeof (struct aspath)); if (aspath->segments) new->segments = assegment_dup_all (aspath->segments); - else - new->segments = NULL; - new->str = aspath_make_str_count (aspath); + if (!aspath->str) + return new; + + new->str = XMALLOC (MTYPE_AS_STR, buflen); + new->str_len = aspath->str_len; + + /* copy the string data */ + if (aspath->str_len > 0) + memcpy (new->str, aspath->str, buflen); + else + new->str[0] = '\0'; return new; } @@ -658,19 +680,24 @@ aspath_dup (struct aspath *aspath) static void * aspath_hash_alloc (void *arg) { - struct aspath *aspath; + const struct aspath *aspath = arg; + struct aspath *new; - /* New aspath structure is needed. */ - aspath = aspath_dup (arg); - /* Malformed AS path value. */ + assert (aspath->str); if (! aspath->str) - { - aspath_free (aspath); - return NULL; - } + return NULL; - return aspath; + /* New aspath structure is needed. */ + new = XMALLOC (MTYPE_AS_PATH, sizeof (struct aspath)); + + /* Reuse segments and string representation */ + new->refcnt = 0; + new->segments = aspath->segments; + new->str = aspath->str; + new->str_len = aspath->str_len; + + return new; } /* parse as-segment byte stream in struct assegment */ @@ -794,19 +821,21 @@ aspath_parse (struct stream *s, size_t length, int use32bit) memset (&as, 0, sizeof (struct aspath)); if (assegments_parse (s, length, &as.segments, use32bit) < 0) return NULL; - + /* If already same aspath exist then return it. */ find = hash_get (ashash, &as, aspath_hash_alloc); - - /* aspath_hash_alloc dupes segments too. that probably could be - * optimised out. - */ - assegment_free_all (as.segments); - if (as.str) - XFREE (MTYPE_AS_STR, as.str); - - if (! find) - return NULL; + + /* bug! should not happen, let the daemon crash below */ + assert (find); + + /* if the aspath was already hashed free temporary memory. */ + if (find->refcnt) + { + assegment_free_all (as.segments); + /* aspath_key_make() always updates the string */ + XFREE (MTYPE_AS_STR, as.str); + } + find->refcnt++; return find; @@ -874,7 +903,7 @@ aspath_put (struct stream *s, struct aspath *as, int use32bit ) assegment_header_put (s, seg->type, AS_SEGMENT_MAX); assegment_data_put (s, seg->as, AS_SEGMENT_MAX, use32bit); written += AS_SEGMENT_MAX; - bytes += ASSEGMENT_SIZE (written, use32bit); + bytes += ASSEGMENT_SIZE (AS_SEGMENT_MAX, use32bit); } /* write the final segment, probably is also the first */ @@ -1035,6 +1064,9 @@ aspath_aggregate (struct aspath *as1, struct aspath *as2) if (match != minlen || match != seg1->length || seg1->length != seg2->length) break; + /* We are moving on to the next segment to reset match */ + else + match = 0; seg1 = seg1->next; seg2 = seg2->next; @@ -1069,6 +1101,128 @@ aspath_aggregate (struct aspath *as1, struct aspath *as2) return aspath; } +/* Modify as1 using as2 for aggregation for multipath, keeping the + * AS-Path length the same, and so minimising change to the preference + * of the mpath aggregrate route. + */ +struct aspath * +aspath_aggregate_mpath (struct aspath *as1, struct aspath *as2) +{ + int i; + int minlen; + int match; + int from1,from2; + struct assegment *seg1 = as1->segments; + struct assegment *seg2 = as2->segments; + struct aspath *aspath = NULL; + struct assegment *asset; + struct assegment *prevseg = NULL; + + match = 0; + minlen = 0; + aspath = NULL; + asset = NULL; + + /* First of all check common leading sequence. */ + while (seg1 && seg2) + { + /* Check segment type. */ + if (seg1->type != seg2->type) + break; + + /* Minimum segment length. */ + minlen = min (seg1->length, seg2->length); + + for (match = 0; match < minlen; match++) + if (seg1->as[match] != seg2->as[match]) + break; + + if (match) + { + struct assegment *seg = assegment_new (seg1->type, 0); + + seg = assegment_append_asns (seg, seg1->as, match); + + if (! aspath) + { + aspath = aspath_new (); + aspath->segments = seg; + } + else + prevseg->next = seg; + + prevseg = seg; + } + + if (match != minlen || match != seg1->length + || seg1->length != seg2->length) + break; + + seg1 = seg1->next; + seg2 = seg2->next; + } + + if (! aspath) + aspath = aspath_new(); + + /* Make as-set using rest of all information. */ + from1 = from2 = match; + while (seg1 || seg2) + { + if (seg1) + { + if (seg1->type == AS_SEQUENCE) + { + asset = aspath_aggregate_as_set_add (aspath, asset, seg1->as[from1]); + from1++; + if (from1 >= seg1->length) + { + from1 = 0; + seg1 = seg1->next; + } + } + else + { + for (i = from1; i < seg1->length; i++) + asset = aspath_aggregate_as_set_add (aspath, asset, seg1->as[i]); + + from1 = 0; + seg1 = seg1->next; + } + } + + if (seg2) + { + if (seg2->type == AS_SEQUENCE) + { + asset = aspath_aggregate_as_set_add (aspath, asset, seg2->as[from2]); + from2++; + if (from2 >= seg2->length) + { + from2 = 0; + seg2 = seg2->next; + } + } + else + { + for (i = from2; i < seg2->length; i++) + asset = aspath_aggregate_as_set_add (aspath, asset, seg2->as[i]); + + from2 = 0; + seg2 = seg2->next; + } + } + + if (asset->length == 1) + asset->type = AS_SEQUENCE; + asset = NULL; + } + + assegment_normalise (aspath->segments); + aspath_str_update (aspath); + return aspath; +} + /* When a BGP router receives an UPDATE with an MP_REACH_NLRI attribute, check the leftmost AS number in the AS_PATH attribute is or not the peer's AS number. */ @@ -1128,8 +1282,7 @@ aspath_private_as_check (struct aspath *aspath) for (i = 0; i < seg->length; i++) { - if ( (seg->as[i] < BGP_PRIVATE_AS_MIN) - || (seg->as[i] > BGP_PRIVATE_AS_MAX) ) + if (!BGP_AS_IS_PRIVATE(seg->as[i])) return 0; } seg = seg->next; @@ -1226,7 +1379,18 @@ aspath_prepend (struct aspath *as1, struct aspath *as2) /* Delete any AS_CONFED_SEQUENCE segment from as2. */ if (seg1->type == AS_SEQUENCE && seg2->type == AS_CONFED_SEQUENCE) as2 = aspath_delete_confed_seq (as2); - + + /* as2 may have been updated */ + seg2 = as2->segments; + + /* as2 may be empty now due to aspath_delete_confed_seq, recheck */ + if (seg2 == NULL) + { + as2->segments = assegment_dup_all (as1->segments); + aspath_str_update (as2); + return as2; + } + /* Compare last segment type of as1 and first segment type of as2. */ if (seg1->type != seg2->type) return aspath_merge (as1, as2); @@ -1353,46 +1517,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; + unsigned 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 @@ -1400,8 +1568,8 @@ aspath_add_seq (struct aspath *aspath, as_t asno) int aspath_cmp_left (const struct aspath *aspath1, const struct aspath *aspath2) { - const struct assegment *seg1 = NULL; - const struct assegment *seg2 = NULL; + const struct assegment *seg1; + const struct assegment *seg2; if (!(aspath1 && aspath2)) return 0; @@ -1409,6 +1577,10 @@ aspath_cmp_left (const struct aspath *aspath1, const struct aspath *aspath2) seg1 = aspath1->segments; seg2 = aspath2->segments; + /* If both paths are originated in this AS then we do want to compare MED */ + if (!seg1 && !seg2) + return 1; + /* find first non-confed segments for each */ while (seg1 && ((seg1->type == AS_CONFED_SEQUENCE) || (seg1->type == AS_CONFED_SET))) @@ -1591,7 +1763,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. */ @@ -1639,7 +1811,7 @@ aspath_empty_get (void) struct aspath *aspath; aspath = aspath_new (); - aspath->str = aspath_make_str_count (aspath); + aspath_make_str_count (aspath); return aspath; } @@ -1648,7 +1820,7 @@ aspath_count (void) { return ashash->count; } - + /* Theoretically, one as path can have: @@ -1798,28 +1970,28 @@ aspath_str2aspath (const char *str) } } - aspath->str = aspath_make_str_count (aspath); + aspath_make_str_count (aspath); return aspath; } - + /* Make hash value by raw aspath data. */ unsigned int aspath_key_make (void *p) { - struct aspath * aspath = (struct aspath *) p; + struct aspath *aspath = (struct aspath *) p; unsigned int key = 0; if (!aspath->str) aspath_str_update (aspath); - - key = jhash (aspath->str, strlen(aspath->str), 2334325); + + key = jhash (aspath->str, aspath->str_len, 2334325); return key; } /* If two aspath have same value then return 1 else return 0 */ -static int +int aspath_cmp (const void *arg1, const void *arg2) { const struct assegment *seg1 = ((const struct aspath *)arg1)->segments; @@ -1847,19 +2019,20 @@ 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 aspath_finish (void) { + hash_clean (ashash, (void (*)(void *))aspath_free); hash_free (ashash); ashash = NULL; if (snmp_stream) stream_free (snmp_stream); } - + /* return and as path value */ const char * aspath_print (struct aspath *as) @@ -1876,7 +2049,7 @@ aspath_print_vty (struct vty *vty, const char *format, struct aspath *as, const { assert (format); vty_out (vty, format, as->str); - if (strlen (as->str) && strlen (suffix)) + if (as->str_len && strlen (suffix)) vty_out (vty, "%s", suffix); } @@ -1887,7 +2060,7 @@ aspath_show_all_iterator (struct hash_backet *backet, struct vty *vty) as = (struct aspath *) backet->data; - vty_out (vty, "[%p:%u] (%ld) ", backet, backet->key, as->refcnt); + vty_out (vty, "[%p:%u] (%ld) ", (void *)backet, backet->key, as->refcnt); vty_out (vty, "%s%s", as->str, VTY_NEWLINE); } diff --git a/bgpd/bgp_aspath.h b/bgpd/bgp_aspath.h index d55f9ce62..c8f929f93 100644 --- a/bgpd/bgp_aspath.h +++ b/bgpd/bgp_aspath.h @@ -31,12 +31,20 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #define BGP_PRIVATE_AS_MIN 64512U #define BGP_PRIVATE_AS_MAX 65535U +/* Private 4 byte AS range defined in RFC6996. */ +#define BGP_PRIVATE_AS4_MIN 4200000000U +#define BGP_PRIVATE_AS4_MAX 4294967294U + /* we leave BGP_AS_MAX as the 16bit AS MAX number. */ #define BGP_AS_MAX 65535U #define BGP_AS4_MAX 4294967295U /* Transition 16Bit AS as defined by IANA */ #define BGP_AS_TRANS 23456U +#define BGP_AS_IS_PRIVATE(ASN) \ + (((ASN) >= BGP_PRIVATE_AS_MIN && (ASN) <= BGP_PRIVATE_AS_MAX) || \ + ((ASN) >= BGP_PRIVATE_AS4_MIN && (ASN) <= BGP_PRIVATE_AS4_MAX)) + /* AS_PATH segment data in abstracted form, no limit is placed on length */ struct assegment { @@ -58,6 +66,7 @@ struct aspath /* String expression of AS path. This string is used by vty output and AS path regular expression match. */ char *str; + unsigned short str_len; }; #define ASPATH_STR_DEFAULT_LEN 32 @@ -68,10 +77,13 @@ extern void aspath_finish (void); extern struct aspath *aspath_parse (struct stream *, size_t, int); extern struct aspath *aspath_dup (struct aspath *); extern struct aspath *aspath_aggregate (struct aspath *, struct aspath *); +extern struct aspath *aspath_aggregate_mpath (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 *); extern int aspath_cmp_left (const struct aspath *, const struct aspath *); extern int aspath_cmp_left_confed (const struct aspath *, const struct aspath *); extern struct aspath *aspath_delete_confed_seq (struct aspath *); @@ -91,10 +103,11 @@ extern int aspath_firstas_check (struct aspath *, as_t); extern int aspath_confed_check (struct aspath *); extern int aspath_left_confed_check (struct aspath *); extern unsigned long aspath_count (void); -extern unsigned int aspath_count_hops (struct aspath *); +extern unsigned int aspath_count_hops (const 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 0d82aba04..a79a03cc6 100644 --- a/bgpd/bgp_attr.c +++ b/bgpd/bgp_attr.c @@ -29,6 +29,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "log.h" #include "hash.h" #include "jhash.h" +#include "filter.h" #include "bgpd/bgpd.h" #include "bgpd/bgp_attr.h" @@ -38,7 +39,10 @@ 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" - +#include "bgpd/bgp_lcommunity.h" +#include "table.h" +#include "bgp_encap_types.h" + /* Attribute strings for logging. */ static const struct message attr_str [] = { @@ -61,8 +65,21 @@ static const struct message attr_str [] = { BGP_ATTR_AS4_PATH, "AS4_PATH" }, { BGP_ATTR_AS4_AGGREGATOR, "AS4_AGGREGATOR" }, { BGP_ATTR_AS_PATHLIMIT, "AS_PATHLIMIT" }, + { BGP_ATTR_ENCAP, "ENCAP" }, + { 21, ""}, + { 22, ""}, + { 23, ""}, + { 24, ""}, + { 25, ""}, + { 26, ""}, + { 27, ""}, + { 28, ""}, + { 29, ""}, + { 30, ""}, + { 31, ""}, + { BGP_ATTR_LARGE_COMMUNITIES, "LARGE_COMMUNITY" } }; -static const int attr_str_max = sizeof(attr_str)/sizeof(attr_str[0]); +static const int attr_str_max = array_size(attr_str); static const struct message attr_flag_str[] = { @@ -72,9 +89,7 @@ static const struct message attr_flag_str[] = /* bgp_attr_flags_diagnose() relies on this bit being last in this list */ { BGP_ATTR_FLAG_EXTLEN, "Extended Length" }, }; -static const size_t attr_flag_str_max = - sizeof (attr_flag_str) / sizeof (attr_flag_str[0]); - + static struct hash *cluster_hash; static void * @@ -205,10 +220,110 @@ cluster_init (void) static void cluster_finish (void) { + hash_clean (cluster_hash, (void (*)(void *))cluster_free); hash_free (cluster_hash); cluster_hash = NULL; } - + +struct bgp_attr_encap_subtlv * +encap_tlv_dup(struct bgp_attr_encap_subtlv *orig) +{ + struct bgp_attr_encap_subtlv *new; + struct bgp_attr_encap_subtlv *tail; + struct bgp_attr_encap_subtlv *p; + + for (p = orig, tail = new = NULL; p; p = p->next) { + int size = sizeof(struct bgp_attr_encap_subtlv) - 1 + p->length; + if (tail) { + tail->next = XCALLOC(MTYPE_ENCAP_TLV, size); + tail = tail->next; + } else { + tail = new = XCALLOC(MTYPE_ENCAP_TLV, size); + } + assert(tail); + memcpy(tail, p, size); + tail->next = NULL; + } + + return new; +} + +static void +encap_free(struct bgp_attr_encap_subtlv *p) +{ + struct bgp_attr_encap_subtlv *next; + while (p) { + next = p->next; + p->next = NULL; + XFREE(MTYPE_ENCAP_TLV, p); + p = next; + } +} + +void +bgp_attr_flush_encap(struct attr *attr) +{ + if (!attr || !attr->extra) + return; + + if (attr->extra->encap_subtlvs) { + encap_free(attr->extra->encap_subtlvs); + attr->extra->encap_subtlvs = NULL; + } +} + +/* + * Compare encap sub-tlv chains + * + * 1 = equivalent + * 0 = not equivalent + * + * This algorithm could be made faster if needed + */ +static int +encap_same(struct bgp_attr_encap_subtlv *h1, struct bgp_attr_encap_subtlv *h2) +{ + struct bgp_attr_encap_subtlv *p; + struct bgp_attr_encap_subtlv *q; + + if (!h1 && !h2) + return 1; + if (h1 && !h2) + return 0; + if (!h1 && h2) + return 0; + if (h1 == h2) + return 1; + + for (p = h1; p; p = p->next) { + for (q = h2; q; q = q->next) { + if ((p->type == q->type) && + (p->length == q->length) && + !memcmp(p->value, q->value, p->length)) { + + break; + } + } + if (!q) + return 0; + } + + for (p = h2; p; p = p->next) { + for (q = h1; q; q = q->next) { + if ((p->type == q->type) && + (p->length == q->length) && + !memcmp(p->value, q->value, p->length)) { + + break; + } + } + if (!q) + return 0; + } + + return 1; +} + /* Unknown transit attribute. */ static struct hash *transit_hash; @@ -281,10 +396,11 @@ transit_init (void) static void transit_finish (void) { + hash_clean (transit_hash, (void (*)(void *))transit_free); hash_free (transit_hash); transit_hash = NULL; } - + /* Attribute hash routines. */ static struct hash *attrhash; @@ -299,6 +415,10 @@ bgp_attr_extra_free (struct attr *attr) { if (attr->extra) { + if (attr->extra->encap_subtlvs) { + encap_free(attr->extra->encap_subtlvs); + attr->extra->encap_subtlvs = NULL; + } XFREE (MTYPE_ATTR_EXTRA, attr->extra); attr->extra = NULL; } @@ -319,11 +439,35 @@ bgp_attr_extra_get (struct attr *attr) void 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; + if (orig->extra->encap_subtlvs) { + new->extra->encap_subtlvs = encap_tlv_dup(orig->extra->encap_subtlvs); + } + } + } + else if (orig->extra) { new->extra = bgp_attr_extra_new(); *new->extra = *orig->extra; + if (orig->extra->encap_subtlvs) { + new->extra->encap_subtlvs = encap_tlv_dup(orig->extra->encap_subtlvs); + } } } @@ -342,7 +486,8 @@ attr_unknown_count (void) unsigned int attrhash_key_make (void *p) { - const struct attr * attr = (struct attr *) p; + const struct attr *attr = (struct attr *) p; + const struct attr_extra *extra = attr->extra; uint32_t key = 0; #define MIX(val) key = jhash_1word(val, key) @@ -356,12 +501,14 @@ attrhash_key_make (void *p) key += attr->med; key += attr->local_pref; - if (attr->extra) + if (extra) { - MIX(attr->extra->aggregator_as); - MIX(attr->extra->aggregator_addr.s_addr); - MIX(attr->extra->weight); - MIX(attr->extra->mp_nexthop_global_in.s_addr); + MIX(extra->aggregator_as); + MIX(extra->aggregator_addr.s_addr); + MIX(extra->weight); + MIX(extra->mp_nexthop_global_in.s_addr); + MIX(extra->originator_id.s_addr); + MIX(extra->tag); } if (attr->aspath) @@ -369,20 +516,20 @@ attrhash_key_make (void *p) if (attr->community) MIX(community_hash_make (attr->community)); - if (attr->extra) + if (extra) { - if (attr->extra->ecommunity) - MIX(ecommunity_hash_make (attr->extra->ecommunity)); - if (attr->extra->cluster) - MIX(cluster_hash_key_make (attr->extra->cluster)); - if (attr->extra->transit) - MIX(transit_hash_key_make (attr->extra->transit)); + if (extra->lcommunity) + MIX(lcommunity_hash_make (extra->lcommunity)); + if (extra->ecommunity) + MIX(ecommunity_hash_make (extra->ecommunity)); + if (extra->cluster) + MIX(cluster_hash_key_make (extra->cluster)); + if (extra->transit) + MIX(transit_hash_key_make (extra->transit)); -#ifdef HAVE_IPV6 - MIX(attr->extra->mp_nexthop_len); - key = jhash(attr->extra->mp_nexthop_global.s6_addr, 16, key); - key = jhash(attr->extra->mp_nexthop_local.s6_addr, 16, key); -#endif /* HAVE_IPV6 */ + MIX(extra->mp_nexthop_len); + key = jhash(extra->mp_nexthop_global.s6_addr, 16, key); + key = jhash(extra->mp_nexthop_local.s6_addr, 16, key); } return key; @@ -409,15 +556,18 @@ attrhash_cmp (const void *p1, const void *p2) && ae1->aggregator_as == ae2->aggregator_as && ae1->aggregator_addr.s_addr == ae2->aggregator_addr.s_addr && ae1->weight == ae2->weight -#ifdef HAVE_IPV6 + && ae1->tag == ae2->tag && ae1->mp_nexthop_len == ae2->mp_nexthop_len && IPV6_ADDR_SAME (&ae1->mp_nexthop_global, &ae2->mp_nexthop_global) && IPV6_ADDR_SAME (&ae1->mp_nexthop_local, &ae2->mp_nexthop_local) -#endif /* HAVE_IPV6 */ && IPV4_ADDR_SAME (&ae1->mp_nexthop_global_in, &ae2->mp_nexthop_global_in) && ae1->ecommunity == ae2->ecommunity + && ae1->lcommunity == ae2->lcommunity && ae1->cluster == ae2->cluster - && ae1->transit == ae2->transit) + && ae1->transit == ae2->transit + && (ae1->encap_tunneltype == ae2->encap_tunneltype) + && encap_same(ae1->encap_subtlvs, ae2->encap_subtlvs) + && IPV4_ADDR_SAME (&ae1->originator_id, &ae2->originator_id)) return 1; else if (ae1 || ae2) return 0; @@ -434,9 +584,20 @@ attrhash_init (void) attrhash = hash_create (attrhash_key_make, attrhash_cmp); } +/* + * special for hash_clean below + */ +static void +attr_vfree (void *attr) +{ + bgp_attr_extra_free ((struct attr *)attr); + XFREE (MTYPE_ATTR, attr); +} + static void attrhash_finish (void) { + hash_clean(attrhash, attr_vfree); hash_free (attrhash); attrhash = NULL; } @@ -471,6 +632,10 @@ bgp_attr_hash_alloc (void *p) { attr->extra = bgp_attr_extra_new (); *attr->extra = *val->extra; + + if (attr->extra->encap_subtlvs) { + attr->extra->encap_subtlvs = encap_tlv_dup(attr->extra->encap_subtlvs); + } } attr->refcnt = 0; return attr; @@ -509,6 +674,13 @@ bgp_attr_intern (struct attr *attr) attre->ecommunity->refcnt++; } + if (attre->lcommunity) + { + if (! attre->lcommunity->refcnt) + attre->lcommunity = lcommunity_intern (attre->lcommunity); + else + attre->lcommunity->refcnt++; + } if (attre->cluster) { if (! attre->cluster->refcnt) @@ -544,10 +716,9 @@ bgp_attr_default_set (struct attr *attr, u_char origin) attr->aspath = aspath_empty (); attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_AS_PATH); attr->extra->weight = BGP_ATTR_DEFAULT_WEIGHT; + attr->extra->tag = 0; attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_NEXT_HOP); -#ifdef HAVE_IPV6 attr->extra->mp_nexthop_len = IPV6_MAX_BYTELEN; -#endif return attr; } @@ -559,10 +730,10 @@ bgp_attr_default_intern (u_char origin) { struct attr attr; struct attr *new; - + memset (&attr, 0, sizeof (struct attr)); bgp_attr_extra_get (&attr); - + bgp_attr_default_set(&attr, origin); new = bgp_attr_intern (&attr); @@ -572,18 +743,21 @@ bgp_attr_default_intern (u_char origin) return new; } +/* Create the attributes for an aggregate */ struct attr * bgp_attr_aggregate_intern (struct bgp *bgp, u_char origin, struct aspath *aspath, - struct community *community, int as_set) + struct community *community, int as_set, + u_char atomic_aggregate) { struct attr attr; struct attr *new; - struct attr_extra *attre; + struct attr_extra attre; memset (&attr, 0, sizeof (struct attr)); - attre = bgp_attr_extra_get (&attr); - + memset (&attre, 0, sizeof (struct attr_extra)); + attr.extra = &attre; + /* Origin attribute. */ attr.origin = origin; attr.flag |= ATTR_FLAG_BIT (BGP_ATTR_ORIGIN); @@ -604,22 +778,20 @@ bgp_attr_aggregate_intern (struct bgp *bgp, u_char origin, attr.flag |= ATTR_FLAG_BIT (BGP_ATTR_COMMUNITIES); } - attre->weight = BGP_ATTR_DEFAULT_WEIGHT; -#ifdef HAVE_IPV6 - attre->mp_nexthop_len = IPV6_MAX_BYTELEN; -#endif - if (! as_set) + attre.weight = BGP_ATTR_DEFAULT_WEIGHT; + attre.mp_nexthop_len = IPV6_MAX_BYTELEN; + + if (! as_set || atomic_aggregate) attr.flag |= ATTR_FLAG_BIT (BGP_ATTR_ATOMIC_AGGREGATE); attr.flag |= ATTR_FLAG_BIT (BGP_ATTR_AGGREGATOR); if (CHECK_FLAG (bgp->config, BGP_CONFIG_CONFEDERATION)) - attre->aggregator_as = bgp->confed_id; + attre.aggregator_as = bgp->confed_id; else - attre->aggregator_as = bgp->as; - attre->aggregator_addr = bgp->router_id; + attre.aggregator_as = bgp->as; + attre.aggregator_addr = bgp->router_id; new = bgp_attr_intern (&attr); - bgp_attr_extra_free (&attr); - + aspath_unintern (&new->aspath); return new; } @@ -631,21 +803,25 @@ 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->lcommunity) + lcommunity_unintern (&attr->extra->lcommunity); + UNSET_FLAG(attr->flag, ATTR_FLAG_BIT (BGP_ATTR_LARGE_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); @@ -654,53 +830,70 @@ bgp_attr_unintern_sub (struct attr *attr) /* Free bgp attribute and aspath. */ void -bgp_attr_unintern (struct attr **attr) +bgp_attr_unintern (struct attr **pattr) { + struct attr *attr = *pattr; struct attr *ret; struct attr tmp; + struct attr_extra tmp_extra; /* Decrement attribute reference. */ - (*attr)->refcnt--; + attr->refcnt--; - tmp = *(*attr); + tmp = *attr; - if ((*attr)->extra) + if (attr->extra) { - tmp.extra = bgp_attr_extra_new (); - memcpy (tmp.extra, (*attr)->extra, sizeof (struct attr_extra)); + tmp.extra = &tmp_extra; + memcpy (tmp.extra, attr->extra, sizeof (struct attr_extra)); } /* If reference becomes zero then free attribute object. */ - if ((*attr)->refcnt == 0) - { - ret = hash_release (attrhash, *attr); + if (attr->refcnt == 0) + { + ret = hash_release (attrhash, attr); assert (ret != NULL); - bgp_attr_extra_free (*attr); - XFREE (MTYPE_ATTR, *attr); - *attr = NULL; + bgp_attr_extra_free (attr); + XFREE (MTYPE_ATTR, attr); + *pattr = NULL; } bgp_attr_unintern_sub (&tmp); - bgp_attr_extra_free (&tmp); } void bgp_attr_flush (struct attr *attr) { if (attr->aspath && ! attr->aspath->refcnt) - aspath_free (attr->aspath); + { + aspath_free (attr->aspath); + attr->aspath = NULL; + } if (attr->community && ! attr->community->refcnt) - community_free (attr->community); + { + community_free (attr->community); + attr->community = NULL; + } if (attr->extra) { struct attr_extra *attre = attr->extra; if (attre->ecommunity && ! attre->ecommunity->refcnt) ecommunity_free (&attre->ecommunity); + if (attre->lcommunity && ! attre->lcommunity->refcnt) + lcommunity_free (&attre->lcommunity); if (attre->cluster && ! attre->cluster->refcnt) - cluster_free (attre->cluster); + { + cluster_free (attre->cluster); + attre->cluster = NULL; + } if (attre->transit && ! attre->transit->refcnt) - transit_free (attre->transit); + { + transit_free (attre->transit); + attre->transit = NULL; + } + encap_free(attre->encap_subtlvs); + attre->encap_subtlvs = NULL; } } @@ -722,7 +915,7 @@ bgp_attr_malformed (struct bgp_attr_parser_args *args, u_char subcode, u_char *notify_datap = (length > 0 ? args->startp : NULL); /* Only relax error handling for eBGP peers */ - if (peer_sort (peer) != BGP_PEER_EBGP) + if (peer->sort != BGP_PEER_EBGP) { bgp_notify_send_with_data (peer, BGP_NOTIFY_UPDATE_ERR, subcode, notify_datap, length); @@ -777,7 +970,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. @@ -838,9 +1031,9 @@ const u_int8_t attr_flags_values [] = { [BGP_ATTR_EXT_COMMUNITIES] = BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS, [BGP_ATTR_AS4_PATH] = BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS, [BGP_ATTR_AS4_AGGREGATOR] = BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS, + [BGP_ATTR_LARGE_COMMUNITIES] = BGP_ATTR_FLAG_TRANS | BGP_ATTR_FLAG_OPTIONAL }; -static const size_t attr_flags_values_max = - sizeof (attr_flags_values) / sizeof (attr_flags_values[0]); +static const size_t attr_flags_values_max = array_size(attr_flags_values) - 1; static int bgp_attr_flag_invalid (struct bgp_attr_parser_args *args) @@ -996,11 +1189,9 @@ bgp_attr_aspath_check (struct peer *const peer, struct attr *const attr) struct bgp *bgp = peer->bgp; struct aspath *aspath; - bgp = peer->bgp; - /* Confederation sanity check. */ - if ((peer_sort (peer) == BGP_PEER_CONFED && ! aspath_left_confed_check (attr->aspath)) || - (peer_sort (peer) == BGP_PEER_EBGP && aspath_confed_check (attr->aspath))) + if ((peer->sort == BGP_PEER_CONFED && ! aspath_left_confed_check (attr->aspath)) || + (peer->sort == BGP_PEER_EBGP && aspath_confed_check (attr->aspath))) { zlog (peer->log, LOG_ERR, "Malformed AS path from %s", peer->host); bgp_notify_send (peer, BGP_NOTIFY_UPDATE_ERR, @@ -1011,7 +1202,7 @@ bgp_attr_aspath_check (struct peer *const peer, struct attr *const attr) /* First AS check for EBGP. */ if (bgp != NULL && bgp_flag_check (bgp, BGP_FLAG_ENFORCE_FIRST_AS)) { - if (peer_sort (peer) == BGP_PEER_EBGP + if (peer->sort == BGP_PEER_EBGP && ! aspath_firstas_check (attr->aspath, peer->as)) { zlog (peer->log, LOG_ERR, @@ -1092,10 +1283,11 @@ bgp_attr_nexthop (struct bgp_attr_parser_args *args) gets ignored in any of these cases. */ nexthop_n = stream_get_ipv4 (peer->ibuf); nexthop_h = ntohl (nexthop_n); - if (IPV4_NET0 (nexthop_h) || IPV4_NET127 (nexthop_h) || IPV4_CLASS_DE (nexthop_h)) + if ((IPV4_NET0 (nexthop_h) || IPV4_NET127 (nexthop_h) || IPV4_CLASS_DE (nexthop_h)) + && !BGP_DEBUG (allow_martians, ALLOW_MARTIANS)) /* loopbacks may be used in testing */ { 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, @@ -1155,7 +1347,7 @@ bgp_attr_local_pref (struct bgp_attr_parser_args *args) /* If it is contained in an UPDATE message that is received from an external peer, then this attribute MUST be ignored by the receiving speaker. */ - if (peer_sort (peer) == BGP_PEER_EBGP) + if (peer->sort == BGP_PEER_EBGP) { stream_forward_getp (peer->ibuf, length); return BGP_ATTR_PARSE_PROCEED; @@ -1267,7 +1459,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 +1549,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; } @@ -1447,7 +1649,6 @@ bgp_mp_reach_parse (struct bgp_attr_parser_args *args, safi_t safi; bgp_size_t nlri_len; size_t start; - int ret; struct stream *s; struct peer *const peer = args->peer; struct attr *const attr = args->attr; @@ -1465,7 +1666,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 +1680,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. */ @@ -1496,12 +1697,36 @@ bgp_mp_reach_parse (struct bgp_attr_parser_args *args, stream_getl (s); /* RD low */ stream_get (&attre->mp_nexthop_global_in, s, 4); break; -#ifdef HAVE_IPV6 + case 24: + { + u_int32_t rd_high __attribute__((unused)); + u_int32_t rd_low __attribute__((unused)); + + rd_high = stream_getl (s); + rd_low = stream_getl (s); + } + /* fall through */ case 16: stream_get (&attre->mp_nexthop_global, s, 16); break; case 32: + case 48: + if (attre->mp_nexthop_len == 48) { + u_int32_t rd_high __attribute__((unused)); + u_int32_t rd_low __attribute__((unused)); + + rd_high = stream_getl (s); + rd_low = stream_getl (s); + } stream_get (&attre->mp_nexthop_global, s, 16); + + if (attre->mp_nexthop_len == 48) { + u_int32_t rd_high __attribute__((unused)); + u_int32_t rd_low __attribute__((unused)); + + rd_high = stream_getl (s); + rd_low = stream_getl (s); + } stream_get (&attre->mp_nexthop_local, s, 16); if (! IN6_IS_ADDR_LINKLOCAL (&attre->mp_nexthop_local)) { @@ -1518,18 +1743,17 @@ bgp_mp_reach_parse (struct bgp_attr_parser_args *args, attre->mp_nexthop_len = 16; } break; -#endif /* HAVE_IPV6 */ 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,20 +1769,9 @@ 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; - } - - if (safi != SAFI_MPLS_LABELED_VPN) - { - ret = bgp_nlri_sanity_check (peer, afi, stream_pnt (s), nlri_len); - if (ret < 0) - { - zlog_info ("%s: (%s) NLRI doesn't pass sanity check", - __func__, peer->host); - return BGP_ATTR_PARSE_ERROR; - } + return BGP_ATTR_PARSE_ERROR_NOTIFYPLS; } - + mp_update->afi = afi; mp_update->safi = safi; mp_update->nlri = stream_pnt (s); @@ -1566,6 +1779,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 } @@ -1579,28 +1794,21 @@ bgp_mp_unreach_parse (struct bgp_attr_parser_args *args, afi_t afi; safi_t safi; 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); withdraw_len = length - BGP_MP_UNREACH_MIN_SIZE; - if (safi != SAFI_MPLS_LABELED_VPN) - { - ret = bgp_nlri_sanity_check (peer, afi, stream_pnt (s), withdraw_len); - if (ret < 0) - return BGP_ATTR_PARSE_ERROR; - } - mp_withdraw->afi = afi; mp_withdraw->safi = safi; mp_withdraw->nlri = stream_pnt (s); @@ -1608,6 +1816,39 @@ 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; +} + +/* Large Community attribute. */ +static bgp_attr_parse_ret_t +bgp_attr_large_community (struct bgp_attr_parser_args *args) +{ + struct peer *const peer = args->peer; + struct attr *const attr = args->attr; + const bgp_size_t length = args->length; + + if (length == 0) + { + if (attr->extra) + attr->extra->lcommunity = NULL; + /* Empty extcomm doesn't seem to be invalid per se */ + return BGP_ATTR_PARSE_PROCEED; + } + + (bgp_attr_extra_get (attr))->lcommunity = + lcommunity_parse ((u_int8_t *)stream_pnt (peer->ibuf), length); + /* XXX: fix ecommunity_parse to use stream API */ + stream_forward_getp (peer->ibuf, length); + + if (attr->extra && !attr->extra->lcommunity) + return bgp_attr_malformed (args, + BGP_NOTIFY_UPDATE_OPT_ATTR_ERR, + args->total); + + attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_LARGE_COMMUNITIES); + return BGP_ATTR_PARSE_PROCEED; } @@ -1632,7 +1873,7 @@ bgp_attr_ext_communities (struct bgp_attr_parser_args *args) /* XXX: fix ecommunity_parse to use stream API */ stream_forward_getp (peer->ibuf, length); - if (!attr->extra->ecommunity) + if (attr->extra && !attr->extra->ecommunity) return bgp_attr_malformed (args, BGP_NOTIFY_UPDATE_OPT_ATTR_ERR, args->total); @@ -1642,11 +1883,129 @@ bgp_attr_ext_communities (struct bgp_attr_parser_args *args) return BGP_ATTR_PARSE_PROCEED; } +/* Parse Tunnel Encap attribute in an UPDATE */ +static int +bgp_attr_encap( + uint8_t type, + struct peer *peer, /* IN */ + bgp_size_t length, /* IN: attr's length field */ + struct attr *attr, /* IN: caller already allocated */ + u_char flag, /* IN: attr's flags field */ + u_char *startp) +{ + bgp_size_t total; + struct attr_extra *attre = NULL; + struct bgp_attr_encap_subtlv *stlv_last = NULL; + uint16_t tunneltype; + + total = length + (CHECK_FLAG (flag, BGP_ATTR_FLAG_EXTLEN) ? 4 : 3); + + if (!CHECK_FLAG(flag, BGP_ATTR_FLAG_TRANS) + || !CHECK_FLAG(flag, BGP_ATTR_FLAG_OPTIONAL)) + { + zlog (peer->log, LOG_ERR, + "Tunnel Encap attribute flag isn't optional and transitive %d", flag); + bgp_notify_send_with_data (peer, + BGP_NOTIFY_UPDATE_ERR, + BGP_NOTIFY_UPDATE_ATTR_FLAG_ERR, + startp, total); + return -1; + } + + if (BGP_ATTR_ENCAP == type) { + /* read outer TLV type and length */ + uint16_t tlv_length; + + if (length < 4) { + zlog (peer->log, LOG_ERR, + "Tunnel Encap attribute not long enough to contain outer T,L"); + bgp_notify_send_with_data(peer, + BGP_NOTIFY_UPDATE_ERR, + BGP_NOTIFY_UPDATE_OPT_ATTR_ERR, + startp, total); + return -1; + } + tunneltype = stream_getw (BGP_INPUT (peer)); + tlv_length = stream_getw (BGP_INPUT (peer)); + length -= 4; + + if (tlv_length != length) { + zlog (peer->log, LOG_ERR, "%s: tlv_length(%d) != length(%d)", + __func__, tlv_length, length); + } + } + + while (length >= 4) { + uint16_t subtype = 0; + uint16_t sublength = 0; + struct bgp_attr_encap_subtlv *tlv; + + if (BGP_ATTR_ENCAP == type) { + subtype = stream_getc (BGP_INPUT (peer)); + sublength = stream_getc (BGP_INPUT (peer)); + length -= 2; + } + + if (sublength > length) { + zlog (peer->log, LOG_ERR, + "Tunnel Encap attribute sub-tlv length %d exceeds remaining length %d", + sublength, length); + bgp_notify_send_with_data (peer, + BGP_NOTIFY_UPDATE_ERR, + BGP_NOTIFY_UPDATE_OPT_ATTR_ERR, + startp, total); + return -1; + } + + /* alloc and copy sub-tlv */ + /* TBD make sure these are freed when attributes are released */ + tlv = XCALLOC (MTYPE_ENCAP_TLV, sizeof(struct bgp_attr_encap_subtlv)-1+sublength); + tlv->type = subtype; + tlv->length = sublength; + stream_get(tlv->value, peer->ibuf, sublength); + length -= sublength; + + /* attach tlv to encap chain */ + if (!attre) { + attre = bgp_attr_extra_get(attr); + if (BGP_ATTR_ENCAP == type) { + for (stlv_last = attre->encap_subtlvs; stlv_last && stlv_last->next; + stlv_last = stlv_last->next); + if (stlv_last) { + stlv_last->next = tlv; + } else { + attre->encap_subtlvs = tlv; + } + } + } else { + stlv_last->next = tlv; + } + stlv_last = tlv; + } + + if (attre && (BGP_ATTR_ENCAP == type)) { + attre->encap_tunneltype = tunneltype; + } + + if (length) { + /* spurious leftover data */ + zlog (peer->log, LOG_ERR, + "Tunnel Encap attribute length is bad: %d leftover octets", length); + bgp_notify_send_with_data (peer, + BGP_NOTIFY_UPDATE_ERR, + BGP_NOTIFY_UPDATE_OPT_ATTR_ERR, + startp, total); + return -1; + } + + return 0; +} + /* BGP unknown attribute treatment. */ static bgp_attr_parse_ret_t bgp_attr_unknown (struct bgp_attr_parser_args *args) { - bgp_size_t total; + bgp_size_t total = args->total; struct transit *transit; struct attr_extra *attre; struct peer *const peer = args->peer; @@ -1708,8 +2067,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) @@ -1725,7 +2134,7 @@ bgp_attr_parse (struct peer *peer, struct attr *attr, bgp_size_t size, /* same goes for as4_aggregator */ struct aspath *as4_path = NULL; as_t as4_aggregator = 0; - struct in_addr as4_aggregator_addr = { 0 }; + struct in_addr as4_aggregator_addr = { .s_addr = 0 }; /* Initialize bitmap. */ memset (seen, 0, BGP_ATTR_BITMAP_SIZE); @@ -1808,9 +2217,11 @@ bgp_attr_parse (struct peer *peer, struct attr *attr, bgp_size_t size, { zlog (peer->log, LOG_WARNING, "%s: BGP type %d length %d is too large, attribute total length is %d. attr_endp is %p. endp is %p", peer->host, type, length, size, attr_endp, endp); - bgp_notify_send (peer, - BGP_NOTIFY_UPDATE_ERR, - BGP_NOTIFY_UPDATE_ATTR_LENG_ERR); + zlog_warn ("%s: BGP type %d length %d is too large, attribute total length is %d. attr_endp is %p. endp is %p", peer->host, type, length, size, attr_endp, endp); + bgp_notify_send_with_data (peer, + BGP_NOTIFY_UPDATE_ERR, + BGP_NOTIFY_UPDATE_ATTR_LENG_ERR, + startp, attr_endp - startp); return BGP_ATTR_PARSE_ERROR; } @@ -1875,6 +2286,9 @@ bgp_attr_parse (struct peer *peer, struct attr *attr, bgp_size_t size, case BGP_ATTR_COMMUNITIES: ret = bgp_attr_community (&attr_args); break; + case BGP_ATTR_LARGE_COMMUNITIES: + ret = bgp_attr_large_community (&attr_args); + break; case BGP_ATTR_ORIGINATOR_ID: ret = bgp_attr_originator_id (&attr_args); break; @@ -1890,11 +2304,22 @@ bgp_attr_parse (struct peer *peer, struct attr *attr, bgp_size_t size, case BGP_ATTR_EXT_COMMUNITIES: ret = bgp_attr_ext_communities (&attr_args); break; + case BGP_ATTR_ENCAP: + ret = bgp_attr_encap (type, peer, length, attr, flag, startp); + break; default: ret = bgp_attr_unknown (&attr_args); 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 +2327,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 +2357,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 +2370,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 +2392,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 +2446,228 @@ 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. */ - if (! CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_NEXT_HOP))) - type = BGP_ATTR_NEXT_HOP; + stream_putw (s, afi); + stream_putc (s, (safi == SAFI_MPLS_VPN) ? SAFI_MPLS_LABELED_VPN : safi); - if (peer_sort (peer) == BGP_PEER_IBGP - && ! CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_LOCAL_PREF))) - type = BGP_ATTR_LOCAL_PREF; + /* Nexthop */ + switch (afi) + { + case AFI_IP: + switch (safi) + { + 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); /* RD = 0, per RFC */ + stream_putl (s, 0); + stream_put (s, &attr->extra->mp_nexthop_global_in, 4); + break; + case SAFI_ENCAP: + stream_putc (s, 4); + stream_put (s, &attr->extra->mp_nexthop_global_in, 4); + break; + case SAFI_UNICAST: /* invalid for IPv4 */ + default: + break; + } + break; + case AFI_IP6: + switch (safi) + { + case SAFI_UNICAST: + case SAFI_MULTICAST: + { + struct attr_extra *attre = attr->extra; - if (type) + 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); + } + break; + case SAFI_MPLS_VPN: + { + struct attr_extra *attre = attr->extra; + + assert (attr->extra); + if (attre->mp_nexthop_len == 16) { + stream_putc (s, 24); + stream_putl (s, 0); /* RD = 0, per RFC */ + stream_putl (s, 0); + stream_put (s, &attre->mp_nexthop_global, 16); + } else if (attre->mp_nexthop_len == 32) { + stream_putc (s, 48); + stream_putl (s, 0); /* RD = 0, per RFC */ + stream_putl (s, 0); + stream_put (s, &attre->mp_nexthop_global, 16); + stream_putl (s, 0); /* RD = 0, per RFC */ + stream_putl (s, 0); + stream_put (s, &attre->mp_nexthop_local, 16); + } + } + break; + case SAFI_ENCAP: + assert (attr->extra); + stream_putc (s, 16); + stream_put (s, &attr->extra->mp_nexthop_global, 16); + break; + default: + break; + } + break; + default: + break; + } + + /* 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) +{ + if (safi == SAFI_MPLS_VPN) { - 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; + /* 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)); } - return BGP_ATTR_PARSE_PROCEED; + else + stream_put_prefix (s, p); +} + +size_t +bgp_packet_mpattr_prefix_size (afi_t afi, safi_t safi, struct prefix *p) +{ + int size = PSIZE (p->prefixlen); + if (safi == SAFI_MPLS_VPN) + size += 88; + return size; +} + +/* + * Encodes the tunnel encapsulation attribute + */ +static void +bgp_packet_mpattr_tea( + struct bgp *bgp, + struct peer *peer, + struct stream *s, + struct attr *attr, + uint8_t attrtype) +{ + unsigned int attrlenfield = 0; + unsigned int attrhdrlen = 0; + struct bgp_attr_encap_subtlv *subtlvs; + struct bgp_attr_encap_subtlv *st; + const char *attrname; + + if (!attr || !attr->extra) + return; + + switch (attrtype) { + case BGP_ATTR_ENCAP: + attrname = "Tunnel Encap"; + subtlvs = attr->extra->encap_subtlvs; + + /* + * The tunnel encap attr has an "outer" tlv. + * T = tunneltype, + * L = total length of subtlvs, + * V = concatenated subtlvs. + */ + attrlenfield = 2 + 2; /* T + L */ + attrhdrlen = 1 + 1; /* subTLV T + L */ + break; + + default: + assert(0); + } + + + /* if no tlvs, don't make attr */ + if (subtlvs == NULL) + return; + + /* compute attr length */ + for (st = subtlvs; st; st = st->next) { + attrlenfield += (attrhdrlen + st->length); + } + + if (attrlenfield > 0xffff) { + zlog (peer->log, LOG_ERR, + "%s attribute is too long (length=%d), can't send it", + attrname, + attrlenfield); + return; + } + + if (attrlenfield > 0xff) { + /* 2-octet length field */ + stream_putc (s, + BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_EXTLEN); + stream_putc (s, attrtype); + stream_putw (s, attrlenfield & 0xffff); + } else { + /* 1-octet length field */ + stream_putc (s, BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_OPTIONAL); + stream_putc (s, attrtype); + stream_putc (s, attrlenfield & 0xff); + } + + if (attrtype == BGP_ATTR_ENCAP) { + /* write outer T+L */ + stream_putw(s, attr->extra->encap_tunneltype); + stream_putw(s, attrlenfield - 4); + } + + /* write each sub-tlv */ + for (st = subtlvs; st; st = st->next) { + if (attrtype == BGP_ATTR_ENCAP) { + stream_putc (s, st->type); + stream_putc (s, st->length); + } + stream_put (s, st->value, st->length); + } +} + +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); } - -int stream_put_prefix (struct stream *, struct prefix *); /* 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; @@ -2060,6 +2682,14 @@ 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)) + { + size_t mpattrlen_pos = 0; + 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); @@ -2069,7 +2699,7 @@ bgp_packet_attribute (struct bgp *bgp, struct peer *peer, /* AS path attribute. */ /* If remote-peer is EBGP */ - if (peer_sort (peer) == BGP_PEER_EBGP + if (peer->sort == BGP_PEER_EBGP && (! CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_AS_PATH_UNCHANGED) || attr->aspath->segments == NULL) && (! CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_RSERVER_CLIENT))) @@ -2085,12 +2715,19 @@ 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 (peer) == BGP_PEER_CONFED) + else if (peer->sort == BGP_PEER_CONFED) { /* A confed member, so we need to do the AS_CONFED_SEQUENCE thing */ aspath = aspath_dup (attr->aspath); @@ -2121,7 +2758,8 @@ bgp_packet_attribute (struct bgp *bgp, struct peer *peer, send_as4_path = 1; /* we'll do this later, at the correct place */ /* Nexthop attribute. */ - if (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_NEXT_HOP) && afi == AFI_IP) + if (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_NEXT_HOP) && afi == AFI_IP && + safi == SAFI_UNICAST) /* only write NH attr for unicast safi */ { stream_putc (s, BGP_ATTR_FLAG_TRANS); stream_putc (s, BGP_ATTR_NEXT_HOP); @@ -2147,8 +2785,8 @@ bgp_packet_attribute (struct bgp *bgp, struct peer *peer, } /* Local preference. */ - if (peer_sort (peer) == BGP_PEER_IBGP || - peer_sort (peer) == BGP_PEER_CONFED) + if (peer->sort == BGP_PEER_IBGP || + peer->sort == BGP_PEER_CONFED) { stream_putc (s, BGP_ATTR_FLAG_TRANS); stream_putc (s, BGP_ATTR_LOCAL_PREF); @@ -2220,10 +2858,32 @@ bgp_packet_attribute (struct bgp *bgp, struct peer *peer, stream_put (s, attr->community->val, attr->community->size * 4); } + /* + * Large Community attribute. + */ + if (attr->extra && + CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_SEND_LARGE_COMMUNITY) + && (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_LARGE_COMMUNITIES))) + { + if (attr->extra->lcommunity->size * 12 > 255) + { + stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_EXTLEN); + stream_putc (s, BGP_ATTR_LARGE_COMMUNITIES); + stream_putw (s, attr->extra->lcommunity->size * 12); + } + else + { + stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_TRANS); + stream_putc (s, BGP_ATTR_LARGE_COMMUNITIES); + stream_putc (s, attr->extra->lcommunity->size * 12); + } + stream_put (s, attr->extra->lcommunity->val, attr->extra->lcommunity->size * 12); + } + /* Route Reflector. */ - if (peer_sort (peer) == BGP_PEER_IBGP + if (peer->sort == BGP_PEER_IBGP && from - && peer_sort (from) == BGP_PEER_IBGP) + && from->sort == BGP_PEER_IBGP) { /* Originator ID. */ stream_putc (s, BGP_ATTR_FLAG_OPTIONAL); @@ -2261,96 +2921,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))) @@ -2359,8 +2929,8 @@ bgp_packet_attribute (struct bgp *bgp, struct peer *peer, assert (attre); - if (peer_sort (peer) == BGP_PEER_IBGP - || peer_sort (peer) == BGP_PEER_CONFED) + if (peer->sort == BGP_PEER_IBGP + || peer->sort == BGP_PEER_CONFED) { if (attre->ecommunity->size * 8 > 255) { @@ -2463,7 +3033,14 @@ bgp_packet_attribute (struct bgp *bgp, struct peer *peer, stream_putl (s, attr->extra->aggregator_as); stream_put_ipv4 (s, attr->extra->aggregator_addr.s_addr); } - + + if ((afi == AFI_IP || afi == AFI_IP6) && + (safi == SAFI_ENCAP || safi == SAFI_MPLS_VPN)) + { + /* Tunnel Encap attribute */ + bgp_packet_mpattr_tea(bgp, peer, s, attr, BGP_ATTR_ENCAP); + } + /* Unknown transit attribute. */ if (attr->extra && attr->extra->transit) stream_put (s, attr->extra->transit->val, attr->extra->transit->length); @@ -2472,50 +3049,35 @@ 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, family2afi (p->family)); + stream_putw (s, 0); /* Length of this attribute. */ - 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_putw (s, afi); + stream_putc (s, (safi == SAFI_MPLS_VPN) ? SAFI_MPLS_LABELED_VPN : safi); + return attrlen_pnt; +} - /* Set MP attribute length. */ - size = stream_get_endp (s) - attrlen_pnt - 1; - stream_putc_at (s, attrlen_pnt, size); +void +bgp_packet_mpunreach_prefix (struct stream *s, struct prefix *p, + afi_t afi, safi_t safi, struct prefix_rd *prd, + u_char *tag) +{ + bgp_packet_mpattr_prefix (s, afi, safi, p, prd, tag); +} - return stream_get_endp (s) - cp; +void +bgp_packet_mpunreach_end (struct stream *s, size_t attrlen_pnt) +{ + bgp_packet_mpattr_end (s, attrlen_pnt); } /* Initialization of attribute. */ @@ -2526,6 +3088,7 @@ bgp_attr_init (void) attrhash_init (); community_init (); ecommunity_init (); + lcommunity_init (); cluster_init (); transit_init (); } @@ -2537,6 +3100,7 @@ bgp_attr_finish (void) attrhash_finish (); community_finish (); ecommunity_finish (); + lcommunity_finish (); cluster_finish (); transit_finish (); } @@ -2575,9 +3139,7 @@ bgp_dump_routes_attr (struct stream *s, struct attr *attr, /* Nexthop attribute. */ /* If it's an IPv6 prefix, don't dump the IPv4 nexthop to save space */ if(prefix != NULL -#ifdef HAVE_IPV6 && prefix->family != AF_INET6 -#endif /* HAVE_IPV6 */ ) { stream_putc (s, BGP_ATTR_FLAG_TRANS); @@ -2641,7 +3203,25 @@ bgp_dump_routes_attr (struct stream *s, struct attr *attr, stream_put (s, attr->community->val, attr->community->size * 4); } -#ifdef HAVE_IPV6 + /* Large Community attribute. */ + if (attr->extra && attr->flag & ATTR_FLAG_BIT (BGP_ATTR_LARGE_COMMUNITIES)) + { + if (attr->extra->lcommunity->size * 12 > 255) + { + stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_EXTLEN); + stream_putc (s, BGP_ATTR_COMMUNITIES); + stream_putw (s, attr->extra->lcommunity->size * 12); + } + else + { + stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_TRANS); + stream_putc (s, BGP_ATTR_COMMUNITIES); + stream_putc (s, attr->extra->lcommunity->size * 12); + } + + stream_put (s, attr->extra->lcommunity->val, attr->extra->lcommunity->size * 12); + } + /* Add a MP_NLRI attribute to dump the IPv6 next hop */ if (prefix != NULL && prefix->family == AF_INET6 && attr->extra && (attr->extra->mp_nexthop_len == 16 || attr->extra->mp_nexthop_len == 32) ) @@ -2673,7 +3253,6 @@ bgp_dump_routes_attr (struct stream *s, struct attr *attr, /* Set MP attribute length. */ stream_putc_at (s, sizep, (stream_get_endp (s) - sizep) - 1); } -#endif /* HAVE_IPV6 */ /* Return total size of attribute. */ len = stream_get_endp (s) - cp - 2; diff --git a/bgpd/bgp_attr.h b/bgpd/bgp_attr.h index df87c8631..9ff074b2c 100644 --- a/bgpd/bgp_attr.h +++ b/bgpd/bgp_attr.h @@ -47,6 +47,13 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #define BGP_ATTR_MIN_LEN 3 /* Attribute flag, type length. */ #define BGP_ATTR_DEFAULT_WEIGHT 32768 +struct bgp_attr_encap_subtlv { + struct bgp_attr_encap_subtlv *next; /* for chaining */ + uint16_t type; + uint16_t length; + uint8_t value[1]; /* will be extended */ +}; + /* Additional/uncommon BGP attributes. * lazily allocated as and when a struct attr * requires it. @@ -54,13 +61,14 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA struct attr_extra { /* Multi-Protocol Nexthop, AFI IPv6 */ -#ifdef HAVE_IPV6 struct in6_addr mp_nexthop_global; struct in6_addr mp_nexthop_local; -#endif /* HAVE_IPV6 */ /* Extended Communities attribute. */ struct ecommunity *ecommunity; + + /* Large Communities attribute. */ + struct lcommunity *lcommunity; /* Route-Reflector Cluster attribute */ struct cluster_list *cluster; @@ -69,7 +77,6 @@ struct attr_extra struct transit *transit; struct in_addr mp_nexthop_global_in; - struct in_addr mp_nexthop_local_in; /* Aggregator Router ID attribute */ struct in_addr aggregator_addr; @@ -85,6 +92,12 @@ struct attr_extra /* MP Nexthop length */ u_char mp_nexthop_len; + + uint16_t encap_tunneltype; /* grr */ + struct bgp_attr_encap_subtlv *encap_subtlvs; /* rfc5512 */ + + /* route tag */ + route_tag_t tag; }; /* BGP core attribute structure. */ @@ -136,6 +149,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 +160,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 *); @@ -156,14 +171,12 @@ extern struct attr *bgp_attr_default_set (struct attr *attr, u_char); 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 *); + struct community *, int as_set, 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 +207,32 @@ 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 *); +extern struct bgp_attr_encap_subtlv * +encap_tlv_dup(struct bgp_attr_encap_subtlv *orig); + +extern void +bgp_attr_flush_encap(struct attr *attr); + +/** + * 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 size_t bgp_packet_mpattr_prefix_size(afi_t afi, safi_t safi, + struct prefix *p); +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_btoa.c b/bgpd/bgp_btoa.c index 7c7088145..b408efdd1 100644 --- a/bgpd/bgp_btoa.c +++ b/bgpd/bgp_btoa.c @@ -25,12 +25,37 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "log.h" #include "prefix.h" #include "command.h" +#include "memory.h" +#include "privs.h" +#include "filter.h" #include "bgpd/bgpd.h" #include "bgpd/bgp_dump.h" #include "bgpd/bgp_attr.h" #include "bgpd/bgp_aspath.h" +/* privileges */ +static zebra_capabilities_t _caps_p [] = +{ + ZCAP_BIND, + ZCAP_NET_RAW, + ZCAP_NET_ADMIN, +}; + +struct zebra_privs_t bgpd_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 = array_size(_caps_p), + .cap_num_i = 0, +}; + enum MRT_MSG_TYPES { MSG_NULL, MSG_START, /* sender is starting up */ @@ -47,7 +72,7 @@ enum MRT_MSG_TYPES { MSG_TABLE_DUMP /* routing table dump */ }; -int +static int attr_parse (struct stream *s, u_int16_t len) { u_int flag; @@ -57,14 +82,14 @@ attr_parse (struct stream *s, u_int16_t len) lim = s->getp + len; - printf ("attr_parse s->getp %d, len %d, lim %d\n", s->getp, len, lim); + printf ("attr_parse s->getp %zd, len %d, lim %d\n", s->getp, len, lim); while (s->getp < lim) { flag = stream_getc (s); type = stream_getc (s); - if (flag & ATTR_FLAG_EXTLEN) + if (flag & BGP_ATTR_FLAG_EXTLEN) length = stream_getw (s); else length = stream_getc (s); @@ -84,27 +109,22 @@ attr_parse (struct stream *s, u_int16_t len) break; case BGP_ATTR_AS_PATH: { - struct aspath aspath; - - aspath.data = (s->data + s->getp); - aspath.length = length; - aspath.str = aspath_make_str_count (&aspath); - printf ("ASPATH: %s\n", aspath.str); - free (aspath.str); - - stream_forward (s, length); + struct aspath *aspath; + + aspath = aspath_parse (s, length, 1); + printf ("ASPATH: %s\n", aspath->str); + aspath_free(aspath); } break; - case BGP_ATTR_NEXT_HOP: + case BGP_ATTR_NEXT_HOP: { struct in_addr nexthop; nexthop.s_addr = stream_get_ipv4 (s); printf ("NEXTHOP: %s\n", inet_ntoa (nexthop)); - /* stream_forward (s, length); */ } break; default: - stream_forward (s, length); + stream_getw_from (s, length); break; } } @@ -121,10 +141,10 @@ main (int argc, char **argv) time_t now; int type; int subtype; - int len; + size_t len; int source_as; int dest_as; - int ifindex; + ifindex_t ifindex; int family; struct in_addr sip; struct in_addr dip; @@ -150,7 +170,7 @@ main (int argc, char **argv) stream_reset (s); ret = fread (s->data, 12, 1, fp); - if (feof (fp)) + if (!ret || feof (fp)) { printf ("END OF FILE\n"); break; @@ -173,6 +193,8 @@ main (int argc, char **argv) if (type == MSG_PROTOCOL_BGP4MP) printf ("TYPE: BGP4MP"); + else if (type == MSG_PROTOCOL_BGP4MP_ET) + printf ("TYPE: BGP4MP_ET"); else if (type == MSG_TABLE_DUMP) printf ("TYPE: MSG_TABLE_DUMP"); else @@ -213,7 +235,7 @@ main (int argc, char **argv) } } - printf ("len: %d\n", len); + printf ("len: %zd\n", len); ret = fread (s->data + 12, len, 1, fp); if (feof (fp)) diff --git a/bgpd/bgp_clist.c b/bgpd/bgp_clist.c index 6c9976e3b..13bdf8e84 100644 --- a/bgpd/bgp_clist.c +++ b/bgpd/bgp_clist.c @@ -23,14 +23,16 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "command.h" #include "prefix.h" #include "memory.h" +#include "filter.h" #include "bgpd/bgpd.h" #include "bgpd/bgp_community.h" #include "bgpd/bgp_ecommunity.h" +#include "bgpd/bgp_lcommunity.h" #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 * @@ -43,6 +45,8 @@ community_list_master_lookup (struct community_list_handler *ch, int master) return &ch->community_list; case EXTCOMMUNITY_LIST_MASTER: return &ch->extcommunity_list; + case LARGE_COMMUNITY_LIST_MASTER: + return &ch->lcommunity_list; } return NULL; } @@ -64,6 +68,10 @@ community_entry_free (struct community_entry *entry) if (entry->u.com) community_free (entry->u.com); break; + case LARGE_COMMUNITY_LIST_STANDARD: + if (entry->u.lcom) + lcommunity_free (&entry->u.lcom); + break; case EXTCOMMUNITY_LIST_STANDARD: /* In case of standard extcommunity-list, configuration string is made by ecommunity_ecom2str(). */ @@ -74,6 +82,7 @@ community_entry_free (struct community_entry *entry) break; case COMMUNITY_LIST_EXPANDED: case EXTCOMMUNITY_LIST_EXPANDED: + case LARGE_COMMUNITY_LIST_EXPANDED: if (entry->config) XFREE (MTYPE_COMMUNITY_LIST_CONFIG, entry->config); if (entry->reg) @@ -262,7 +271,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, @@ -314,12 +323,17 @@ community_list_entry_lookup (struct community_list *list, const void *arg, if (community_cmp (entry->u.com, arg)) return entry; break; + case LARGE_COMMUNITY_LIST_STANDARD: + if (lcommunity_cmp (entry->u.lcom, arg)) + return entry; + break; case EXTCOMMUNITY_LIST_STANDARD: if (ecommunity_cmp (entry->u.ecom, arg)) return entry; break; case COMMUNITY_LIST_EXPANDED: case EXTCOMMUNITY_LIST_EXPANDED: + case LARGE_COMMUNITY_LIST_EXPANDED: if (strcmp (entry->config, arg) == 0) return entry; break; @@ -329,7 +343,100 @@ community_list_entry_lookup (struct community_list *list, const void *arg, } return NULL; } - + +static char * +community_str_get (struct community *com, int i) +{ + int len; + u_int32_t comval; + u_int16_t as; + u_int16_t val; + char *str; + char *pnt; + + memcpy (&comval, com_nthval (com, i), sizeof (u_int32_t)); + comval = ntohl (comval); + + switch (comval) + { + case COMMUNITY_INTERNET: + len = strlen (" internet"); + break; + case COMMUNITY_NO_EXPORT: + len = strlen (" no-export"); + break; + case COMMUNITY_NO_ADVERTISE: + len = strlen (" no-advertise"); + break; + case COMMUNITY_LOCAL_AS: + len = strlen (" local-AS"); + break; + default: + len = strlen (" 65536:65535"); + break; + } + + /* Allocate memory. */ + str = pnt = XMALLOC (MTYPE_COMMUNITY_STR, len); + + switch (comval) + { + case COMMUNITY_INTERNET: + strcpy (pnt, "internet"); + pnt += strlen ("internet"); + break; + case COMMUNITY_NO_EXPORT: + strcpy (pnt, "no-export"); + pnt += strlen ("no-export"); + break; + case COMMUNITY_NO_ADVERTISE: + strcpy (pnt, "no-advertise"); + pnt += strlen ("no-advertise"); + break; + case COMMUNITY_LOCAL_AS: + strcpy (pnt, "local-AS"); + pnt += strlen ("local-AS"); + break; + default: + as = (comval >> 16) & 0xFFFF; + val = comval & 0xFFFF; + sprintf (pnt, "%u:%d", as, val); + pnt += strlen (pnt); + break; + } + + *pnt = '\0'; + + return str; +} + +/* Internal function to perform regular expression match for + * * a single community. */ +static int +community_regexp_include (regex_t * reg, struct community *com, int i) +{ + char *str; + int rv; + + /* When there is no communities attribute it is treated as empty + * string. */ + if (com == NULL || com->size == 0) + str = XSTRDUP(MTYPE_COMMUNITY_STR, ""); + else + str = community_str_get (com, i); + + /* Regular expression match. */ + rv = regexec (reg, str, 0, NULL, 0); + + XFREE(MTYPE_COMMUNITY_STR, str); + + if (rv == 0) + return 1; + + /* No match. */ + return 0; +} + /* Internal function to perform regular expression match for community attribute. */ static int @@ -352,17 +459,61 @@ community_regexp_match (struct community *com, regex_t * reg) return 0; } +static char * +lcommunity_str_get (struct lcommunity *lcom, int i) +{ + struct lcommunity_val lcomval; + u_int32_t globaladmin; + u_int32_t localdata1; + u_int32_t localdata2; + char *str; + u_char *ptr; + char *pnt; + + ptr = lcom->val; + ptr += (i * LCOMMUNITY_SIZE); + + memcpy (&lcomval, ptr, LCOMMUNITY_SIZE); + + /* Allocate memory. 48 bytes taken off bgp_lcommunity.c */ + str = pnt = XMALLOC (MTYPE_LCOMMUNITY_STR, 48); + + ptr = (u_char *)lcomval.val; + globaladmin = (*ptr++ << 24); + globaladmin |= (*ptr++ << 16); + globaladmin |= (*ptr++ << 8); + globaladmin |= (*ptr++); + + localdata1 = (*ptr++ << 24); + localdata1 |= (*ptr++ << 16); + localdata1 |= (*ptr++ << 8); + localdata1 |= (*ptr++); + + localdata2 = (*ptr++ << 24); + localdata2 |= (*ptr++ << 16); + localdata2 |= (*ptr++ << 8); + localdata2 |= (*ptr++); + + sprintf (pnt, "%u:%u:%u", globaladmin, localdata1, localdata2); + pnt += strlen (pnt); + *pnt = '\0'; + + return str; +} + +/* Internal function to perform regular expression match for + * * a single community. */ static int -ecommunity_regexp_match (struct ecommunity *ecom, regex_t * reg) +lcommunity_regexp_include (regex_t * reg, struct lcommunity *lcom, int i) { const char *str; /* When there is no communities attribute it is treated as empty - string. */ - if (ecom == NULL || ecom->size == 0) + * string. */ + if (lcom == NULL || lcom->size == 0) str = ""; else - str = ecommunity_str (ecom); + str = lcommunity_str_get (lcom, i); /* Regular expression match. */ if (regexec (reg, str, 0, NULL, 0) == 0) @@ -372,52 +523,45 @@ ecommunity_regexp_match (struct ecommunity *ecom, regex_t * reg) return 0; } -/* Delete community attribute using regular expression match. Return - modified communites attribute. */ -static struct community * -community_regexp_delete (struct community *com, regex_t * reg) +static int +lcommunity_regexp_match (struct lcommunity *com, regex_t * reg) { - int i; - u_int32_t comval; - /* Maximum is "65535:65535" + '\0'. */ - char c[12]; const char *str; - if (!com) - return NULL; + /* When there is no communities attribute it is treated as empty + string. */ + if (com == NULL || com->size == 0) + str = ""; + else + str = lcommunity_str (com); - i = 0; - while (i < com->size) - { - memcpy (&comval, com_nthval (com, i), sizeof (u_int32_t)); - comval = ntohl (comval); + /* Regular expression match. */ + if (regexec (reg, str, 0, NULL, 0) == 0) + return 1; - switch (comval) - { - case COMMUNITY_INTERNET: - str = "internet"; - break; - case COMMUNITY_NO_EXPORT: - str = "no-export"; - break; - case COMMUNITY_NO_ADVERTISE: - str = "no-advertise"; - break; - case COMMUNITY_LOCAL_AS: - str = "local-AS"; - break; - default: - sprintf (c, "%d:%d", (comval >> 16) & 0xFFFF, comval & 0xFFFF); - str = c; - break; - } + /* No match. */ + return 0; +} - if (regexec (reg, str, 0, NULL, 0) == 0) - community_del_val (com, com_nthval (com, i)); - else - i++; - } - return com; + +static int +ecommunity_regexp_match (struct ecommunity *ecom, regex_t * reg) +{ + const char *str; + + /* When there is no communities attribute it is treated as empty + string. */ + if (ecom == NULL || ecom->size == 0) + str = ""; + else + str = ecommunity_str (ecom); + + /* Regular expression match. */ + if (regexec (reg, str, 0, NULL, 0) == 0) + return 1; + + /* No match. */ + return 0; } /* When given community attribute matches to the community-list return @@ -449,6 +593,30 @@ community_list_match (struct community *com, struct community_list *list) return 0; } +int +lcommunity_list_match (struct lcommunity *lcom, struct community_list *list) +{ + struct community_entry *entry; + + for (entry = list->head; entry; entry = entry->next) + { + if (entry->any) + return entry->direct == COMMUNITY_PERMIT ? 1 : 0; + + if (entry->style == LARGE_COMMUNITY_LIST_STANDARD) + { + if (lcommunity_match (lcom, entry->u.lcom)) + return entry->direct == COMMUNITY_PERMIT ? 1 : 0; + } + else if (entry->style == LARGE_COMMUNITY_LIST_EXPANDED) + { + if (lcommunity_regexp_match (lcom, entry->reg)) + return entry->direct == COMMUNITY_PERMIT ? 1 : 0; + } + } + return 0; +} + int ecommunity_list_match (struct ecommunity *ecom, struct community_list *list) { @@ -509,41 +677,63 @@ community_list_match_delete (struct community *com, struct community_list *list) { struct community_entry *entry; + u_int32_t val; + u_int32_t com_index_to_delete[com->size]; + int delete_index = 0; + int i; - for (entry = list->head; entry; entry = entry->next) + /* Loop over each community value and evaluate each against the + * community-list. If we need to delete a community value add its index to + * com_index_to_delete. + */ + for (i = 0; i < com->size; i++) { - if (entry->any) + val = community_val_get (com, i); + + for (entry = list->head; entry; entry = entry->next) { - if (entry->direct == COMMUNITY_PERMIT) + if (entry->any) { - /* This is a tricky part. Currently only - * route_set_community_delete() uses this function. In the - * function com->size is zero, it free the community - * structure. - */ - com->size = 0; + if (entry->direct == COMMUNITY_PERMIT) + { + com_index_to_delete[delete_index] = i; + delete_index++; + } + break; } - return com; - } - if ((entry->style == COMMUNITY_LIST_STANDARD) - && (community_include (entry->u.com, COMMUNITY_INTERNET) - || community_match (com, entry->u.com) )) - { + else if ((entry->style == COMMUNITY_LIST_STANDARD) + && (community_include (entry->u.com, COMMUNITY_INTERNET) + || community_include (entry->u.com, val) )) + { if (entry->direct == COMMUNITY_PERMIT) - community_delete (com, entry->u.com); - else - break; - } - else if ((entry->style == COMMUNITY_LIST_EXPANDED) - && community_regexp_match (com, entry->reg)) - { - if (entry->direct == COMMUNITY_PERMIT) - community_regexp_delete (com, entry->reg); - else - break; - } + { + com_index_to_delete[delete_index] = i; + delete_index++; + } + break; + } + + else if ((entry->style == COMMUNITY_LIST_EXPANDED) + && community_regexp_include (entry->reg, com, i)) + { + if (entry->direct == COMMUNITY_PERMIT) + { + com_index_to_delete[delete_index] = i; + delete_index++; + } + break; + } + } + } + + /* Delete all of the communities we flagged for deletion */ + for (i = delete_index-1; i >= 0; i--) + { + val = community_val_get (com, com_index_to_delete[i]); + community_del_val (com, &val); } + return com; } @@ -579,9 +769,17 @@ community_list_dup_check (struct community_list *list, if (ecommunity_cmp (entry->u.ecom, new->u.ecom)) return 1; break; + case LARGE_COMMUNITY_LIST_STANDARD: + if (lcommunity_cmp (entry->u.lcom, new->u.lcom)) + return 1; + break; case COMMUNITY_LIST_EXPANDED: case EXTCOMMUNITY_LIST_EXPANDED: - if (strcmp (entry->config, new->config) == 0) + case LARGE_COMMUNITY_LIST_EXPANDED: + if (entry->config && new->config + && strcmp (entry->config, new->config) == 0) + return 1; + if (!entry->config && !new->config) return 1; break; default: @@ -590,7 +788,7 @@ community_list_dup_check (struct community_list *list, } return 0; } - + /* Set community-list. */ int community_list_set (struct community_list_handler *ch, @@ -699,6 +897,186 @@ community_list_unset (struct community_list_handler *ch, return 0; } +/* Delete all permitted large communities in the list from com. */ +struct lcommunity * +lcommunity_list_match_delete (struct lcommunity *lcom, + struct community_list *list) +{ + struct community_entry *entry; + u_int32_t com_index_to_delete[lcom->size]; + u_char *ptr; + int delete_index = 0; + int i; + + /* Loop over each lcommunity value and evaluate each against the + * community-list. If we need to delete a community value add its index to + * com_index_to_delete. + */ + + for (i = 0; i < lcom->size; i++) + { + ptr = lcom->val + (i * LCOMMUNITY_SIZE); + for (entry = list->head; entry; entry = entry->next) + { + if (entry->any) + { + if (entry->direct == COMMUNITY_PERMIT) + { + com_index_to_delete[delete_index] = i; + delete_index++; + } + break; + } + + else if ((entry->style == LARGE_COMMUNITY_LIST_STANDARD) + && lcommunity_include (entry->u.lcom, ptr) ) + { + if (entry->direct == COMMUNITY_PERMIT) + { + com_index_to_delete[delete_index] = i; + delete_index++; + } + break; + } + + else if ((entry->style == LARGE_COMMUNITY_LIST_STANDARD) + && entry->reg + && lcommunity_regexp_include (entry->reg, lcom, i)) + { + if (entry->direct == COMMUNITY_PERMIT) + { + com_index_to_delete[delete_index] = i; + delete_index++; + } + break; + } + } + } + + /* Delete all of the communities we flagged for deletion */ + + for (i = delete_index-1; i >= 0; i--) + { + ptr = lcom->val + (com_index_to_delete[i] * LCOMMUNITY_SIZE); + lcommunity_del_val (lcom, ptr); + } + + return lcom; +} + +/* Set lcommunity-list. */ +int +lcommunity_list_set (struct community_list_handler *ch, + const char *name, const char *str, int direct, int style) +{ + struct community_entry *entry = NULL; + struct community_list *list; + struct lcommunity *lcom = NULL; + regex_t *regex = NULL; + + /* Get community list. */ + list = community_list_get (ch, name, LARGE_COMMUNITY_LIST_MASTER); + + /* When community-list already has entry, new entry should have same + style. If you want to have mixed style community-list, you can + comment out this check. */ + if (!community_list_empty_p (list)) + { + struct community_entry *first; + + first = list->head; + + if (style != first->style) + { + return (first->style == COMMUNITY_LIST_STANDARD + ? COMMUNITY_LIST_ERR_STANDARD_CONFLICT + : COMMUNITY_LIST_ERR_EXPANDED_CONFLICT); + } + } + + if (str) + { + if (style == LARGE_COMMUNITY_LIST_STANDARD) + lcom = lcommunity_str2com (str); + else + regex = bgp_regcomp (str); + + if (! lcom && ! regex) + return COMMUNITY_LIST_ERR_MALFORMED_VAL; + } + + entry = community_entry_new (); + entry->direct = direct; + entry->style = style; + entry->any = (str ? 0 : 1); + entry->u.lcom = lcom; + entry->reg = regex; + if (lcom) + entry->config = lcommunity_lcom2str (lcom, LCOMMUNITY_FORMAT_COMMUNITY_LIST); + else if (regex) + entry->config = XSTRDUP (MTYPE_COMMUNITY_LIST_CONFIG, str); + else + entry->config = NULL; + + /* Do not put duplicated community entry. */ + if (community_list_dup_check (list, entry)) + community_entry_free (entry); + else + community_list_entry_add (list, entry); + + return 0; +} + +/* Unset community-list. When str is NULL, delete all of + community-list entry belongs to the specified name. */ +int +lcommunity_list_unset (struct community_list_handler *ch, + const char *name, const char *str, + int direct, int style) +{ + struct community_entry *entry = NULL; + struct community_list *list; + struct lcommunity *lcom = NULL; + regex_t *regex = NULL; + + /* Lookup community list. */ + list = community_list_lookup (ch, name, LARGE_COMMUNITY_LIST_MASTER); + if (list == NULL) + return COMMUNITY_LIST_ERR_CANT_FIND_LIST; + + /* Delete all of entry belongs to this community-list. */ + if (!str) + { + community_list_delete (list); + return 0; + } + + if (style == LARGE_COMMUNITY_LIST_STANDARD) + lcom = lcommunity_str2com (str); + else + regex = bgp_regcomp (str); + + if (! lcom && ! regex) + return COMMUNITY_LIST_ERR_MALFORMED_VAL; + + if (lcom) + entry = community_list_entry_lookup (list, lcom, direct); + else + entry = community_list_entry_lookup (list, str, direct); + + if (lcom) + lcommunity_free (&lcom); + if (regex) + bgp_regex_free (regex); + + if (!entry) + return COMMUNITY_LIST_ERR_CANT_FIND_LIST; + + community_list_entry_delete (list, entry, style); + + return 0; +} + /* Set extcommunity-list. */ int extcommunity_list_set (struct community_list_handler *ch, @@ -841,6 +1219,12 @@ community_list_terminate (struct community_list_handler *ch) while ((list = cm->str.head) != NULL) community_list_delete (list); + cm = &ch->lcommunity_list; + while ((list = cm->num.head) != NULL) + community_list_delete (list); + while ((list = cm->str.head) != NULL) + community_list_delete (list); + cm = &ch->extcommunity_list; while ((list = cm->num.head) != NULL) community_list_delete (list); diff --git a/bgpd/bgp_clist.h b/bgpd/bgp_clist.h index 5dcb3b4c1..d9db4189e 100644 --- a/bgpd/bgp_clist.h +++ b/bgpd/bgp_clist.h @@ -24,6 +24,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA /* Master Community-list. */ #define COMMUNITY_LIST_MASTER 0 #define EXTCOMMUNITY_LIST_MASTER 1 +#define LARGE_COMMUNITY_LIST_MASTER 2 /* Community-list deny and permit. */ #define COMMUNITY_DENY 0 @@ -38,6 +39,8 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #define COMMUNITY_LIST_EXPANDED 1 /* Expanded community-list. */ #define EXTCOMMUNITY_LIST_STANDARD 2 /* Standard extcommunity-list. */ #define EXTCOMMUNITY_LIST_EXPANDED 3 /* Expanded extcommunity-list. */ +#define LARGE_COMMUNITY_LIST_STANDARD 4 /* Standard Large community-list. */ +#define LARGE_COMMUNITY_LIST_EXPANDED 5 /* Expanded Large community-list. */ /* Community-list. */ struct community_list @@ -80,6 +83,7 @@ struct community_entry { struct community *com; struct ecommunity *ecom; + struct lcommunity *lcom; } u; /* Configuration string. */ @@ -112,6 +116,9 @@ struct community_list_handler /* Exteded community-list. */ struct community_list_master extcommunity_list; + + /* Large community-list. */ + struct community_list_master lcommunity_list; }; /* Error code of community-list. */ @@ -139,6 +146,12 @@ extern int extcommunity_list_set (struct community_list_handler *ch, extern int extcommunity_list_unset (struct community_list_handler *ch, const char *name, const char *str, int direct, int style); +extern int lcommunity_list_set (struct community_list_handler *ch, + const char *name, const char *str, + int direct, int style); +extern int lcommunity_list_unset (struct community_list_handler *ch, + const char *name, const char *str, + int direct, int style); extern struct community_list_master * community_list_master_lookup (struct community_list_handler *, int); @@ -148,9 +161,12 @@ community_list_lookup (struct community_list_handler *, const char *, int); extern int community_list_match (struct community *, struct community_list *); extern int ecommunity_list_match (struct ecommunity *, struct community_list *); +extern int lcommunity_list_match (struct lcommunity *, struct community_list *); extern int community_list_exact_match (struct community *, struct community_list *); extern struct community * community_list_match_delete (struct community *, struct community_list *); - +extern struct lcommunity * +lcommunity_list_match_delete (struct lcommunity *lcom, + struct community_list *list); #endif /* _QUAGGA_BGP_CLIST_H */ diff --git a/bgpd/bgp_community.c b/bgpd/bgp_community.c index 2ba45f6e4..f1997bd9c 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--; @@ -144,7 +144,7 @@ community_include (struct community *com, u_int32_t val) return 0; } -static u_int32_t +u_int32_t community_val_get (struct community *com, int i) { u_char *p; @@ -394,16 +394,19 @@ community_str (struct community *com) unsigned int community_hash_make (struct community *com) { + unsigned char *pnt = (unsigned char *)com->val; + int size = com->size * 4; + unsigned int key = 0; int c; - unsigned int key; - unsigned char *pnt; - key = 0; - pnt = (unsigned char *)com->val; - - for(c = 0; c < com->size * 4; c++) - key += pnt[c]; - + for (c = 0; c < size; c += 4) + { + key += pnt[c]; + key += pnt[c + 1]; + key += pnt[c + 2]; + key += pnt[c + 3]; + } + return key; } diff --git a/bgpd/bgp_community.h b/bgpd/bgp_community.h index 9e483770e..c73dab304 100644 --- a/bgpd/bgp_community.h +++ b/bgpd/bgp_community.h @@ -70,5 +70,6 @@ extern int community_include (struct community *, u_int32_t); extern void community_del_val (struct community *, u_int32_t *); extern unsigned long community_count (void); extern struct hash *community_hash (void); +extern u_int32_t community_val_get (struct community *com, int i); #endif /* _QUAGGA_BGP_COMMUNITY_H */ diff --git a/bgpd/bgp_damp.c b/bgpd/bgp_damp.c index a51388335..ac647239e 100644 --- a/bgpd/bgp_damp.c +++ b/bgpd/bgp_damp.c @@ -26,6 +26,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "command.h" #include "log.h" #include "thread.h" +#include "filter.h" #include "bgpd/bgpd.h" #include "bgpd/bgp_damp.h" @@ -42,7 +43,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 +87,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) @@ -115,7 +116,7 @@ bgp_reuse_timer (struct thread *t) damp->t_reuse = NULL; damp->t_reuse = - thread_add_timer (master, bgp_reuse_timer, NULL, DELTA_REUSE); + thread_add_timer (bm->master, bgp_reuse_timer, NULL, DELTA_REUSE); t_now = bgp_clock (); @@ -447,7 +448,7 @@ bgp_damp_enable (struct bgp *bgp, afi_t afi, safi_t safi, time_t half, /* Register reuse timer. */ if (! damp->t_reuse) damp->t_reuse = - thread_add_timer (master, bgp_reuse_timer, NULL, DELTA_REUSE); + thread_add_timer (bm->master, bgp_reuse_timer, NULL, DELTA_REUSE); return 0; } @@ -498,6 +499,10 @@ bgp_damp_info_clean (void) int bgp_damp_disable (struct bgp *bgp, afi_t afi, safi_t safi) { + /* If it wasn't enabled, there's nothing to do. */ + if (! CHECK_FLAG (bgp->af_flags[afi][safi], BGP_CONFIG_DAMPENING)) + return 0; + /* Cancel reuse thread. */ if (damp->t_reuse ) thread_cancel (damp->t_reuse); @@ -525,15 +530,15 @@ bgp_config_write_damp (struct vty *vty) && bgp_damp_cfg.reuse_limit == DEFAULT_REUSE && bgp_damp_cfg.suppress_value == DEFAULT_SUPPRESS && bgp_damp_cfg.max_suppress_time == bgp_damp_cfg.half_life*4) - vty_out (vty, " bgp dampening %ld%s", - bgp_damp_cfg.half_life/60, + vty_out (vty, " bgp dampening %lld%s", + bgp_damp_cfg.half_life/60LL, VTY_NEWLINE); else - vty_out (vty, " bgp dampening %ld %d %d %ld%s", - bgp_damp_cfg.half_life/60, + vty_out (vty, " bgp dampening %lld %d %d %lld%s", + bgp_damp_cfg.half_life/60LL, bgp_damp_cfg.reuse_limit, bgp_damp_cfg.suppress_value, - bgp_damp_cfg.max_suppress_time/60, + bgp_damp_cfg.max_suppress_time/60LL, VTY_NEWLINE); } @@ -635,3 +640,36 @@ bgp_damp_reuse_time_vty (struct vty *vty, struct bgp_info *binfo, return bgp_get_reuse_time (penalty, timebuf, len); } + +int +bgp_show_dampening_parameters (struct vty *vty, afi_t afi, safi_t safi) +{ + struct bgp *bgp; + bgp = bgp_get_default(); + + if (bgp == NULL) + { + vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (CHECK_FLAG (bgp->af_flags[afi][safi], BGP_CONFIG_DAMPENING)) + { + vty_out (vty, "Half-life time: %ld min%s", + damp->half_life / 60, VTY_NEWLINE); + vty_out (vty, "Reuse penalty: %d%s", + damp->reuse_limit, VTY_NEWLINE); + vty_out (vty, "Suppress penalty: %d%s", + damp->suppress_value, VTY_NEWLINE); + vty_out (vty, "Max suppress time: %ld min%s", + damp->max_suppress_time / 60, VTY_NEWLINE); + vty_out (vty, "Max supress penalty: %u%s", + damp->ceiling, VTY_NEWLINE); + vty_out (vty, "%s", VTY_NEWLINE); + } + else + vty_out (vty, "dampening not enabled for %s%s", + afi == AFI_IP ? "IPv4" : "IPv6", VTY_NEWLINE); + + return CMD_SUCCESS; +} diff --git a/bgpd/bgp_damp.h b/bgpd/bgp_damp.h index e1d319b56..16fd36715 100644 --- a/bgpd/bgp_damp.h +++ b/bgpd/bgp_damp.h @@ -144,4 +144,6 @@ extern void bgp_damp_info_vty (struct vty *, struct bgp_info *); extern const char * bgp_damp_reuse_time_vty (struct vty *, struct bgp_info *, char *, size_t); +extern int bgp_show_dampening_parameters (struct vty *vty, afi_t, safi_t); + #endif /* _QUAGGA_BGP_DAMP_H */ diff --git a/bgpd/bgp_debug.c b/bgpd/bgp_debug.c index 8e1618645..ba7972281 100644 --- a/bgpd/bgp_debug.c +++ b/bgpd/bgp_debug.c @@ -28,6 +28,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "str.h" #include "log.h" #include "sockunion.h" +#include "filter.h" #include "bgpd/bgpd.h" #include "bgpd/bgp_aspath.h" @@ -45,6 +46,8 @@ unsigned long conf_bgp_debug_keepalive; unsigned long conf_bgp_debug_update; unsigned long conf_bgp_debug_normal; unsigned long conf_bgp_debug_zebra; +unsigned long conf_bgp_debug_allow_martians; +unsigned long conf_bgp_debug_nht; unsigned long term_bgp_debug_as4; unsigned long term_bgp_debug_fsm; @@ -55,6 +58,8 @@ unsigned long term_bgp_debug_keepalive; unsigned long term_bgp_debug_update; unsigned long term_bgp_debug_normal; unsigned long term_bgp_debug_zebra; +unsigned long term_bgp_debug_allow_martians; +unsigned long term_bgp_debug_nht; /* messages for BGP-4 status */ const struct message bgp_status_msg[] = @@ -174,7 +179,6 @@ bgp_dump_attr (struct peer *peer, struct attr *attr, char *buf, size_t size) snprintf (buf + strlen (buf), size - strlen (buf), ", origin %s", bgp_origin_str[attr->origin]); -#ifdef HAVE_IPV6 if (attr->extra) { char addrbuf[BUFSIZ]; @@ -191,14 +195,13 @@ bgp_dump_attr (struct peer *peer, struct attr *attr, char *buf, size_t size) inet_ntop (AF_INET6, &attr->extra->mp_nexthop_local, addrbuf, BUFSIZ)); } -#endif /* HAVE_IPV6 */ if (CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_LOCAL_PREF))) - snprintf (buf + strlen (buf), size - strlen (buf), ", localpref %d", + snprintf (buf + strlen (buf), size - strlen (buf), ", localpref %u", attr->local_pref); if (CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_MULTI_EXIT_DISC))) - snprintf (buf + strlen (buf), size - strlen (buf), ", metric %d", + snprintf (buf + strlen (buf), size - strlen (buf), ", metric %u", attr->med); if (CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_COMMUNITIES))) @@ -243,31 +246,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 +284,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; @@ -467,6 +474,48 @@ ALIAS (no_debug_bgp_events, BGP_STR "BGP events\n") +DEFUN (debug_bgp_nht, + debug_bgp_nht_cmd, + "debug bgp nht", + DEBUG_STR + BGP_STR + "BGP nexthop tracking events\n") +{ + if (vty->node == CONFIG_NODE) + DEBUG_ON (nht, NHT); + else + { + TERM_DEBUG_ON (nht, NHT); + vty_out (vty, "BGP nexthop tracking debugging is on%s", VTY_NEWLINE); + } + return CMD_SUCCESS; +} + +DEFUN (no_debug_bgp_nht, + no_debug_bgp_nht_cmd, + "no debug bgp nht", + NO_STR + DEBUG_STR + BGP_STR + "BGP nexthop tracking events\n") +{ + if (vty->node == CONFIG_NODE) + DEBUG_OFF (nht, NHT); + else + { + TERM_DEBUG_OFF (nht, NHT); + vty_out (vty, "BGP nexthop tracking debugging is off%s", VTY_NEWLINE); + } + return CMD_SUCCESS; +} + +ALIAS (no_debug_bgp_nht, + undebug_bgp_nht_cmd, + "undebug bgp nht", + UNDEBUG_STR + BGP_STR + "BGP next-hop tracking updates\n") + DEFUN (debug_bgp_filter, debug_bgp_filter_cmd, "debug bgp filters", @@ -722,6 +771,48 @@ ALIAS (no_debug_bgp_zebra, BGP_STR "BGP Zebra messages\n") +DEFUN (debug_bgp_allow_martians, + debug_bgp_allow_martians_cmd, + "debug bgp allow-martians", + DEBUG_STR + BGP_STR + "BGP allow martian next hops\n") +{ + if (vty->node == CONFIG_NODE) + DEBUG_ON (allow_martians, ALLOW_MARTIANS); + else + { + TERM_DEBUG_ON (allow_martians, ALLOW_MARTIANS); + vty_out (vty, "BGP allow_martian next hop debugging is on%s", VTY_NEWLINE); + } + return CMD_SUCCESS; +} + +DEFUN (no_debug_bgp_allow_martians, + no_debug_bgp_allow_martians_cmd, + "no debug bgp allow-martians", + NO_STR + DEBUG_STR + BGP_STR + "BGP allow martian next hops\n") +{ + if (vty->node == CONFIG_NODE) + DEBUG_OFF (allow_martians, ALLOW_MARTIANS); + else + { + TERM_DEBUG_OFF (allow_martians, ALLOW_MARTIANS); + vty_out (vty, "BGP allow martian next hop debugging is off%s", VTY_NEWLINE); + } + return CMD_SUCCESS; +} + +ALIAS (no_debug_bgp_allow_martians, + undebug_bgp_allow_martians_cmd, + "undebug bgp allow-martians", + UNDEBUG_STR + BGP_STR + "BGP allow martian next hops\n") + DEFUN (no_debug_bgp_all, no_debug_bgp_all_cmd, "no debug all bgp", @@ -740,6 +831,7 @@ DEFUN (no_debug_bgp_all, TERM_DEBUG_OFF (fsm, FSM); TERM_DEBUG_OFF (filter, FILTER); TERM_DEBUG_OFF (zebra, ZEBRA); + TERM_DEBUG_OFF (allow_martians, ALLOW_MARTIANS); vty_out (vty, "All possible debugging has been turned off%s", VTY_NEWLINE); return CMD_SUCCESS; @@ -783,6 +875,10 @@ DEFUN (show_debugging_bgp, vty_out (vty, " BGP as4 debugging is on%s", VTY_NEWLINE); if (BGP_DEBUG (as4, AS4_SEGMENT)) vty_out (vty, " BGP as4 aspath segment debugging is on%s", VTY_NEWLINE); + if (BGP_DEBUG (allow_martians, ALLOW_MARTIANS)) + vty_out (vty, " BGP allow martian next hop debugging is on%s", VTY_NEWLINE); + if (BGP_DEBUG (nht, NHT)) + vty_out (vty, " BGP next-hop tracking debugging is on%s", VTY_NEWLINE); vty_out (vty, "%s", VTY_NEWLINE); return CMD_SUCCESS; } @@ -856,6 +952,18 @@ bgp_config_write_debug (struct vty *vty) write++; } + if (CONF_BGP_DEBUG (allow_martians, ALLOW_MARTIANS)) + { + vty_out (vty, "debug bgp allow-martians%s", VTY_NEWLINE); + write++; + } + + if (CONF_BGP_DEBUG (nht, NHT)) + { + vty_out (vty, "debug bgp nht%s", VTY_NEWLINE); + write++; + } + return write; } @@ -882,6 +990,8 @@ bgp_debug_init (void) install_element (CONFIG_NODE, &debug_bgp_fsm_cmd); install_element (ENABLE_NODE, &debug_bgp_events_cmd); install_element (CONFIG_NODE, &debug_bgp_events_cmd); + install_element (ENABLE_NODE, &debug_bgp_nht_cmd); + install_element (CONFIG_NODE, &debug_bgp_nht_cmd); install_element (ENABLE_NODE, &debug_bgp_filter_cmd); install_element (CONFIG_NODE, &debug_bgp_filter_cmd); install_element (ENABLE_NODE, &debug_bgp_keepalive_cmd); @@ -894,6 +1004,8 @@ bgp_debug_init (void) install_element (CONFIG_NODE, &debug_bgp_normal_cmd); install_element (ENABLE_NODE, &debug_bgp_zebra_cmd); install_element (CONFIG_NODE, &debug_bgp_zebra_cmd); + install_element (ENABLE_NODE, &debug_bgp_allow_martians_cmd); + install_element (CONFIG_NODE, &debug_bgp_allow_martians_cmd); install_element (ENABLE_NODE, &no_debug_bgp_as4_cmd); install_element (ENABLE_NODE, &undebug_bgp_as4_cmd); @@ -908,6 +1020,9 @@ bgp_debug_init (void) install_element (ENABLE_NODE, &no_debug_bgp_events_cmd); install_element (ENABLE_NODE, &undebug_bgp_events_cmd); install_element (CONFIG_NODE, &no_debug_bgp_events_cmd); + install_element (ENABLE_NODE, &no_debug_bgp_nht_cmd); + install_element (ENABLE_NODE, &undebug_bgp_nht_cmd); + install_element (CONFIG_NODE, &no_debug_bgp_nht_cmd); install_element (ENABLE_NODE, &no_debug_bgp_filter_cmd); install_element (ENABLE_NODE, &undebug_bgp_filter_cmd); install_element (CONFIG_NODE, &no_debug_bgp_filter_cmd); @@ -923,6 +1038,9 @@ bgp_debug_init (void) install_element (ENABLE_NODE, &no_debug_bgp_zebra_cmd); install_element (ENABLE_NODE, &undebug_bgp_zebra_cmd); install_element (CONFIG_NODE, &no_debug_bgp_zebra_cmd); + install_element (ENABLE_NODE, &no_debug_bgp_allow_martians_cmd); + install_element (ENABLE_NODE, &undebug_bgp_allow_martians_cmd); + install_element (CONFIG_NODE, &no_debug_bgp_allow_martians_cmd); install_element (ENABLE_NODE, &no_debug_bgp_all_cmd); install_element (ENABLE_NODE, &undebug_bgp_all_cmd); } diff --git a/bgpd/bgp_debug.h b/bgpd/bgp_debug.h index ce8547b04..253bd7fe6 100644 --- a/bgpd/bgp_debug.h +++ b/bgpd/bgp_debug.h @@ -67,6 +67,8 @@ extern unsigned long conf_bgp_debug_keepalive; extern unsigned long conf_bgp_debug_update; extern unsigned long conf_bgp_debug_normal; extern unsigned long conf_bgp_debug_zebra; +extern unsigned long conf_bgp_debug_allow_martians; +extern unsigned long conf_bgp_debug_nht; extern unsigned long term_bgp_debug_as4; extern unsigned long term_bgp_debug_fsm; @@ -77,6 +79,8 @@ extern unsigned long term_bgp_debug_keepalive; extern unsigned long term_bgp_debug_update; extern unsigned long term_bgp_debug_normal; extern unsigned long term_bgp_debug_zebra; +extern unsigned long term_bgp_debug_allow_martians; +extern unsigned long term_bgp_debug_nht; #define BGP_DEBUG_AS4 0x01 #define BGP_DEBUG_AS4_SEGMENT 0x02 @@ -90,6 +94,8 @@ extern unsigned long term_bgp_debug_zebra; #define BGP_DEBUG_UPDATE_OUT 0x02 #define BGP_DEBUG_NORMAL 0x01 #define BGP_DEBUG_ZEBRA 0x01 +#define BGP_DEBUG_ALLOW_MARTIANS 0x01 +#define BGP_DEBUG_NHT 0x01 #define BGP_DEBUG_PACKET_SEND 0x01 #define BGP_DEBUG_PACKET_SEND_DETAIL 0x02 diff --git a/bgpd/bgp_dump.c b/bgpd/bgp_dump.c index edb725a97..01f9b412f 100644 --- a/bgpd/bgp_dump.c +++ b/bgpd/bgp_dump.c @@ -27,20 +27,36 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "prefix.h" #include "thread.h" #include "linklist.h" -#include "bgpd/bgp_table.h" +#include "filter.h" +#include "bgpd/bgp_table.h" #include "bgpd/bgpd.h" #include "bgpd/bgp_route.h" #include "bgpd/bgp_attr.h" #include "bgpd/bgp_dump.h" - + enum bgp_dump_type { BGP_DUMP_ALL, + BGP_DUMP_ALL_ET, BGP_DUMP_UPDATES, + BGP_DUMP_UPDATES_ET, BGP_DUMP_ROUTES }; +static const struct bgp_dump_type_map { + enum bgp_dump_type type; + const char *str; +} bgp_dump_type_map[] = + { + {BGP_DUMP_ALL, "all"}, + {BGP_DUMP_ALL_ET, "all-et"}, + {BGP_DUMP_UPDATES, "updates"}, + {BGP_DUMP_UPDATES_ET, "updates-et"}, + {BGP_DUMP_ROUTES, "routes-mrt"}, + {0, NULL}, + }; + enum MRT_MSG_TYPES { MSG_NULL, MSG_START, /* sender is starting up */ @@ -58,8 +74,6 @@ enum MRT_MSG_TYPES { MSG_TABLE_DUMP_V2 /* routing table dump, version 2 */ }; -static int bgp_dump_interval_func (struct thread *); - struct bgp_dump { enum bgp_dump_type type; @@ -75,6 +89,9 @@ struct bgp_dump struct thread *t_interval; }; +static int bgp_dump_unset (struct vty *vty, struct bgp_dump *bgp_dump); +static int bgp_dump_interval_func (struct thread *); + /* BGP packet dump output buffer. */ struct stream *bgp_dump_obuf; @@ -87,10 +104,6 @@ struct bgp_dump bgp_dump_updates; /* BGP dump structure for 'dump bgp routes' */ 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) { @@ -156,13 +169,13 @@ bgp_dump_interval_add (struct bgp_dump *bgp_dump, int interval) secs_into_day = tm->tm_sec + 60*tm->tm_min + 60*60*tm->tm_hour; interval = interval - secs_into_day % interval; /* always > 0 */ } - bgp_dump->t_interval = thread_add_timer (master, bgp_dump_interval_func, + bgp_dump->t_interval = thread_add_timer (bm->master, bgp_dump_interval_func, bgp_dump, interval); } else { /* One-off dump: execute immediately, don't affect any scheduled dumps */ - bgp_dump->t_interval = thread_add_event (master, bgp_dump_interval_func, + bgp_dump->t_interval = thread_add_event (bm->master, bgp_dump_interval_func, bgp_dump, 0); } @@ -171,24 +184,40 @@ bgp_dump_interval_add (struct bgp_dump *bgp_dump, int interval) /* Dump common header. */ static void -bgp_dump_header (struct stream *obuf, int type, int subtype) +bgp_dump_header (struct stream *obuf, int type, int subtype, int dump_type) { - time_t now; + struct timeval clock; + long msecs; + time_t secs; + + if ((dump_type == BGP_DUMP_ALL_ET || dump_type == BGP_DUMP_UPDATES_ET) + && type == MSG_PROTOCOL_BGP4MP) + type = MSG_PROTOCOL_BGP4MP_ET; + + gettimeofday(&clock, NULL); - /* Set header. */ - time (&now); + secs = clock.tv_sec; + msecs = clock.tv_usec; /* Put dump packet header. */ - stream_putl (obuf, now); + stream_putl (obuf, secs); stream_putw (obuf, type); stream_putw (obuf, subtype); - stream_putl (obuf, 0); /* len */ + + /* Adding microseconds for the MRT Extended Header */ + if (type == MSG_PROTOCOL_BGP4MP_ET) + stream_putl (obuf, msecs); } static void bgp_dump_set_size (struct stream *s, int type) { + /* + * The BGP_DUMP_HEADER_SIZE stay at 12 event when ET: + * "The Microsecond Timestamp is included in the computation + * of the Length field value." (RFC6396 2011) + */ stream_putl_at (s, 8, stream_get_endp (s) - BGP_DUMP_HEADER_SIZE); } @@ -197,14 +226,15 @@ bgp_dump_routes_index_table(struct bgp *bgp) { struct peer *peer; struct listnode *node; - uint16_t peerno = 0; + uint16_t peerno = 1; struct stream *obuf; obuf = bgp_dump_obuf; stream_reset (obuf); /* MRT header */ - bgp_dump_header (obuf, MSG_TABLE_DUMP_V2, TABLE_DUMP_V2_PEER_INDEX_TABLE); + bgp_dump_header (obuf, MSG_TABLE_DUMP_V2, TABLE_DUMP_V2_PEER_INDEX_TABLE, + BGP_DUMP_ROUTES); /* Collector BGP ID */ stream_put_in_addr (obuf, &bgp->router_id); @@ -220,8 +250,18 @@ bgp_dump_routes_index_table(struct bgp *bgp) stream_putw(obuf, 0); } - /* Peer count */ - stream_putw (obuf, listcount(bgp->peer)); + /* Peer count ( plus one extra internal peer ) */ + stream_putw (obuf, listcount(bgp->peer) + 1); + + /* Populate fake peer at index 0, for locally originated routes */ + /* Peer type (IPv4) */ + stream_putc (obuf, TABLE_DUMP_V2_PEER_INDEX_TABLE_AS4+TABLE_DUMP_V2_PEER_INDEX_TABLE_IP); + /* Peer BGP ID (0.0.0.0) */ + stream_putl (obuf, 0); + /* Peer IP address (0.0.0.0) */ + stream_putl (obuf, 0); + /* Peer ASN (0) */ + stream_putl (obuf, 0); /* Walk down all peers */ for(ALL_LIST_ELEMENTS_RO (bgp->peer, node, peer)) @@ -232,12 +272,10 @@ bgp_dump_routes_index_table(struct bgp *bgp) { stream_putc (obuf, TABLE_DUMP_V2_PEER_INDEX_TABLE_AS4+TABLE_DUMP_V2_PEER_INDEX_TABLE_IP); } -#ifdef HAVE_IPV6 else if (sockunion_family(&peer->su) == AF_INET6) { stream_putc (obuf, TABLE_DUMP_V2_PEER_INDEX_TABLE_AS4+TABLE_DUMP_V2_PEER_INDEX_TABLE_IP6); } -#endif /* HAVE_IPV6 */ /* Peer's BGP ID */ stream_put_in_addr (obuf, &peer->remote_id); @@ -247,13 +285,11 @@ bgp_dump_routes_index_table(struct bgp *bgp) { stream_put_in_addr (obuf, &peer->su.sin.sin_addr); } -#ifdef HAVE_IPV6 else if (sockunion_family(&peer->su) == AF_INET6) { stream_write (obuf, (u_char *)&peer->su.sin6.sin6_addr, IPV6_MAX_BYTELEN); } -#endif /* HAVE_IPV6 */ /* Peer's AS number. */ /* Note that, as this is an AS4 compliant quagga, the RIB is always AS4 */ @@ -271,11 +307,100 @@ bgp_dump_routes_index_table(struct bgp *bgp) } +static struct bgp_info * +bgp_dump_route_node_record (int afi, struct bgp_node *rn, + struct bgp_info *info, unsigned int seq) +{ + struct stream *obuf; + size_t sizep; + size_t endp; + + obuf = bgp_dump_obuf; + stream_reset (obuf); + + /* MRT header */ + if (afi == AFI_IP) + bgp_dump_header (obuf, MSG_TABLE_DUMP_V2, TABLE_DUMP_V2_RIB_IPV4_UNICAST, + BGP_DUMP_ROUTES); + else if (afi == AFI_IP6) + bgp_dump_header (obuf, MSG_TABLE_DUMP_V2, TABLE_DUMP_V2_RIB_IPV6_UNICAST, + BGP_DUMP_ROUTES); + + /* Sequence number */ + stream_putl (obuf, seq); + + /* Prefix length */ + stream_putc (obuf, rn->p.prefixlen); + + /* Prefix */ + if (afi == AFI_IP) + { + /* We'll dump only the useful bits (those not 0), but have to + * align on 8 bits */ + stream_write (obuf, (u_char *) &rn->p.u.prefix4, + (rn->p.prefixlen + 7) / 8); + } + else if (afi == AFI_IP6) + { + /* We'll dump only the useful bits (those not 0), but have to + * align on 8 bits */ + stream_write (obuf, (u_char *) &rn->p.u.prefix6, + (rn->p.prefixlen + 7) / 8); + } + + /* Save where we are now, so we can overwride the entry count later */ + sizep = stream_get_endp (obuf); + + /* Entry count */ + uint16_t entry_count = 0; + + /* Entry count, note that this is overwritten later */ + stream_putw (obuf, 0); + + endp = stream_get_endp (obuf); + for (; info; info = info->next) + { + size_t cur_endp; + + /* Peer index */ + stream_putw (obuf, info->peer->table_dump_index); + + /* Originated */ +#ifdef HAVE_CLOCK_MONOTONIC + stream_putl (obuf, time (NULL) - (bgp_clock () - info->uptime)); +#else + stream_putl (obuf, info->uptime); +#endif /* HAVE_CLOCK_MONOTONIC */ + + /* Dump attribute. */ + /* Skip prefix & AFI/SAFI for MP_NLRI */ + bgp_dump_routes_attr (obuf, info->attr, &rn->p); + + cur_endp = stream_get_endp (obuf); + if (cur_endp > BGP_MAX_PACKET_SIZE + BGP_DUMP_MSG_HEADER + + BGP_DUMP_HEADER_SIZE) + { + stream_set_endp (obuf, endp); + break; + } + + entry_count++; + endp = cur_endp; + } + + /* Overwrite the entry count, now that we know the right number */ + stream_putw_at (obuf, sizep, entry_count); + + bgp_dump_set_size (obuf, MSG_TABLE_DUMP_V2); + fwrite (STREAM_DATA (obuf), stream_get_endp (obuf), 1, bgp_dump_routes.fp); + + return info; +} + /* Runs under child process. */ static unsigned int bgp_dump_routes_func (int afi, int first_run, unsigned int seq) { - struct stream *obuf; struct bgp_info *info; struct bgp_node *rn; struct bgp *bgp; @@ -294,87 +419,17 @@ bgp_dump_routes_func (int afi, int first_run, unsigned int seq) if(first_run) bgp_dump_routes_index_table(bgp); - obuf = bgp_dump_obuf; - stream_reset(obuf); - /* Walk down each BGP route. */ table = bgp->rib[afi][SAFI_UNICAST]; for (rn = bgp_table_top (table); rn; rn = bgp_route_next (rn)) { - if(!rn->info) - continue; - - stream_reset(obuf); - - /* MRT header */ - if (afi == AFI_IP) - { - bgp_dump_header (obuf, MSG_TABLE_DUMP_V2, TABLE_DUMP_V2_RIB_IPV4_UNICAST); - } -#ifdef HAVE_IPV6 - else if (afi == AFI_IP6) - { - bgp_dump_header (obuf, MSG_TABLE_DUMP_V2, TABLE_DUMP_V2_RIB_IPV6_UNICAST); - } -#endif /* HAVE_IPV6 */ - - /* Sequence number */ - stream_putl(obuf, seq); - - /* Prefix length */ - stream_putc (obuf, rn->p.prefixlen); - - /* Prefix */ - if (afi == AFI_IP) - { - /* We'll dump only the useful bits (those not 0), but have to align on 8 bits */ - stream_write(obuf, (u_char *)&rn->p.u.prefix4, (rn->p.prefixlen+7)/8); - } -#ifdef HAVE_IPV6 - else if (afi == AFI_IP6) - { - /* We'll dump only the useful bits (those not 0), but have to align on 8 bits */ - stream_write (obuf, (u_char *)&rn->p.u.prefix6, (rn->p.prefixlen+7)/8); - } -#endif /* HAVE_IPV6 */ - - /* Save where we are now, so we can overwride the entry count later */ - int sizep = stream_get_endp(obuf); - - /* Entry count */ - uint16_t entry_count = 0; - - /* Entry count, note that this is overwritten later */ - stream_putw(obuf, 0); - - for (info = rn->info; info; info = info->next) - { - entry_count++; - - /* Peer index */ - stream_putw(obuf, info->peer->table_dump_index); - - /* Originated */ -#ifdef HAVE_CLOCK_MONOTONIC - stream_putl (obuf, time(NULL) - (bgp_clock() - info->uptime)); -#else - stream_putl (obuf, info->uptime); -#endif /* HAVE_CLOCK_MONOTONIC */ - - /* Dump attribute. */ - /* Skip prefix & AFI/SAFI for MP_NLRI */ - bgp_dump_routes_attr (obuf, info->attr, &rn->p); - } - - /* Overwrite the entry count, now that we know the right number */ - stream_putw_at (obuf, sizep, entry_count); - - seq++; - - bgp_dump_set_size(obuf, MSG_TABLE_DUMP_V2); - fwrite (STREAM_DATA (obuf), stream_get_endp (obuf), 1, bgp_dump_routes.fp); - + info = rn->info; + while (info) + { + info = bgp_dump_route_node_record(afi, rn, info, seq); + seq++; + } } fflush (bgp_dump_routes.fp); @@ -396,9 +451,7 @@ bgp_dump_interval_func (struct thread *t) if (bgp_dump->type == BGP_DUMP_ROUTES) { unsigned int seq = bgp_dump_routes_func (AFI_IP, 1, 0); -#ifdef HAVE_IPV6 bgp_dump_routes_func (AFI_IP6, 0, seq); -#endif /* HAVE_IPV6 */ /* Close the file now. For a RIB dump there's no point in leaving * it open until the next scheduled dump starts. */ fclose(bgp_dump->fp); bgp_dump->fp = NULL; @@ -442,7 +495,6 @@ bgp_dump_common (struct stream *obuf, struct peer *peer, int forceas4) else stream_put (obuf, empty, IPV4_MAX_BYTELEN); } -#ifdef HAVE_IPV6 else if (peer->su.sa.sa_family == AF_INET6) { /* Interface Index and Address family. */ @@ -457,7 +509,6 @@ bgp_dump_common (struct stream *obuf, struct peer *peer, int forceas4) else stream_put (obuf, empty, IPV6_MAX_BYTELEN); } -#endif /* HAVE_IPV6 */ } /* Dump BGP status change. */ @@ -474,7 +525,8 @@ bgp_dump_state (struct peer *peer, int status_old, int status_new) obuf = bgp_dump_obuf; stream_reset (obuf); - bgp_dump_header (obuf, MSG_PROTOCOL_BGP4MP, BGP4MP_STATE_CHANGE_AS4); + bgp_dump_header (obuf, MSG_PROTOCOL_BGP4MP, BGP4MP_STATE_CHANGE_AS4, + bgp_dump_all.type); bgp_dump_common (obuf, peer, 1);/* force this in as4speak*/ stream_putw (obuf, status_old); @@ -505,11 +557,13 @@ bgp_dump_packet_func (struct bgp_dump *bgp_dump, struct peer *peer, /* Dump header and common part. */ if (CHECK_FLAG (peer->cap, PEER_CAP_AS4_RCV) ) { - bgp_dump_header (obuf, MSG_PROTOCOL_BGP4MP, BGP4MP_MESSAGE_AS4); + bgp_dump_header (obuf, MSG_PROTOCOL_BGP4MP, BGP4MP_MESSAGE_AS4, + bgp_dump->type); } else { - bgp_dump_header (obuf, MSG_PROTOCOL_BGP4MP, BGP4MP_MESSAGE); + bgp_dump_header (obuf, MSG_PROTOCOL_BGP4MP, BGP4MP_MESSAGE, + bgp_dump->type); } bgp_dump_common (obuf, peer, 0); @@ -535,7 +589,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) { @@ -590,9 +644,28 @@ bgp_dump_set (struct vty *vty, struct bgp_dump *bgp_dump, { unsigned int interval; + /* Don't schedule duplicate dumps if the dump command is given twice */ + if (bgp_dump->filename && strcmp(path, bgp_dump->filename) == 0 + && type == bgp_dump->type) + { + if (interval_str) + { + if (bgp_dump->interval_str && + strcmp(bgp_dump->interval_str, interval_str) == 0) + return CMD_SUCCESS; + } + else + { + if (!bgp_dump->interval_str) + return CMD_SUCCESS; + } + } + + /* Removing previous config */ + bgp_dump_unset(vty, bgp_dump); + if (interval_str) { - /* Check interval string. */ interval = bgp_dump_parse_time (interval_str); if (interval == 0) @@ -601,37 +674,26 @@ bgp_dump_set (struct vty *vty, struct bgp_dump *bgp_dump, return CMD_WARNING; } - /* Don't schedule duplicate dumps if the dump command is given twice */ - if (interval == bgp_dump->interval && - type == bgp_dump->type && - path && bgp_dump->filename && !strcmp (path, bgp_dump->filename)) - { - return CMD_SUCCESS; - } - - /* Set interval. */ - bgp_dump->interval = interval; - if (bgp_dump->interval_str) - free (bgp_dump->interval_str); + /* Setting interval string */ bgp_dump->interval_str = strdup (interval_str); - } else { interval = 0; } - - /* Create interval thread. */ - bgp_dump_interval_add (bgp_dump, interval); /* Set type. */ bgp_dump->type = type; + /* Set interval */ + bgp_dump->interval = interval; + /* Set file name. */ - if (bgp_dump->filename) - free (bgp_dump->filename); bgp_dump->filename = strdup (path); + /* Create interval thread. */ + bgp_dump_interval_add (bgp_dump, interval); + /* This should be called when interval is expired. */ bgp_dump_open_file (bgp_dump); @@ -641,21 +703,21 @@ bgp_dump_set (struct vty *vty, struct bgp_dump *bgp_dump, static int bgp_dump_unset (struct vty *vty, struct bgp_dump *bgp_dump) { - /* Set file name. */ + /* Removing file name. */ if (bgp_dump->filename) { free (bgp_dump->filename); bgp_dump->filename = NULL; } - /* This should be called when interval is expired. */ + /* Closing file. */ if (bgp_dump->fp) { fclose (bgp_dump->fp); bgp_dump->fp = NULL; } - /* Create interval thread. */ + /* Removing interval thread. */ if (bgp_dump->t_interval) { thread_cancel (bgp_dump->t_interval); @@ -664,116 +726,71 @@ bgp_dump_unset (struct vty *vty, struct bgp_dump *bgp_dump) bgp_dump->interval = 0; + /* Removing interval string. */ if (bgp_dump->interval_str) { free (bgp_dump->interval_str); bgp_dump->interval_str = NULL; } - return CMD_SUCCESS; } DEFUN (dump_bgp_all, dump_bgp_all_cmd, - "dump bgp all PATH", - "Dump packet\n" - "BGP packet dump\n" - "Dump all BGP packets\n" - "Output filename\n") -{ - return bgp_dump_set (vty, &bgp_dump_all, BGP_DUMP_ALL, argv[0], NULL); -} - -DEFUN (dump_bgp_all_interval, - dump_bgp_all_interval_cmd, - "dump bgp all PATH INTERVAL", + "dump bgp (all|all-et|updates|updates-et|routes-mrt) PATH [INTERVAL]", "Dump packet\n" "BGP packet dump\n" - "Dump all BGP packets\n" + "Dump all BGP packets\nDump all BGP packets (Extended Tiemstamp Header)\n" + "Dump BGP updates only\nDump BGP updates only (Extended Tiemstamp Header)\n" + "Dump whole BGP routing table\n" "Output filename\n" "Interval of output\n") { - return bgp_dump_set (vty, &bgp_dump_all, BGP_DUMP_ALL, argv[0], argv[1]); -} - -DEFUN (no_dump_bgp_all, - no_dump_bgp_all_cmd, - "no dump bgp all [PATH] [INTERVAL]", - NO_STR - "Dump packet\n" - "BGP packet dump\n" - "Dump all BGP packets\n") -{ - return bgp_dump_unset (vty, &bgp_dump_all); -} - -DEFUN (dump_bgp_updates, - dump_bgp_updates_cmd, - "dump bgp updates PATH", - "Dump packet\n" - "BGP packet dump\n" - "Dump BGP updates only\n" - "Output filename\n") -{ - return bgp_dump_set (vty, &bgp_dump_updates, BGP_DUMP_UPDATES, argv[0], NULL); -} + int bgp_dump_type = 0; + const char *interval = NULL; + struct bgp_dump *bgp_dump_struct = NULL; + const struct bgp_dump_type_map *map = NULL; -DEFUN (dump_bgp_updates_interval, - dump_bgp_updates_interval_cmd, - "dump bgp updates PATH INTERVAL", - "Dump packet\n" - "BGP packet dump\n" - "Dump BGP updates only\n" - "Output filename\n" - "Interval of output\n") -{ - return bgp_dump_set (vty, &bgp_dump_updates, BGP_DUMP_UPDATES, argv[0], argv[1]); -} + for (map = bgp_dump_type_map; map->str; map++) + if (strcmp(argv[0], map->str) == 0) + bgp_dump_type = map->type; -DEFUN (no_dump_bgp_updates, - no_dump_bgp_updates_cmd, - "no dump bgp updates [PATH] [INTERVAL]", - NO_STR - "Dump packet\n" - "BGP packet dump\n" - "Dump BGP updates only\n") -{ - return bgp_dump_unset (vty, &bgp_dump_updates); -} + switch (bgp_dump_type) + { + case BGP_DUMP_ALL: + case BGP_DUMP_ALL_ET: + bgp_dump_struct = &bgp_dump_all; + break; + case BGP_DUMP_UPDATES: + case BGP_DUMP_UPDATES_ET: + bgp_dump_struct = &bgp_dump_updates; + break; + case BGP_DUMP_ROUTES: + default: + bgp_dump_struct = &bgp_dump_routes; + break; + } -DEFUN (dump_bgp_routes, - dump_bgp_routes_cmd, - "dump bgp routes-mrt PATH", - "Dump packet\n" - "BGP packet dump\n" - "Dump whole BGP routing table\n" - "Output filename\n") -{ - return bgp_dump_set (vty, &bgp_dump_routes, BGP_DUMP_ROUTES, argv[0], NULL); -} + /* When an interval is given */ + if (argc == 3) + interval = argv[2]; -DEFUN (dump_bgp_routes_interval, - dump_bgp_routes_interval_cmd, - "dump bgp routes-mrt PATH INTERVAL", - "Dump packet\n" - "BGP packet dump\n" - "Dump whole BGP routing table\n" - "Output filename\n" - "Interval of output\n") -{ - return bgp_dump_set (vty, &bgp_dump_routes, BGP_DUMP_ROUTES, argv[0], argv[1]); + return bgp_dump_set (vty, bgp_dump_struct, bgp_dump_type, + argv[1], interval); } -DEFUN (no_dump_bgp_routes, - no_dump_bgp_routes_cmd, - "no dump bgp routes-mrt [PATH] [INTERVAL]", +DEFUN (no_dump_bgp_all, + no_dump_bgp_all_cmd, + "no dump bgp (all|updates|routes-mrt) [PATH] [INTERVAL]", NO_STR - "Dump packet\n" - "BGP packet dump\n" - "Dump whole BGP routing table\n") + "Stop dump packet\n" + "Stop BGP packet dump\n" + "Stop dump process all/all-et\n" + "Stop dump process updates/updates-et\n" + "Stop dump process route-mrt\n") { - return bgp_dump_unset (vty, &bgp_dump_routes); + return bgp_dump_unset (vty, &bgp_dump_all); } /* BGP node structure. */ @@ -815,18 +832,26 @@ config_write_bgp_dump (struct vty *vty) { if (bgp_dump_all.filename) { + const char *type_str = "all"; + if (bgp_dump_all.type == BGP_DUMP_ALL_ET) + type_str = "all-et"; + if (bgp_dump_all.interval_str) - vty_out (vty, "dump bgp all %s %s%s", + vty_out (vty, "dump bgp %s %s %s%s", type_str, bgp_dump_all.filename, bgp_dump_all.interval_str, VTY_NEWLINE); else - vty_out (vty, "dump bgp all %s%s", + vty_out (vty, "dump bgp %s %s%s", type_str, bgp_dump_all.filename, VTY_NEWLINE); } if (bgp_dump_updates.filename) { + const char *type_str = "updates"; + if (bgp_dump_updates.type == BGP_DUMP_UPDATES_ET) + type_str = "updates-et"; + if (bgp_dump_updates.interval_str) - vty_out (vty, "dump bgp updates %s %s%s", + vty_out (vty, "dump bgp %s %s %s%s", type_str, bgp_dump_updates.filename, bgp_dump_updates.interval_str, VTY_NEWLINE); else @@ -839,13 +864,10 @@ config_write_bgp_dump (struct vty *vty) vty_out (vty, "dump bgp routes-mrt %s %s%s", bgp_dump_routes.filename, bgp_dump_routes.interval_str, VTY_NEWLINE); - else - vty_out (vty, "dump bgp routes-mrt %s%s", - bgp_dump_routes.filename, VTY_NEWLINE); } return 0; } - + /* Initialize BGP packet dump functionality. */ void bgp_dump_init (void) @@ -854,20 +876,13 @@ bgp_dump_init (void) memset (&bgp_dump_updates, 0, sizeof (struct bgp_dump)); memset (&bgp_dump_routes, 0, sizeof (struct bgp_dump)); - bgp_dump_obuf = stream_new (BGP_MAX_PACKET_SIZE + BGP_DUMP_MSG_HEADER - + BGP_DUMP_HEADER_SIZE); + bgp_dump_obuf = stream_new ((BGP_MAX_PACKET_SIZE << 1) + + BGP_DUMP_MSG_HEADER + BGP_DUMP_HEADER_SIZE); install_node (&bgp_dump_node, config_write_bgp_dump); install_element (CONFIG_NODE, &dump_bgp_all_cmd); - install_element (CONFIG_NODE, &dump_bgp_all_interval_cmd); install_element (CONFIG_NODE, &no_dump_bgp_all_cmd); - install_element (CONFIG_NODE, &dump_bgp_updates_cmd); - install_element (CONFIG_NODE, &dump_bgp_updates_interval_cmd); - install_element (CONFIG_NODE, &no_dump_bgp_updates_cmd); - install_element (CONFIG_NODE, &dump_bgp_routes_cmd); - install_element (CONFIG_NODE, &dump_bgp_routes_interval_cmd); - install_element (CONFIG_NODE, &no_dump_bgp_routes_cmd); } void diff --git a/bgpd/bgp_dump.h b/bgpd/bgp_dump.h index e097c7840..d1e66d91c 100644 --- a/bgpd/bgp_dump.h +++ b/bgpd/bgp_dump.h @@ -23,7 +23,9 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA /* MRT compatible packet dump values. */ /* type value */ -#define MSG_PROTOCOL_BGP4MP 16 +#define MSG_PROTOCOL_BGP4MP 16 +#define MSG_PROTOCOL_BGP4MP_ET 17 + /* subtype value */ #define BGP4MP_STATE_CHANGE 0 #define BGP4MP_MESSAGE 1 diff --git a/bgpd/bgp_ecommunity.c b/bgpd/bgp_ecommunity.c index 440c15a40..5d69b42c3 100644 --- a/bgpd/bgp_ecommunity.c +++ b/bgpd/bgp_ecommunity.c @@ -24,6 +24,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "memory.h" #include "prefix.h" #include "command.h" +#include "filter.h" #include "bgpd/bgpd.h" #include "bgpd/bgp_ecommunity.h" @@ -31,7 +32,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) @@ -99,7 +100,7 @@ ecommunity_add_val (struct ecommunity *ecom, struct ecommunity_val *eval) /* This function takes pointer to Extended Communites strucutre then create a new Extended Communities structure by uniq and sort each Extended Communities value. */ -static struct ecommunity * +struct ecommunity * ecommunity_uniq_sort (struct ecommunity *ecom) { int i; @@ -233,15 +234,22 @@ unsigned int ecommunity_hash_make (void *arg) { const struct ecommunity *ecom = arg; + int size = ecom->size * ECOMMUNITY_SIZE; + u_int8_t *pnt = ecom->val; + unsigned int key = 0; int c; - unsigned int key; - u_int8_t *pnt; - key = 0; - pnt = ecom->val; - - for (c = 0; c < ecom->size * ECOMMUNITY_SIZE; c++) - key += pnt[c]; + for (c = 0; c < size; c += ECOMMUNITY_SIZE) + { + key += pnt[c]; + key += pnt[c + 1]; + key += pnt[c + 2]; + key += pnt[c + 3]; + key += pnt[c + 4]; + key += pnt[c + 5]; + key += pnt[c + 6]; + key += pnt[c + 7]; + } return key; } @@ -270,14 +278,14 @@ ecommunity_finish (void) hash_free (ecomhash); ecomhash = NULL; } - + /* Extended Communities token enum. */ enum ecommunity_token { + ecommunity_token_unknown = 0, ecommunity_token_rt, ecommunity_token_soo, ecommunity_token_val, - ecommunity_token_unknown }; /* Get next Extended Communities token from the string. */ @@ -504,7 +512,7 @@ struct ecommunity * ecommunity_str2com (const char *str, int type, int keyword_included) { struct ecommunity *ecom = NULL; - enum ecommunity_token token; + enum ecommunity_token token = ecommunity_token_unknown; struct ecommunity_val eval; int keyword = 0; @@ -635,15 +643,34 @@ ecommunity_ecom2str (struct ecommunity *ecom, int format) /* High-order octet of type. */ encode = *pnt++; - if (encode != ECOMMUNITY_ENCODE_AS && encode != ECOMMUNITY_ENCODE_IP - && encode != ECOMMUNITY_ENCODE_AS4) - { - len = sprintf (str_buf + str_pnt, "?"); - str_pnt += len; - first = 0; - continue; - } - + + switch (encode) + { + case ECOMMUNITY_ENCODE_AS: + case ECOMMUNITY_ENCODE_IP: + case ECOMMUNITY_ENCODE_AS4: + break; + + case ECOMMUNITY_ENCODE_OPAQUE: + if (*pnt == ECOMMUNITY_OPAQUE_SUBTYPE_ENCAP) + { + uint16_t tunneltype; + memcpy (&tunneltype, pnt + 5, 2); + tunneltype = ntohs(tunneltype); + len = sprintf (str_buf + str_pnt, "ET:%d", tunneltype); + str_pnt += len; + first = 0; + continue; + } + /* fall through */ + + default: + len = sprintf (str_buf + str_pnt, "?"); + str_pnt += len; + first = 0; + continue; + } + /* Low-order octet of type. */ type = *pnt++; if (type != ECOMMUNITY_ROUTE_TARGET && type != ECOMMUNITY_SITE_ORIGIN) @@ -681,7 +708,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; @@ -696,7 +723,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; @@ -708,7 +735,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_ecommunity.h b/bgpd/bgp_ecommunity.h index 2f59dc409..993fd5acf 100644 --- a/bgpd/bgp_ecommunity.h +++ b/bgpd/bgp_ecommunity.h @@ -25,11 +25,15 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #define ECOMMUNITY_ENCODE_AS 0x00 #define ECOMMUNITY_ENCODE_IP 0x01 #define ECOMMUNITY_ENCODE_AS4 0x02 +#define ECOMMUNITY_ENCODE_OPAQUE 0x03 -/* Low-order octet of the Extended Communityes type field. */ +/* Low-order octet of the Extended Communities type field. */ #define ECOMMUNITY_ROUTE_TARGET 0x02 #define ECOMMUNITY_SITE_ORIGIN 0x03 +/* Low-order octet of the Extended Communities type field for OPAQUE types */ +#define ECOMMUNITY_OPAQUE_SUBTYPE_ENCAP 0x0c + /* Extended communities attribute string format. */ #define ECOMMUNITY_FORMAT_ROUTE_MAP 0 #define ECOMMUNITY_FORMAT_COMMUNITY_LIST 1 @@ -71,6 +75,7 @@ extern void ecommunity_free (struct ecommunity **); extern struct ecommunity *ecommunity_parse (u_int8_t *, u_short); extern struct ecommunity *ecommunity_dup (struct ecommunity *); extern struct ecommunity *ecommunity_merge (struct ecommunity *, struct ecommunity *); +extern struct ecommunity *ecommunity_uniq_sort (struct ecommunity *); extern struct ecommunity *ecommunity_intern (struct ecommunity *); extern int ecommunity_cmp (const void *, const void *); extern void ecommunity_unintern (struct ecommunity **); diff --git a/bgpd/bgp_encap.c b/bgpd/bgp_encap.c new file mode 100644 index 000000000..cd58ac25c --- /dev/null +++ b/bgpd/bgp_encap.c @@ -0,0 +1,954 @@ + +/* + * This file created by LabN Consulting, L.L.C. + * + * + * This file is based on bgp_mplsvpn.c which is Copyright (C) 2000 + * 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 "command.h" +#include "prefix.h" +#include "log.h" +#include "memory.h" +#include "stream.h" +#include "filter.h" + +#include "bgpd/bgpd.h" +#include "bgpd/bgp_table.h" +#include "bgpd/bgp_route.h" +#include "bgpd/bgp_attr.h" +#include "bgpd/bgp_ecommunity.h" +#include "bgpd/bgp_mplsvpn.h" +#include "bgpd/bgp_vty.h" +#include "bgpd/bgp_encap.h" + +static u_int16_t +decode_rd_type (u_char *pnt) +{ + u_int16_t v; + + v = ((u_int16_t) *pnt++ << 8); + v |= (u_int16_t) *pnt; + return v; +} + + +static void +decode_rd_as (u_char *pnt, struct rd_as *rd_as) +{ + rd_as->as = (u_int16_t) *pnt++ << 8; + rd_as->as |= (u_int16_t) *pnt++; + + rd_as->val = ((u_int32_t) *pnt++) << 24; + rd_as->val |= ((u_int32_t) *pnt++) << 16; + rd_as->val |= ((u_int32_t) *pnt++) << 8; + rd_as->val |= (u_int32_t) *pnt; +} + +static void +decode_rd_as4 (u_char *pnt, struct rd_as *rd_as) +{ + rd_as->as = (u_int32_t) *pnt++ << 24; + rd_as->as |= (u_int32_t) *pnt++ << 16; + rd_as->as |= (u_int32_t) *pnt++ << 8; + rd_as->as |= (u_int32_t) *pnt++; + + rd_as->val = ((u_int32_t) *pnt++ << 8); + rd_as->val |= (u_int32_t) *pnt; +} + +static void +decode_rd_ip (u_char *pnt, struct rd_ip *rd_ip) +{ + memcpy (&rd_ip->ip, pnt, 4); + pnt += 4; + + rd_ip->val = ((u_int16_t) *pnt++ << 8); + rd_ip->val |= (u_int16_t) *pnt; +} + +static void +ecom2prd(struct ecommunity *ecom, struct prefix_rd *prd) +{ + int i; + + memset(prd, 0, sizeof(struct prefix_rd)); + prd->family = AF_UNSPEC; + prd->prefixlen = 64; + + if (!ecom) + return; + + for (i = 0; i < (ecom->size * ECOMMUNITY_SIZE); i += ECOMMUNITY_SIZE) { + + uint8_t *ep; + + ep = ecom->val + i; + + switch (ep[0]) { + default: + continue; + + case 0x80: + case 0x81: + case 0x82: + if (ep[1] == 0x0) { + prd->val[1] = ep[0] & 0x03; + memcpy(prd->val + 2, ep + 2, 6); + return; + } + } + } +} + +int +bgp_nlri_parse_encap( + struct peer *peer, + struct attr *attr, /* Need even for withdraw */ + struct bgp_nlri *packet) +{ + u_char *pnt; + u_char *lim; + afi_t afi = packet->afi; + struct prefix p; + int psize = 0; + int prefixlen; + struct rd_as rd_as; + struct rd_ip rd_ip; + struct prefix_rd prd; + struct ecommunity *pEcom = NULL; + u_int16_t rdtype = 0xffff; + char buf[BUFSIZ]; + + /* Check peer status. */ + if (peer->status != Established) + return 0; + + /* Make prefix_rd */ + if (attr && attr->extra && attr->extra->ecommunity) + pEcom = attr->extra->ecommunity; + + ecom2prd(pEcom, &prd); + memset(&rd_as, 0, sizeof(rd_as)); + memset(&rd_ip, 0, sizeof(rd_ip)); + + if (pEcom) { + + rdtype = (prd.val[0] << 8) | prd.val[1]; + + /* Decode RD value. */ + if (rdtype == RD_TYPE_AS) + decode_rd_as (prd.val + 2, &rd_as); + else if (rdtype == RD_TYPE_IP) + decode_rd_ip (prd.val + 2, &rd_ip); + else if (rdtype == RD_TYPE_AS4) + decode_rd_as4 (prd.val + 2, &rd_as); + else + { + zlog_err ("Invalid RD type %d", rdtype); + } + + } + + /* + * NB: this code was based on the MPLS VPN code, which supported RDs. + * For the moment we are retaining the underlying RIB structure that + * keeps a per-RD radix tree, but since the RDs are not carried over + * the wire, we set the RD internally to 0. + */ + prd.family = AF_UNSPEC; + prd.prefixlen = 64; + memset(prd.val, 0, sizeof(prd.val)); + + pnt = packet->nlri; + lim = pnt + packet->length; + + for (; pnt < lim; pnt += psize) + { + /* Clear prefix structure. */ + memset (&p, 0, sizeof (struct prefix)); + + /* Fetch prefix length. */ + prefixlen = *pnt++; + p.family = afi2family(afi); + if (p.family == 0) { + /* bad afi, shouldn't happen */ + zlog_warn("%s: bad afi %d, dropping incoming route", __func__, afi); + continue; + } + psize = PSIZE (prefixlen); + + p.prefixlen = prefixlen; + memcpy (&p.u.prefix, pnt, psize); + + if (pnt + psize > lim) + return -1; + + + if (rdtype == RD_TYPE_AS) + zlog_info ("rd-as %u:%u prefix %s/%d", rd_as.as, rd_as.val, + inet_ntop (p.family, &p.u.prefix, buf, BUFSIZ), + p.prefixlen); + else if (rdtype == RD_TYPE_IP) + zlog_info ("rd-ip %s:%u prefix %s/%d", inet_ntoa (rd_ip.ip), + rd_ip.val, + inet_ntop (p.family, &p.u.prefix, buf, BUFSIZ), + p.prefixlen); + else if (rdtype == RD_TYPE_AS4) + zlog_info ("rd-as4 %u:%u prefix %s/%d", rd_as.as, rd_as.val, + inet_ntop (p.family, &p.u.prefix, buf, BUFSIZ), + p.prefixlen); + else + zlog_info ("rd unknown, default to 0:0 prefix %s/%d", + inet_ntop (p.family, &p.u.prefix, buf, BUFSIZ), + p.prefixlen); + + if (attr) { + bgp_update (peer, &p, attr, afi, SAFI_ENCAP, + ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, &prd, NULL, 0); + } else { + bgp_withdraw (peer, &p, attr, afi, SAFI_ENCAP, + ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, &prd, NULL); + } + } + + /* Packet length consistency check. */ + if (pnt != lim) + return -1; + + return 0; +} + + +/* TBD: these routes should probably all be host routes */ + +/* For testing purpose, static route of ENCAP. */ +DEFUN (encap_network, + encap_network_cmd, + "network A.B.C.D/M rd ASN:nn_or_IP-address:nn tag WORD", + "Specify a network to announce via BGP\n" + "IP prefix /, e.g., 35.0.0.0/8\n" + "Specify Route Distinguisher\n" + "ENCAP Route Distinguisher\n" + "BGP tag\n" + "tag value\n") +{ + return bgp_static_set_safi (SAFI_ENCAP, vty, argv[0], argv[1], argv[2], NULL); +} + +/* For testing purpose, static route of ENCAP. */ +DEFUN (no_encap_network, + no_encap_network_cmd, + "no network A.B.C.D/M rd ASN:nn_or_IP-address:nn tag WORD", + NO_STR + "Specify a network to announce via BGP\n" + "IP prefix /, e.g., 35.0.0.0/8\n" + "Specify Route Distinguisher\n" + "ENCAP Route Distinguisher\n" + "BGP tag\n" + "tag value\n") +{ + return bgp_static_unset_safi (SAFI_ENCAP, vty, argv[0], argv[1], argv[2]); +} + +static int +show_adj_route_encap (struct vty *vty, struct peer *peer, struct prefix_rd *prd) +{ + struct bgp *bgp; + struct bgp_table *table; + struct bgp_node *rn; + struct bgp_node *rm; + struct attr *attr; + int rd_header; + int header = 1; + char v4_header[] = " Network Next Hop Metric LocPrf Weight Path%s"; + + bgp = bgp_get_default (); + if (bgp == NULL) + { + vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + + for (rn = bgp_table_top (bgp->rib[AFI_IP][SAFI_ENCAP]); rn; + rn = bgp_route_next (rn)) + { + if (prd && memcmp (rn->p.u.val, prd->val, 8) != 0) + continue; + + if ((table = rn->info) != NULL) + { + rd_header = 1; + + for (rm = bgp_table_top (table); rm; rm = bgp_route_next (rm)) + if ((attr = rm->info) != NULL) + { + if (header) + { + vty_out (vty, "BGP table version is 0, local router ID is %s%s", + inet_ntoa (bgp->router_id), VTY_NEWLINE); + vty_out (vty, "Status codes: s suppressed, d damped, h history, * valid, > best, i - internal%s", + VTY_NEWLINE); + vty_out (vty, "Origin codes: i - IGP, e - EGP, ? - incomplete%s%s", + VTY_NEWLINE, VTY_NEWLINE); + vty_out (vty, v4_header, VTY_NEWLINE); + header = 0; + } + + if (rd_header) + { + u_int16_t type; + struct rd_as rd_as; + struct rd_ip rd_ip; + u_char *pnt; + + pnt = rn->p.u.val; + + vty_out (vty, "Route Distinguisher: "); + + /* Decode RD type. */ + type = decode_rd_type (pnt); + + switch (type) { + + case RD_TYPE_AS: + decode_rd_as (pnt + 2, &rd_as); + vty_out (vty, "%u:%d", rd_as.as, rd_as.val); + break; + + case RD_TYPE_IP: + decode_rd_ip (pnt + 2, &rd_ip); + vty_out (vty, "%s:%d", inet_ntoa (rd_ip.ip), rd_ip.val); + break; + + default: + vty_out (vty, "unknown RD type"); + } + + + vty_out (vty, "%s", VTY_NEWLINE); + rd_header = 0; + } + route_vty_out_tmp (vty, &rm->p, attr, SAFI_ENCAP); + } + } + } + return CMD_SUCCESS; +} + +enum bgp_show_type +{ + bgp_show_type_normal, + bgp_show_type_regexp, + bgp_show_type_prefix_list, + bgp_show_type_filter_list, + bgp_show_type_neighbor, + bgp_show_type_cidr_only, + bgp_show_type_prefix_longer, + bgp_show_type_community_all, + bgp_show_type_community, + bgp_show_type_community_exact, + bgp_show_type_community_list, + bgp_show_type_community_list_exact +}; + +static int +bgp_show_encap ( + struct vty *vty, + afi_t afi, + struct prefix_rd *prd, + enum bgp_show_type type, + void *output_arg, + int tags) +{ + struct bgp *bgp; + struct bgp_table *table; + struct bgp_node *rn; + struct bgp_node *rm; + struct bgp_info *ri; + int rd_header; + int header = 1; + char v4_header[] = " Network Next Hop Metric LocPrf Weight Path%s"; + char v4_header_tag[] = " Network Next Hop In tag/Out tag%s"; + + unsigned long output_count = 0; + unsigned long total_count = 0; + + bgp = bgp_get_default (); + if (bgp == NULL) + { + vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if ((afi != AFI_IP) && (afi != AFI_IP6)) { + vty_out (vty, "Afi %d not supported%s", afi, VTY_NEWLINE); + return CMD_WARNING; + } + + for (rn = bgp_table_top (bgp->rib[afi][SAFI_ENCAP]); rn; rn = bgp_route_next (rn)) + { + if (prd && memcmp (rn->p.u.val, prd->val, 8) != 0) + continue; + + if ((table = rn->info) != NULL) + { + rd_header = 1; + + for (rm = bgp_table_top (table); rm; rm = bgp_route_next (rm)) + for (ri = rm->info; ri; ri = ri->next) + { + total_count++; + if (type == bgp_show_type_neighbor) + { + union sockunion *su = output_arg; + + if (ri->peer->su_remote == NULL || ! sockunion_same(ri->peer->su_remote, su)) + continue; + } + if (header) + { + if (tags) + vty_out (vty, v4_header_tag, VTY_NEWLINE); + else + { + vty_out (vty, "BGP table version is 0, local router ID is %s%s", + inet_ntoa (bgp->router_id), VTY_NEWLINE); + vty_out (vty, "Status codes: s suppressed, d damped, h history, * valid, > best, i - internal%s", + VTY_NEWLINE); + vty_out (vty, "Origin codes: i - IGP, e - EGP, ? - incomplete%s%s", + VTY_NEWLINE, VTY_NEWLINE); + vty_out (vty, v4_header, VTY_NEWLINE); + } + header = 0; + } + + if (rd_header) + { + u_int16_t type; + struct rd_as rd_as; + struct rd_ip rd_ip; + u_char *pnt; + + pnt = rn->p.u.val; + + /* Decode RD type. */ + type = decode_rd_type (pnt); + + vty_out (vty, "Route Distinguisher: "); + + switch (type) { + + case RD_TYPE_AS: + decode_rd_as (pnt + 2, &rd_as); + vty_out (vty, "%u:%d", rd_as.as, rd_as.val); + break; + + case RD_TYPE_IP: + decode_rd_ip (pnt + 2, &rd_ip); + vty_out (vty, "%s:%d", inet_ntoa (rd_ip.ip), rd_ip.val); + break; + + default: + vty_out (vty, "Unknown RD type"); + break; + } + + vty_out (vty, "%s", VTY_NEWLINE); + rd_header = 0; + } + if (tags) + route_vty_out_tag (vty, &rm->p, ri, 0, SAFI_ENCAP); + else + route_vty_out (vty, &rm->p, ri, 0, SAFI_ENCAP); + output_count++; + } + } + } + + if (output_count == 0) + { + vty_out (vty, "No prefixes displayed, %ld exist%s", total_count, VTY_NEWLINE); + } + else + vty_out (vty, "%sDisplayed %ld out of %ld total prefixes%s", + VTY_NEWLINE, output_count, total_count, VTY_NEWLINE); + + return CMD_SUCCESS; +} + +DEFUN (show_bgp_ipv4_encap, + show_bgp_ipv4_encap_cmd, + "show bgp ipv4 encap", + SHOW_STR + BGP_STR + "Address Family\n" + "Display ENCAP NLRI specific information\n") +{ + return bgp_show_encap (vty, AFI_IP, NULL, bgp_show_type_normal, NULL, 0); +} + +DEFUN (show_bgp_ipv6_encap, + show_bgp_ipv6_encap_cmd, + "show bgp ipv6 encap", + SHOW_STR + BGP_STR + "Address Family\n" + "Display ENCAP NLRI specific information\n") +{ + return bgp_show_encap (vty, AFI_IP6, NULL, bgp_show_type_normal, NULL, 0); +} + +DEFUN (show_bgp_ipv4_encap_rd, + show_bgp_ipv4_encap_rd_cmd, + "show bgp ipv4 encap rd ASN:nn_or_IP-address:nn", + SHOW_STR + BGP_STR + "Address Family\n" + "Display ENCAP NLRI specific information\n" + "Display information for a route distinguisher\n" + "ENCAP Route Distinguisher\n") +{ + int ret; + struct prefix_rd prd; + + ret = str2prefix_rd (argv[0], &prd); + if (! ret) + { + vty_out (vty, "%% Malformed Route Distinguisher%s", VTY_NEWLINE); + return CMD_WARNING; + } + return bgp_show_encap (vty, AFI_IP, &prd, bgp_show_type_normal, NULL, 0); +} + +DEFUN (show_bgp_ipv6_encap_rd, + show_bgp_ipv6_encap_rd_cmd, + "show bgp ipv6 encap rd ASN:nn_or_IP-address:nn", + SHOW_STR + BGP_STR + "Address Family\n" + "Display ENCAP NLRI specific information\n" + "Display information for a route distinguisher\n" + "ENCAP Route Distinguisher\n" + "Display BGP tags for prefixes\n") +{ + int ret; + struct prefix_rd prd; + + ret = str2prefix_rd (argv[0], &prd); + if (! ret) + { + vty_out (vty, "%% Malformed Route Distinguisher%s", VTY_NEWLINE); + return CMD_WARNING; + } + return bgp_show_encap (vty, AFI_IP6, &prd, bgp_show_type_normal, NULL, 0); +} + +DEFUN (show_bgp_ipv4_encap_tags, + show_bgp_ipv4_encap_tags_cmd, + "show bgp ipv4 encap tags", + SHOW_STR + BGP_STR + "Address Family\n" + "Display ENCAP NLRI specific information\n" + "Display BGP tags for prefixes\n") +{ + return bgp_show_encap (vty, AFI_IP, NULL, bgp_show_type_normal, NULL, 1); +} + +DEFUN (show_bgp_ipv6_encap_tags, + show_bgp_ipv6_encap_tags_cmd, + "show bgp ipv6 encap tags", + SHOW_STR + BGP_STR + "Address Family\n" + "Display ENCAP NLRI specific information\n" + "Display BGP tags for prefixes\n") +{ + return bgp_show_encap (vty, AFI_IP6, NULL, bgp_show_type_normal, NULL, 1); +} + + +DEFUN (show_bgp_ipv4_encap_rd_tags, + show_bgp_ipv4_encap_rd_tags_cmd, + "show bgp ipv4 encap rd ASN:nn_or_IP-address:nn tags", + SHOW_STR + BGP_STR + "Address Family\n" + "Display ENCAP NLRI specific information\n" + "Display information for a route distinguisher\n" + "ENCAP Route Distinguisher\n" + "Display BGP tags for prefixes\n") +{ + int ret; + struct prefix_rd prd; + + ret = str2prefix_rd (argv[0], &prd); + if (! ret) + { + vty_out (vty, "%% Malformed Route Distinguisher%s", VTY_NEWLINE); + return CMD_WARNING; + } + return bgp_show_encap (vty, AFI_IP, &prd, bgp_show_type_normal, NULL, 1); +} + +DEFUN (show_bgp_ipv6_encap_rd_tags, + show_bgp_ipv6_encap_rd_tags_cmd, + "show bgp ipv6 encap rd ASN:nn_or_IP-address:nn tags", + SHOW_STR + BGP_STR + "Address Family\n" + "Display ENCAP NLRI specific information\n" + "Display information for a route distinguisher\n" + "ENCAP Route Distinguisher\n" + "Display BGP tags for prefixes\n") +{ + int ret; + struct prefix_rd prd; + + ret = str2prefix_rd (argv[0], &prd); + if (! ret) + { + vty_out (vty, "%% Malformed Route Distinguisher%s", VTY_NEWLINE); + return CMD_WARNING; + } + return bgp_show_encap (vty, AFI_IP6, &prd, bgp_show_type_normal, NULL, 1); +} + +DEFUN (show_bgp_ipv4_encap_neighbor_routes, + show_bgp_ipv4_encap_neighbor_routes_cmd, + "show bgp ipv4 encap neighbors A.B.C.D routes", + SHOW_STR + BGP_STR + "Address Family\n" + "Display ENCAP NLRI specific information\n" + "Detailed information on TCP and BGP neighbor connections\n" + "Neighbor to display information about\n" + "Display routes learned from neighbor\n") +{ + union sockunion su; + struct peer *peer; + + if (str2sockunion(argv[0], &su)) + { + vty_out (vty, "Malformed address: %s%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + + peer = peer_lookup (NULL, &su); + if (! peer || ! peer->afc[AFI_IP][SAFI_ENCAP]) + { + vty_out (vty, "%% No such neighbor or address family%s", VTY_NEWLINE); + return CMD_WARNING; + } + + return bgp_show_encap (vty, AFI_IP, NULL, bgp_show_type_neighbor, &su, 0); +} + +DEFUN (show_bgp_ipv6_encap_neighbor_routes, + show_bgp_ipv6_encap_neighbor_routes_cmd, + "show bgp ipv6 encap neighbors A.B.C.D routes", + SHOW_STR + BGP_STR + "Address Family\n" + "Display ENCAP NLRI specific information\n" + "Detailed information on TCP and BGP neighbor connections\n" + "Neighbor to display information about\n" + "Display routes learned from neighbor\n") +{ + union sockunion su; + struct peer *peer; + + if (str2sockunion(argv[0], &su)) + { + vty_out (vty, "Malformed address: %s%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + + peer = peer_lookup (NULL, &su); + if (! peer || ! peer->afc[AFI_IP6][SAFI_ENCAP]) + { + vty_out (vty, "%% No such neighbor or address family%s", VTY_NEWLINE); + return CMD_WARNING; + } + + return bgp_show_encap (vty, AFI_IP6, NULL, bgp_show_type_neighbor, &su, 0); +} + +DEFUN (show_bgp_ipv4_encap_rd_neighbor_routes, + show_bgp_ipv4_encap_rd_neighbor_routes_cmd, + "show bgp ipv4 encap rd ASN:nn_or_IP-address:nn neighbors (A.B.C.D|X:X::X:X) routes", + SHOW_STR + BGP_STR + "Address Family\n" + "Display ENCAP NLRI specific information\n" + "Display information for a route distinguisher\n" + "ENCAP Route Distinguisher\n" + "Detailed information on TCP and BGP neighbor connections\n" + "Neighbor to display information about\n" + "Neighbor to display information about\n" + "Display routes learned from neighbor\n") +{ + int ret; + union sockunion su; + struct peer *peer; + struct prefix_rd prd; + + ret = str2prefix_rd (argv[0], &prd); + if (! ret) + { + vty_out (vty, "%% Malformed Route Distinguisher%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (str2sockunion(argv[1], &su)) + { + vty_out (vty, "Malformed address: %s%s", argv[1], VTY_NEWLINE); + return CMD_WARNING; + } + + peer = peer_lookup (NULL, &su); + if (! peer || ! peer->afc[AFI_IP][SAFI_ENCAP]) + { + vty_out (vty, "%% No such neighbor or address family%s", VTY_NEWLINE); + return CMD_WARNING; + } + + return bgp_show_encap (vty, AFI_IP, &prd, bgp_show_type_neighbor, &su, 0); +} + +DEFUN (show_bgp_ipv6_encap_rd_neighbor_routes, + show_bgp_ipv6_encap_rd_neighbor_routes_cmd, + "show bgp ipv6 encap rd ASN:nn_or_IP-address:nn neighbors (A.B.C.D|X:X::X:X) routes", + SHOW_STR + BGP_STR + "Address Family\n" + "Display ENCAP NLRI specific information\n" + "Display information for a route distinguisher\n" + "ENCAP Route Distinguisher\n" + "Detailed information on TCP and BGP neighbor connections\n" + "Neighbor to display information about\n" + "Neighbor to display information about\n" + "Display routes learned from neighbor\n") +{ + int ret; + union sockunion su; + struct peer *peer; + struct prefix_rd prd; + + ret = str2prefix_rd (argv[0], &prd); + if (! ret) + { + vty_out (vty, "%% Malformed Route Distinguisher%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (str2sockunion(argv[1], &su)) + { + vty_out (vty, "Malformed address: %s%s", argv[1], VTY_NEWLINE); + return CMD_WARNING; + } + + peer = peer_lookup (NULL, &su); + if (! peer || ! peer->afc[AFI_IP6][SAFI_ENCAP]) + { + vty_out (vty, "%% No such neighbor or address family%s", VTY_NEWLINE); + return CMD_WARNING; + } + + return bgp_show_encap (vty, AFI_IP6, &prd, bgp_show_type_neighbor, &su, 0); +} + +DEFUN (show_bgp_ipv4_encap_neighbor_advertised_routes, + show_bgp_ipv4_encap_neighbor_advertised_routes_cmd, + "show bgp ipv4 encap neighbors A.B.C.D advertised-routes", + SHOW_STR + BGP_STR + "Address Family\n" + "Display ENCAP NLRI specific information\n" + "Detailed information on TCP and BGP neighbor connections\n" + "Neighbor to display information about\n" + "Display the routes advertised to a BGP neighbor\n") +{ + int ret; + struct peer *peer; + union sockunion su; + + ret = str2sockunion (argv[0], &su); + if (ret < 0) + { + vty_out (vty, "%% Malformed address: %s%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + peer = peer_lookup (NULL, &su); + if (! peer || ! peer->afc[AFI_IP][SAFI_ENCAP]) + { + vty_out (vty, "%% No such neighbor or address family%s", VTY_NEWLINE); + return CMD_WARNING; + } + + return show_adj_route_encap (vty, peer, NULL); +} + +DEFUN (show_bgp_ipv6_encap_neighbor_advertised_routes, + show_bgp_ipv6_encap_neighbor_advertised_routes_cmd, + "show bgp ipv6 encap neighbors A.B.C.D advertised-routes", + SHOW_STR + BGP_STR + "Address Family\n" + "Display ENCAP NLRI specific information\n" + "Detailed information on TCP and BGP neighbor connections\n" + "Neighbor to display information about\n" + "Display the routes advertised to a BGP neighbor\n") +{ + int ret; + struct peer *peer; + union sockunion su; + + ret = str2sockunion (argv[0], &su); + if (ret < 0) + { + vty_out (vty, "%% Malformed address: %s%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + peer = peer_lookup (NULL, &su); + if (! peer || ! peer->afc[AFI_IP6][SAFI_ENCAP]) + { + vty_out (vty, "%% No such neighbor or address family%s", VTY_NEWLINE); + return CMD_WARNING; + } + + return show_adj_route_encap (vty, peer, NULL); +} + +DEFUN (show_bgp_ipv4_encap_rd_neighbor_advertised_routes, + show_bgp_ipv4_encap_rd_neighbor_advertised_routes_cmd, + "show bgp ipv4 encap rd ASN:nn_or_IP-address:nn neighbors (A.B.C.D|X:X::X:X) advertised-routes", + SHOW_STR + BGP_STR + "Address Family\n" + "Display ENCAP NLRI specific information\n" + "Display information for a route distinguisher\n" + "ENCAP Route Distinguisher\n" + "Detailed information on TCP and BGP neighbor connections\n" + "Neighbor to display information about\n" + "Neighbor to display information about\n" + "Display the routes advertised to a BGP neighbor\n") +{ + int ret; + struct peer *peer; + struct prefix_rd prd; + union sockunion su; + + ret = str2sockunion (argv[1], &su); + if (ret < 0) + { + vty_out (vty, "%% Malformed address: %s%s", argv[1], VTY_NEWLINE); + return CMD_WARNING; + } + peer = peer_lookup (NULL, &su); + if (! peer || ! peer->afc[AFI_IP][SAFI_ENCAP]) + { + vty_out (vty, "%% No such neighbor or address family%s", VTY_NEWLINE); + return CMD_WARNING; + } + + ret = str2prefix_rd (argv[0], &prd); + if (! ret) + { + vty_out (vty, "%% Malformed Route Distinguisher%s", VTY_NEWLINE); + return CMD_WARNING; + } + + return show_adj_route_encap (vty, peer, &prd); +} + +DEFUN (show_bgp_ipv6_encap_rd_neighbor_advertised_routes, + show_bgp_ipv6_encap_rd_neighbor_advertised_routes_cmd, + "show bgp ipv6 encap rd ASN:nn_or_IP-address:nn neighbors (A.B.C.D|X:X::X:X) advertised-routes", + SHOW_STR + BGP_STR + "Address Family\n" + "Display ENCAP NLRI specific information\n" + "Display information for a route distinguisher\n" + "ENCAP Route Distinguisher\n" + "Detailed information on TCP and BGP neighbor connections\n" + "Neighbor to display information about\n" + "Neighbor to display information about\n" + "Display the routes advertised to a BGP neighbor\n") +{ + int ret; + struct peer *peer; + struct prefix_rd prd; + union sockunion su; + + ret = str2sockunion (argv[1], &su); + if (ret < 0) + { + vty_out (vty, "%% Malformed address: %s%s", argv[1], VTY_NEWLINE); + return CMD_WARNING; + } + peer = peer_lookup (NULL, &su); + if (! peer || ! peer->afc[AFI_IP6][SAFI_ENCAP]) + { + vty_out (vty, "%% No such neighbor or address family%s", VTY_NEWLINE); + return CMD_WARNING; + } + + ret = str2prefix_rd (argv[0], &prd); + if (! ret) + { + vty_out (vty, "%% Malformed Route Distinguisher%s", VTY_NEWLINE); + return CMD_WARNING; + } + + return show_adj_route_encap (vty, peer, &prd); +} + +void +bgp_encap_init (void) +{ + install_element (BGP_ENCAP_NODE, &encap_network_cmd); + install_element (BGP_ENCAP_NODE, &no_encap_network_cmd); + + install_element (VIEW_NODE, &show_bgp_ipv4_encap_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_encap_rd_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_encap_tags_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_encap_rd_tags_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_encap_neighbor_routes_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_encap_rd_neighbor_routes_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_encap_neighbor_advertised_routes_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_encap_rd_neighbor_advertised_routes_cmd); + + install_element (VIEW_NODE, &show_bgp_ipv6_encap_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_encap_rd_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_encap_tags_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_encap_rd_tags_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_encap_neighbor_routes_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_encap_rd_neighbor_routes_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_encap_neighbor_advertised_routes_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_encap_rd_neighbor_advertised_routes_cmd); +} diff --git a/bgpd/bgp_encap.h b/bgpd/bgp_encap.h new file mode 100644 index 000000000..7442c73c4 --- /dev/null +++ b/bgpd/bgp_encap.h @@ -0,0 +1,29 @@ +/* + * + * Copyright 2009-2015, LabN Consulting, L.L.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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#ifndef _QUAGGA_BGP_ENCAP_H +#define _QUAGGA_BGP_ENCAP_H + +extern void bgp_encap_init (void); +extern int bgp_nlri_parse_encap (struct peer *, struct attr *, struct bgp_nlri *); + +#include "bgp_encap_types.h" +#endif /* _QUAGGA_BGP_ENCAP_H */ diff --git a/bgpd/bgp_encap_tlv.c b/bgpd/bgp_encap_tlv.c new file mode 100644 index 000000000..347b4b3ce --- /dev/null +++ b/bgpd/bgp_encap_tlv.c @@ -0,0 +1,1012 @@ +/* + * Copyright 2015, LabN Consulting, L.L.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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#include + +#include "memory.h" +#include "prefix.h" +#include "vty.h" +#include "filter.h" + +#include "bgpd.h" +#include "bgp_attr.h" + +#include "bgp_encap_types.h" +#include "bgp_encap_tlv.h" + +/*********************************************************************** + * SUBTLV ENCODE + ***********************************************************************/ + +/* rfc5512 4.1 */ +static struct bgp_attr_encap_subtlv * +subtlv_encode_encap_l2tpv3_over_ip( + struct bgp_tea_subtlv_encap_l2tpv3_over_ip *st) +{ + struct bgp_attr_encap_subtlv *new; + uint8_t *p; + int total = 4 + st->cookie_length; + + /* sanity check */ + assert(st->cookie_length <= sizeof(st->cookie)); + assert(total <= 0xff); + + new = XCALLOC(MTYPE_ENCAP_TLV, sizeof(struct bgp_attr_encap_subtlv) - 1 + total); + assert(new); + new->type = BGP_ENCAP_SUBTLV_TYPE_ENCAPSULATION; + new->length = total; + p = new->value; + + *p++ = (st->sessionid & 0xff000000) >> 24; + *p++ = (st->sessionid & 0xff0000) >> 16; + *p++ = (st->sessionid & 0xff00) >> 8; + *p++ = (st->sessionid & 0xff); + memcpy(p, st->cookie, st->cookie_length); + return new; +} + +/* rfc5512 4.1 */ +static struct bgp_attr_encap_subtlv * +subtlv_encode_encap_gre( + struct bgp_tea_subtlv_encap_gre_key *st) +{ + struct bgp_attr_encap_subtlv *new; + uint8_t *p; + int total = 4; + + assert(total <= 0xff); + + new = XCALLOC(MTYPE_ENCAP_TLV, sizeof(struct bgp_attr_encap_subtlv) - 1 + total); + assert(new); + new->type = BGP_ENCAP_SUBTLV_TYPE_ENCAPSULATION; + new->length = total; + p = new->value; + + *p++ = (st->gre_key & 0xff000000) >> 24; + *p++ = (st->gre_key & 0xff0000) >> 16; + *p++ = (st->gre_key & 0xff00) >> 8; + *p++ = (st->gre_key & 0xff); + return new; +} + +static struct bgp_attr_encap_subtlv * +subtlv_encode_encap_pbb( + struct bgp_tea_subtlv_encap_pbb *st) +{ + struct bgp_attr_encap_subtlv *new; + uint8_t *p; + int total = 1 + 3 + 6 + 2; /* flags + isid + madaddr + vid */ + + assert(total <= 0xff); + + new = XCALLOC(MTYPE_ENCAP_TLV, sizeof(struct bgp_attr_encap_subtlv) - 1 + total); + assert(new); + new->type = BGP_ENCAP_SUBTLV_TYPE_ENCAPSULATION; + new->length = total; + p = new->value; + + *p++ = (st->flag_isid? 0x80: 0) | + (st->flag_vid? 0x40: 0) | + 0; + if (st->flag_isid) { + *p = (st->isid & 0xff0000) >> 16; + *(p+1) = (st->isid & 0xff00) >> 8; + *(p+2) = (st->isid & 0xff); + } + p += 3; + memcpy(p, st->macaddr, 6); + p += 6; + if (st->flag_vid) { + *p++ = (st->vid & 0xf00) >> 8; + *p++ = st->vid & 0xff; + } + return new; +} + +/* rfc5512 4.2 */ +static struct bgp_attr_encap_subtlv * +subtlv_encode_proto_type( + struct bgp_tea_subtlv_proto_type *st) +{ + struct bgp_attr_encap_subtlv *new; + uint8_t *p; + int total = 2; + + assert(total <= 0xff); + + new = XCALLOC(MTYPE_ENCAP_TLV, sizeof(struct bgp_attr_encap_subtlv) - 1 + total); + assert(new); + new->type = BGP_ENCAP_SUBTLV_TYPE_PROTO_TYPE; + new->length = total; + p = new->value; + + *p++ = (st->proto & 0xff00) >> 8; + *p++ = (st->proto & 0xff); + return new; +} + +/* rfc5512 4.3 */ +static struct bgp_attr_encap_subtlv * +subtlv_encode_color( + struct bgp_tea_subtlv_color *st) +{ + struct bgp_attr_encap_subtlv *new; + uint8_t *p; + int total = 8; + + assert(total <= 0xff); + + new = XCALLOC(MTYPE_ENCAP_TLV, sizeof(struct bgp_attr_encap_subtlv) - 1 + total); + assert(new); + new->type = BGP_ENCAP_SUBTLV_TYPE_COLOR; + new->length = total; + p = new->value; + + *p++ = 0x03; /* transitive*/ + *p++ = 0x0b; + *p++ = 0; /* reserved */ + *p++ = 0; /* reserved */ + + *p++ = (st->color & 0xff000000) >> 24; + *p++ = (st->color & 0xff0000) >> 16; + *p++ = (st->color & 0xff00) >> 8; + *p++ = (st->color & 0xff); + + return new; +} + +/* rfc 5566 4. */ +static struct bgp_attr_encap_subtlv * +subtlv_encode_ipsec_ta( + struct bgp_tea_subtlv_ipsec_ta *st) +{ + struct bgp_attr_encap_subtlv *new; + uint8_t *p; + int total = 2 + st->authenticator_length; + + /* sanity check */ + assert(st->authenticator_length <= sizeof(st->value)); + assert(total <= 0xff); + + new = XCALLOC(MTYPE_ENCAP_TLV, sizeof(struct bgp_attr_encap_subtlv) - 1 + total); + assert(new); + new->type = BGP_ENCAP_SUBTLV_TYPE_IPSEC_TA; + new->length = total; + p = new->value; + + *p++ = (st->authenticator_type & 0xff00) >> 8; + *p++ = st->authenticator_type & 0xff; + memcpy(p, st->value, st->authenticator_length); + return new; +} + +/* draft-rosen-idr-tunnel-encaps 2.1 */ +static struct bgp_attr_encap_subtlv * +subtlv_encode_remote_endpoint( + struct bgp_tea_subtlv_remote_endpoint *st) +{ + struct bgp_attr_encap_subtlv *new; + uint8_t *p; + + int total = (st->family==AF_INET?8:20); + + assert(total <= 0xff); + + new = XCALLOC(MTYPE_ENCAP_TLV, sizeof(struct bgp_attr_encap_subtlv) - 1 + total); + assert(new); + new->type = BGP_ENCAP_SUBTLV_TYPE_REMOTE_ENDPOINT; + new->length = total; + p = new->value; + if (st->family == AF_INET) { + memcpy (p, &(st->ip_address.v4.s_addr), 4); + p+=4; + } else { + assert (st->family == AF_INET6); + memcpy (p, &(st->ip_address.v6.s6_addr), 16); + p+=16; + } + memcpy (p, &(st->as4), 4); + return new; +} + +/*********************************************************************** + * TUNNEL TYPE-SPECIFIC TLV ENCODE + ***********************************************************************/ + +/* + * requires "extra" and "last" to be defined in caller + */ +#define ENC_SUBTLV(flag, function, field) do {\ + struct bgp_attr_encap_subtlv *new;\ + if (CHECK_FLAG(bet->valid_subtlvs, (flag))) {\ + new = function(&bet->field);\ + if (last) {\ + last->next = new;\ + } else {\ + extra->encap_subtlvs = new;\ + }\ + last = new;\ + }\ +} while (0) + +void +bgp_encap_type_l2tpv3overip_to_tlv( + struct bgp_encap_type_l2tpv3_over_ip *bet, /* input structure */ + struct attr *attr) +{ + struct attr_extra *extra = bgp_attr_extra_get(attr); + struct bgp_attr_encap_subtlv *last; + + /* advance to last subtlv */ + for (last = extra->encap_subtlvs; last && last->next; last = last->next); + + extra->encap_tunneltype = BGP_ENCAP_TYPE_L2TPV3_OVER_IP; + + assert(CHECK_FLAG(bet->valid_subtlvs, BGP_TEA_SUBTLV_ENCAP)); + + ENC_SUBTLV(BGP_TEA_SUBTLV_ENCAP, subtlv_encode_encap_l2tpv3_over_ip, st_encap); + ENC_SUBTLV(BGP_TEA_SUBTLV_PROTO_TYPE, subtlv_encode_proto_type, st_proto); + ENC_SUBTLV(BGP_TEA_SUBTLV_COLOR, subtlv_encode_color, st_color); + ENC_SUBTLV(BGP_TEA_SUBTLV_REMOTE_ENDPOINT, subtlv_encode_remote_endpoint, st_endpoint); +} + +void +bgp_encap_type_gre_to_tlv( + struct bgp_encap_type_gre *bet, /* input structure */ + struct attr *attr) +{ + struct attr_extra *extra = bgp_attr_extra_get(attr); + struct bgp_attr_encap_subtlv *last; + + /* advance to last subtlv */ + for (last = extra->encap_subtlvs; last && last->next; last = last->next); + + extra->encap_tunneltype = BGP_ENCAP_TYPE_GRE; + + ENC_SUBTLV(BGP_TEA_SUBTLV_ENCAP, subtlv_encode_encap_gre, st_encap); + ENC_SUBTLV(BGP_TEA_SUBTLV_PROTO_TYPE, subtlv_encode_proto_type, st_proto); + ENC_SUBTLV(BGP_TEA_SUBTLV_COLOR, subtlv_encode_color, st_color); + ENC_SUBTLV(BGP_TEA_SUBTLV_REMOTE_ENDPOINT, subtlv_encode_remote_endpoint, st_endpoint); +} + +void +bgp_encap_type_ip_in_ip_to_tlv( + struct bgp_encap_type_ip_in_ip *bet, /* input structure */ + struct attr *attr) +{ + struct attr_extra *extra = bgp_attr_extra_get(attr); + struct bgp_attr_encap_subtlv *last; + + /* advance to last subtlv */ + for (last = extra->encap_subtlvs; last && last->next; last = last->next); + + extra->encap_tunneltype = BGP_ENCAP_TYPE_IP_IN_IP; + + ENC_SUBTLV(BGP_TEA_SUBTLV_PROTO_TYPE, subtlv_encode_proto_type, st_proto); + ENC_SUBTLV(BGP_TEA_SUBTLV_COLOR, subtlv_encode_color, st_color); + ENC_SUBTLV(BGP_TEA_SUBTLV_REMOTE_ENDPOINT, subtlv_encode_remote_endpoint, st_endpoint); +} + +void +bgp_encap_type_transmit_tunnel_endpoint( + struct bgp_encap_type_transmit_tunnel_endpoint *bet, /* input structure */ + struct attr *attr) +{ + struct attr_extra *extra = bgp_attr_extra_get(attr); + struct bgp_attr_encap_subtlv *last; + + /* advance to last subtlv */ + for (last = extra->encap_subtlvs; last && last->next; last = last->next); + + extra->encap_tunneltype = BGP_ENCAP_TYPE_TRANSMIT_TUNNEL_ENDPOINT; + + /* no subtlvs for this type */ +} + +void +bgp_encap_type_ipsec_in_tunnel_mode_to_tlv( + struct bgp_encap_type_ipsec_in_tunnel_mode *bet, /* input structure */ + struct attr *attr) +{ + struct attr_extra *extra = bgp_attr_extra_get(attr); + struct bgp_attr_encap_subtlv *last; + + /* advance to last subtlv */ + for (last = extra->encap_subtlvs; last && last->next; last = last->next); + + extra->encap_tunneltype = BGP_ENCAP_TYPE_IPSEC_IN_TUNNEL_MODE; + + ENC_SUBTLV(BGP_TEA_SUBTLV_IPSEC_TA, subtlv_encode_ipsec_ta, st_ipsec_ta); +} + +void +bgp_encap_type_ip_in_ip_tunnel_with_ipsec_transport_mode_to_tlv( + struct bgp_encap_type_ip_in_ip_tunnel_with_ipsec_transport_mode *bet, /* input structure */ + struct attr *attr) +{ + struct attr_extra *extra = bgp_attr_extra_get(attr); + struct bgp_attr_encap_subtlv *last; + + /* advance to last subtlv */ + for (last = extra->encap_subtlvs; last && last->next; last = last->next); + + extra->encap_tunneltype = BGP_ENCAP_TYPE_IP_IN_IP_TUNNEL_WITH_IPSEC_TRANSPORT_MODE; + + ENC_SUBTLV(BGP_TEA_SUBTLV_IPSEC_TA, subtlv_encode_ipsec_ta, st_ipsec_ta); +} + +void +bgp_encap_type_mpls_in_ip_tunnel_with_ipsec_transport_mode_to_tlv( + struct bgp_encap_type_mpls_in_ip_tunnel_with_ipsec_transport_mode *bet, /* input structure */ + struct attr *attr) +{ + struct attr_extra *extra = bgp_attr_extra_get(attr); + struct bgp_attr_encap_subtlv *last; + + /* advance to last subtlv */ + for (last = extra->encap_subtlvs; last && last->next; last = last->next); + + extra->encap_tunneltype = BGP_ENCAP_TYPE_MPLS_IN_IP_TUNNEL_WITH_IPSEC_TRANSPORT_MODE; + + ENC_SUBTLV(BGP_TEA_SUBTLV_IPSEC_TA, subtlv_encode_ipsec_ta, st_ipsec_ta); +} + +void +bgp_encap_type_pbb_to_tlv( + struct bgp_encap_type_pbb *bet, /* input structure */ + struct attr *attr) +{ + struct attr_extra *extra = bgp_attr_extra_get(attr); + struct bgp_attr_encap_subtlv *last; + + /* advance to last subtlv */ + for (last = extra->encap_subtlvs; last && last->next; last = last->next); + + extra->encap_tunneltype = BGP_ENCAP_TYPE_PBB; + + assert(CHECK_FLAG(bet->valid_subtlvs, BGP_TEA_SUBTLV_ENCAP)); + ENC_SUBTLV(BGP_TEA_SUBTLV_ENCAP, subtlv_encode_encap_pbb, st_encap); +} + +void +bgp_encap_type_vxlan_to_tlv( + struct bgp_encap_type_vxlan *bet, /* input structure */ + struct attr *attr) +{ + struct attr_extra *extra = bgp_attr_extra_get(attr); + + extra->encap_tunneltype = BGP_ENCAP_TYPE_VXLAN; +} + +void +bgp_encap_type_nvgre_to_tlv( + struct bgp_encap_type_nvgre *bet, /* input structure */ + struct attr *attr) +{ + struct attr_extra *extra = bgp_attr_extra_get(attr); + + extra->encap_tunneltype = BGP_ENCAP_TYPE_NVGRE; +} + +void +bgp_encap_type_mpls_to_tlv( + struct bgp_encap_type_mpls *bet, /* input structure */ + struct attr *attr) +{ + struct attr_extra *extra = bgp_attr_extra_get(attr); + + extra->encap_tunneltype = BGP_ENCAP_TYPE_MPLS; +} + +void +bgp_encap_type_mpls_in_gre_to_tlv( + struct bgp_encap_type_mpls_in_gre *bet, /* input structure */ + struct attr *attr) +{ + struct attr_extra *extra = bgp_attr_extra_get(attr); + + extra->encap_tunneltype = BGP_ENCAP_TYPE_MPLS_IN_GRE; +} + +void +bgp_encap_type_vxlan_gpe_to_tlv( + struct bgp_encap_type_vxlan_gpe *bet, /* input structure */ + struct attr *attr) +{ + struct attr_extra *extra = bgp_attr_extra_get(attr); + + extra->encap_tunneltype = BGP_ENCAP_TYPE_VXLAN_GPE; +} + +void +bgp_encap_type_mpls_in_udp_to_tlv( + struct bgp_encap_type_mpls_in_udp *bet, /* input structure */ + struct attr *attr) +{ + struct attr_extra *extra = bgp_attr_extra_get(attr); + + extra->encap_tunneltype = BGP_ENCAP_TYPE_MPLS_IN_UDP; +} + + +/*********************************************************************** + * SUBTLV DECODE + ***********************************************************************/ +/* rfc5512 4.1 */ +static int +subtlv_decode_encap_l2tpv3_over_ip( + struct bgp_attr_encap_subtlv *subtlv, + struct bgp_tea_subtlv_encap_l2tpv3_over_ip *st) +{ + if (subtlv->length < 4) { + zlog_debug("%s, subtlv length %d is less than 4", + __func__, subtlv->length); + return -1; + } + + st->sessionid = (subtlv->value[0] << 24) | + (subtlv->value[1] << 16) | + (subtlv->value[2] << 8) | + subtlv->value[3]; + st->cookie_length = subtlv->length - 4; + if (st->cookie_length > sizeof(st->cookie)) { + zlog_debug("%s, subtlv length %d is greater than %d", + __func__, st->cookie_length, (int)sizeof(st->cookie)); + return -1; + } + memcpy(st->cookie, subtlv->value + 4, st->cookie_length); + return 0; +} + +/* rfc5512 4.1 */ +static int +subtlv_decode_encap_gre( + struct bgp_attr_encap_subtlv *subtlv, + struct bgp_tea_subtlv_encap_gre_key *st) +{ + if (subtlv->length != 4) { + zlog_debug("%s, subtlv length %d does not equal 4", + __func__, subtlv->length); + return -1; + } + st->gre_key = (subtlv->value[0] << 24) | + (subtlv->value[1] << 16) | + (subtlv->value[2] << 8) | + subtlv->value[3]; + return 0; +} + +static int +subtlv_decode_encap_pbb( + struct bgp_attr_encap_subtlv *subtlv, + struct bgp_tea_subtlv_encap_pbb *st) +{ + if (subtlv->length != 1 + 3 + 6 + 2) { + zlog_debug("%s, subtlv length %d does not equal %d", + __func__, subtlv->length, 1 + 3 + 6 + 2); + return -1; + } + if (subtlv->value[0] & 0x80) { + st->flag_isid = 1; + st->isid = (subtlv->value[1] << 16) | + (subtlv->value[2] << 8) | + subtlv->value[3]; + } + if (subtlv->value[0] & 0x40) { + st->flag_vid = 1; + st->vid = ((subtlv->value[10] & 0x0f) << 8) | subtlv->value[11]; + } + memcpy(st->macaddr, subtlv->value + 4, 6); + return 0; +} + +/* rfc5512 4.2 */ +static int +subtlv_decode_proto_type( + struct bgp_attr_encap_subtlv *subtlv, + struct bgp_tea_subtlv_proto_type *st) +{ + if (subtlv->length != 2) { + zlog_debug("%s, subtlv length %d does not equal 2", + __func__, subtlv->length); + return -1; + } + st->proto = (subtlv->value[0] << 8) | subtlv->value[1]; + return 0; +} + +/* rfc5512 4.3 */ +static int +subtlv_decode_color( + struct bgp_attr_encap_subtlv *subtlv, + struct bgp_tea_subtlv_color *st) +{ + if (subtlv->length != 8) { + zlog_debug("%s, subtlv length %d does not equal 8", + __func__, subtlv->length); + return -1; + } + if ((subtlv->value[0] != 0x03) || + (subtlv->value[1] != 0x0b) || + (subtlv->value[2] != 0) || + (subtlv->value[3] != 0)) { + zlog_debug("%s, subtlv value 1st 4 bytes are not 0x030b0000", __func__); + return -1; + } + st->color = (subtlv->value[4] << 24) | + (subtlv->value[5] << 16) | + (subtlv->value[6] << 8) | + subtlv->value[7]; + return 0; +} + +/* rfc 5566 4. */ +static int +subtlv_decode_ipsec_ta( + struct bgp_attr_encap_subtlv *subtlv, + struct bgp_tea_subtlv_ipsec_ta *st) +{ + st->authenticator_length = subtlv->length - 2; + if (st->authenticator_length > sizeof(st->value)) { + zlog_debug("%s, authenticator length %d exceeds storage maximum %d", + __func__, st->authenticator_length, (int)sizeof(st->value)); + return -1; + } + st->authenticator_type = (subtlv->value[0] << 8) | subtlv->value[1]; + memcpy(st->value, subtlv->value + 2, st->authenticator_length); + return 0; +} + +/* draft-rosen-idr-tunnel-encaps 2.1 */ +static int +subtlv_decode_remote_endpoint( + struct bgp_attr_encap_subtlv *subtlv, + struct bgp_tea_subtlv_remote_endpoint *st) +{ + int i; + if (subtlv->length != 8 && subtlv->length != 20 ) { + zlog_debug("%s, subtlv length %d does not equal 8 or 20", + __func__, subtlv->length); + return -1; + } + if (subtlv->length == 8) { + st->family = AF_INET; + st->ip_address.v4.s_addr = ((subtlv->value[0] << 24) | + (subtlv->value[1] << 16) | + (subtlv->value[2] << 8) | + subtlv->value[3]); + } else { + st->family = AF_INET6; + memcpy (&(st->ip_address.v6.s6_addr), subtlv->value, 16); + } + i = subtlv->length - 4; + st->as4 = ((subtlv->value[i] << 24) | + (subtlv->value[i+1] << 16) | + (subtlv->value[i+2] << 8) | + subtlv->value[i+3]); + return 0; +} + +/*********************************************************************** + * TUNNEL TYPE-SPECIFIC TLV DECODE + ***********************************************************************/ + +int +tlv_to_bgp_encap_type_l2tpv3overip( + struct bgp_attr_encap_subtlv *stlv, /* subtlv chain */ + struct bgp_encap_type_l2tpv3_over_ip *bet) /* caller-allocated */ +{ + struct bgp_attr_encap_subtlv *st; + int rc = 0; + + for (st = stlv; st; st = st->next) { + switch (st->type) { + case BGP_ENCAP_SUBTLV_TYPE_ENCAPSULATION: + rc |= subtlv_decode_encap_l2tpv3_over_ip(st, &bet->st_encap); + SET_SUBTLV_FLAG(bet, BGP_TEA_SUBTLV_ENCAP); + break; + + case BGP_ENCAP_SUBTLV_TYPE_PROTO_TYPE: + rc |= subtlv_decode_proto_type(st, &bet->st_proto); + SET_SUBTLV_FLAG(bet, BGP_TEA_SUBTLV_PROTO_TYPE); + break; + + case BGP_ENCAP_SUBTLV_TYPE_COLOR: + rc |= subtlv_decode_color(st, &bet->st_color); + SET_SUBTLV_FLAG(bet, BGP_TEA_SUBTLV_COLOR); + break; + + case BGP_ENCAP_SUBTLV_TYPE_REMOTE_ENDPOINT: + rc |= subtlv_decode_remote_endpoint(st, &bet->st_endpoint); + SET_SUBTLV_FLAG(bet, BGP_TEA_SUBTLV_REMOTE_ENDPOINT); + break; + + default: + zlog_debug("%s: unexpected subtlv type %d", __func__, st->type); + rc |= -1; + break; + } + } + return rc; +} + +int +tlv_to_bgp_encap_type_gre( + struct bgp_attr_encap_subtlv *stlv, /* subtlv chain */ + struct bgp_encap_type_gre *bet) /* caller-allocated */ +{ + struct bgp_attr_encap_subtlv *st; + int rc = 0; + + for (st = stlv; st; st = st->next) { + switch (st->type) { + case BGP_ENCAP_SUBTLV_TYPE_ENCAPSULATION: + rc |= subtlv_decode_encap_gre(st, &bet->st_encap); + SET_SUBTLV_FLAG(bet, BGP_TEA_SUBTLV_ENCAP); + break; + + case BGP_ENCAP_SUBTLV_TYPE_PROTO_TYPE: + rc |= subtlv_decode_proto_type(st, &bet->st_proto); + SET_SUBTLV_FLAG(bet, BGP_TEA_SUBTLV_PROTO_TYPE); + break; + + case BGP_ENCAP_SUBTLV_TYPE_COLOR: + rc |= subtlv_decode_color(st, &bet->st_color); + SET_SUBTLV_FLAG(bet, BGP_TEA_SUBTLV_COLOR); + break; + + case BGP_ENCAP_SUBTLV_TYPE_REMOTE_ENDPOINT: + rc |= subtlv_decode_remote_endpoint(st, &bet->st_endpoint); + SET_SUBTLV_FLAG(bet, BGP_TEA_SUBTLV_REMOTE_ENDPOINT); + break; + + default: + zlog_debug("%s: unexpected subtlv type %d", __func__, st->type); + rc |= -1; + break; + } + } + return rc; +} + +int +tlv_to_bgp_encap_type_ip_in_ip( + struct bgp_attr_encap_subtlv *stlv, /* subtlv chain */ + struct bgp_encap_type_ip_in_ip *bet) /* caller-allocated */ +{ + struct bgp_attr_encap_subtlv *st; + int rc = 0; + + for (st = stlv; st; st = st->next) { + switch (st->type) { + case BGP_ENCAP_SUBTLV_TYPE_PROTO_TYPE: + rc |= subtlv_decode_proto_type(st, &bet->st_proto); + SET_SUBTLV_FLAG(bet, BGP_TEA_SUBTLV_PROTO_TYPE); + break; + + case BGP_ENCAP_SUBTLV_TYPE_COLOR: + rc |= subtlv_decode_color(st, &bet->st_color); + SET_SUBTLV_FLAG(bet, BGP_TEA_SUBTLV_COLOR); + break; + + case BGP_ENCAP_SUBTLV_TYPE_REMOTE_ENDPOINT: + rc |= subtlv_decode_remote_endpoint(st, &bet->st_endpoint); + SET_SUBTLV_FLAG(bet, BGP_TEA_SUBTLV_REMOTE_ENDPOINT); + break; + + default: + zlog_debug("%s: unexpected subtlv type %d", __func__, st->type); + rc |= -1; + break; + } + } + return rc; +} + +int +tlv_to_bgp_encap_type_transmit_tunnel_endpoint( + struct bgp_attr_encap_subtlv *stlv, + struct bgp_encap_type_transmit_tunnel_endpoint *bet) +{ + struct bgp_attr_encap_subtlv *st; + int rc = 0; + + for (st = stlv; st; st = st->next) { + switch (st->type) { + + case BGP_ENCAP_SUBTLV_TYPE_REMOTE_ENDPOINT: + rc |= subtlv_decode_remote_endpoint(st, &bet->st_endpoint); + SET_SUBTLV_FLAG(bet, BGP_TEA_SUBTLV_REMOTE_ENDPOINT); + break; + + default: + zlog_debug("%s: unexpected subtlv type %d", __func__, st->type); + rc |= -1; + break; + } + } + return rc; +} + +int +tlv_to_bgp_encap_type_ipsec_in_tunnel_mode( + struct bgp_attr_encap_subtlv *stlv, /* subtlv chain */ + struct bgp_encap_type_ipsec_in_tunnel_mode *bet) /* caller-allocated */ +{ + struct bgp_attr_encap_subtlv *st; + int rc = 0; + + for (st = stlv; st; st = st->next) { + switch (st->type) { + case BGP_ENCAP_SUBTLV_TYPE_IPSEC_TA: + rc |= subtlv_decode_ipsec_ta(st, &bet->st_ipsec_ta); + SET_SUBTLV_FLAG(bet, BGP_TEA_SUBTLV_IPSEC_TA); + break; + + case BGP_ENCAP_SUBTLV_TYPE_REMOTE_ENDPOINT: + rc |= subtlv_decode_remote_endpoint(st, &bet->st_endpoint); + SET_SUBTLV_FLAG(bet, BGP_TEA_SUBTLV_REMOTE_ENDPOINT); + break; + + default: + zlog_debug("%s: unexpected subtlv type %d", __func__, st->type); + rc |= -1; + break; + } + } + return rc; +} + +int +tlv_to_bgp_encap_type_ip_in_ip_tunnel_with_ipsec_transport_mode( + struct bgp_attr_encap_subtlv *stlv, + struct bgp_encap_type_ip_in_ip_tunnel_with_ipsec_transport_mode *bet) +{ + struct bgp_attr_encap_subtlv *st; + int rc = 0; + + for (st = stlv; st; st = st->next) { + switch (st->type) { + case BGP_ENCAP_SUBTLV_TYPE_IPSEC_TA: + rc |= subtlv_decode_ipsec_ta(st, &bet->st_ipsec_ta); + SET_SUBTLV_FLAG(bet, BGP_TEA_SUBTLV_IPSEC_TA); + break; + + case BGP_ENCAP_SUBTLV_TYPE_REMOTE_ENDPOINT: + rc |= subtlv_decode_remote_endpoint(st, &bet->st_endpoint); + SET_SUBTLV_FLAG(bet, BGP_TEA_SUBTLV_REMOTE_ENDPOINT); + break; + + default: + zlog_debug("%s: unexpected subtlv type %d", __func__, st->type); + rc |= -1; + break; + } + } + return rc; +} + +int +tlv_to_bgp_encap_type_mpls_in_ip_tunnel_with_ipsec_transport_mode( + struct bgp_attr_encap_subtlv *stlv, + struct bgp_encap_type_mpls_in_ip_tunnel_with_ipsec_transport_mode *bet) +{ + struct bgp_attr_encap_subtlv *st; + int rc = 0; + + for (st = stlv; st; st = st->next) { + switch (st->type) { + case BGP_ENCAP_SUBTLV_TYPE_IPSEC_TA: + rc |= subtlv_decode_ipsec_ta(st, &bet->st_ipsec_ta); + SET_SUBTLV_FLAG(bet, BGP_TEA_SUBTLV_IPSEC_TA); + break; + + case BGP_ENCAP_SUBTLV_TYPE_REMOTE_ENDPOINT: + rc |= subtlv_decode_remote_endpoint(st, &bet->st_endpoint); + SET_SUBTLV_FLAG(bet, BGP_TEA_SUBTLV_REMOTE_ENDPOINT); + break; + + default: + zlog_debug("%s: unexpected subtlv type %d", __func__, st->type); + rc |= -1; + break; + } + } + return rc; +} + +int +tlv_to_bgp_encap_type_vxlan( + struct bgp_attr_encap_subtlv *stlv, + struct bgp_encap_type_vxlan *bet) +{ + struct bgp_attr_encap_subtlv *st; + int rc = 0; + + for (st = stlv; st; st = st->next) { + switch (st->type) { + + case BGP_ENCAP_SUBTLV_TYPE_REMOTE_ENDPOINT: + rc |= subtlv_decode_remote_endpoint(st, &bet->st_endpoint); + SET_SUBTLV_FLAG(bet, BGP_TEA_SUBTLV_REMOTE_ENDPOINT); + break; + + default: + zlog_debug("%s: unexpected subtlv type %d", __func__, st->type); + rc |= -1; + break; + } + } + return rc; +} + +int +tlv_to_bgp_encap_type_nvgre( + struct bgp_attr_encap_subtlv *stlv, + struct bgp_encap_type_nvgre *bet) +{ + struct bgp_attr_encap_subtlv *st; + int rc = 0; + + for (st = stlv; st; st = st->next) { + switch (st->type) { + + case BGP_ENCAP_SUBTLV_TYPE_REMOTE_ENDPOINT: + rc |= subtlv_decode_remote_endpoint(st, &bet->st_endpoint); + SET_SUBTLV_FLAG(bet, BGP_TEA_SUBTLV_REMOTE_ENDPOINT); + break; + + default: + zlog_debug("%s: unexpected subtlv type %d", __func__, st->type); + rc |= -1; + break; + } + } + return rc; +} + +int +tlv_to_bgp_encap_type_mpls( + struct bgp_attr_encap_subtlv *stlv, + struct bgp_encap_type_mpls *bet) +{ + struct bgp_attr_encap_subtlv *st; + int rc = 0; + + for (st = stlv; st; st = st->next) { + switch (st->type) { + + case BGP_ENCAP_SUBTLV_TYPE_REMOTE_ENDPOINT: + rc |= subtlv_decode_remote_endpoint(st, &bet->st_endpoint); + SET_SUBTLV_FLAG(bet, BGP_TEA_SUBTLV_REMOTE_ENDPOINT); + break; + + default: + zlog_debug("%s: unexpected subtlv type %d", __func__, st->type); + rc |= -1; + break; + } + } + return rc; +} + +int +tlv_to_bgp_encap_type_mpls_in_gre( + struct bgp_attr_encap_subtlv *stlv, + struct bgp_encap_type_mpls_in_gre *bet) +{ + struct bgp_attr_encap_subtlv *st; + int rc = 0; + + for (st = stlv; st; st = st->next) { + switch (st->type) { + + case BGP_ENCAP_SUBTLV_TYPE_REMOTE_ENDPOINT: + rc |= subtlv_decode_remote_endpoint(st, &bet->st_endpoint); + SET_SUBTLV_FLAG(bet, BGP_TEA_SUBTLV_REMOTE_ENDPOINT); + break; + + default: + zlog_debug("%s: unexpected subtlv type %d", __func__, st->type); + rc |= -1; + break; + } + } + return rc; +} + +int +tlv_to_bgp_encap_type_vxlan_gpe( + struct bgp_attr_encap_subtlv *stlv, + struct bgp_encap_type_vxlan_gpe *bet) +{ + struct bgp_attr_encap_subtlv *st; + int rc = 0; + + for (st = stlv; st; st = st->next) { + switch (st->type) { + + case BGP_ENCAP_SUBTLV_TYPE_REMOTE_ENDPOINT: + rc |= subtlv_decode_remote_endpoint(st, &bet->st_endpoint); + SET_SUBTLV_FLAG(bet, BGP_TEA_SUBTLV_REMOTE_ENDPOINT); + break; + + default: + zlog_debug("%s: unexpected subtlv type %d", __func__, st->type); + rc |= -1; + break; + } + } + return rc; +} + +int +tlv_to_bgp_encap_type_mpls_in_udp( + struct bgp_attr_encap_subtlv *stlv, + struct bgp_encap_type_mpls_in_udp *bet) +{ + struct bgp_attr_encap_subtlv *st; + int rc = 0; + + for (st = stlv; st; st = st->next) { + switch (st->type) { + + case BGP_ENCAP_SUBTLV_TYPE_REMOTE_ENDPOINT: + rc |= subtlv_decode_remote_endpoint(st, &bet->st_endpoint); + SET_SUBTLV_FLAG(bet, BGP_TEA_SUBTLV_REMOTE_ENDPOINT); + break; + + default: + zlog_debug("%s: unexpected subtlv type %d", __func__, st->type); + rc |= -1; + break; + } + } + return rc; +} + +int +tlv_to_bgp_encap_type_pbb( + struct bgp_attr_encap_subtlv *stlv, /* subtlv chain */ + struct bgp_encap_type_pbb *bet) /* caller-allocated */ +{ + struct bgp_attr_encap_subtlv *st; + int rc = 0; + + for (st = stlv; st; st = st->next) { + switch (st->type) { + case BGP_ENCAP_SUBTLV_TYPE_ENCAPSULATION: + rc |= subtlv_decode_encap_pbb(st, &bet->st_encap); + SET_SUBTLV_FLAG(bet, BGP_TEA_SUBTLV_ENCAP); + break; + + case BGP_ENCAP_SUBTLV_TYPE_REMOTE_ENDPOINT: + rc |= subtlv_decode_remote_endpoint(st, &bet->st_endpoint); + SET_SUBTLV_FLAG(bet, BGP_TEA_SUBTLV_REMOTE_ENDPOINT); + break; + + default: + zlog_debug("%s: unexpected subtlv type %d", __func__, st->type); + rc |= -1; + break; + } + } + return rc; +} + diff --git a/bgpd/bgp_encap_tlv.h b/bgpd/bgp_encap_tlv.h new file mode 100644 index 000000000..d94d544d2 --- /dev/null +++ b/bgpd/bgp_encap_tlv.h @@ -0,0 +1,177 @@ +/* + * Copyright 2015, LabN Consulting, L.L.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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#ifndef _QUAGGA_BGP_ENCAP_TLV_H +#define _QUAGGA_BGP_ENCAP_TLV_H + + +/*********************************************************************** + * TUNNEL TYPE-SPECIFIC TLV ENCODE + ***********************************************************************/ + +extern void +bgp_encap_type_l2tpv3overip_to_tlv( + struct bgp_encap_type_l2tpv3_over_ip *bet, + struct attr *attr); + +extern void +bgp_encap_type_gre_to_tlv( + struct bgp_encap_type_gre *bet, + struct attr *attr); + +extern void +bgp_encap_type_ip_in_ip_to_tlv( + struct bgp_encap_type_ip_in_ip *bet, + struct attr *attr); + +extern void +bgp_encap_type_transmit_tunnel_endpoint( + struct bgp_encap_type_transmit_tunnel_endpoint *bet, + struct attr *attr); + +extern void +bgp_encap_type_ipsec_in_tunnel_mode_to_tlv( + struct bgp_encap_type_ipsec_in_tunnel_mode *bet, + struct attr *attr); + +extern void +bgp_encap_type_ip_in_ip_tunnel_with_ipsec_transport_mode_to_tlv( + struct bgp_encap_type_ip_in_ip_tunnel_with_ipsec_transport_mode *bet, + struct attr *attr); + +extern void +bgp_encap_type_mpls_in_ip_tunnel_with_ipsec_transport_mode_to_tlv( + struct bgp_encap_type_mpls_in_ip_tunnel_with_ipsec_transport_mode *bet, + struct attr *attr); + +extern void +bgp_encap_type_pbb_to_tlv( + struct bgp_encap_type_pbb *bet, + struct attr *attr); + +extern void +bgp_encap_type_vxlan_to_tlv( + struct bgp_encap_type_vxlan *bet, + struct attr *attr); + +extern void +bgp_encap_type_nvgre_to_tlv( + struct bgp_encap_type_nvgre *bet, + struct attr *attr); + +extern void +bgp_encap_type_mpls_to_tlv( + struct bgp_encap_type_mpls *bet, + struct attr *attr); + +extern void +bgp_encap_type_mpls_in_gre_to_tlv( + struct bgp_encap_type_mpls_in_gre *bet, + struct attr *attr); + +extern void +bgp_encap_type_vxlan_gpe_to_tlv( + struct bgp_encap_type_vxlan_gpe *bet, + struct attr *attr); + +extern void +bgp_encap_type_mpls_in_udp_to_tlv( + struct bgp_encap_type_mpls_in_udp *bet, + struct attr *attr); + +/*********************************************************************** + * TUNNEL TYPE-SPECIFIC TLV DECODE + ***********************************************************************/ + +extern int +tlv_to_bgp_encap_type_l2tpv3overip( + struct bgp_attr_encap_subtlv *stlv, /* subtlv chain */ + struct bgp_encap_type_l2tpv3_over_ip *bet); /* caller-allocated */ + +extern int +tlv_to_bgp_encap_type_gre( + struct bgp_attr_encap_subtlv *stlv, /* subtlv chain */ + struct bgp_encap_type_gre *bet); /* caller-allocated */ + +extern int +tlv_to_bgp_encap_type_ip_in_ip( + struct bgp_attr_encap_subtlv *stlv, /* subtlv chain */ + struct bgp_encap_type_ip_in_ip *bet); /* caller-allocated */ + +extern int +tlv_to_bgp_encap_type_transmit_tunnel_endpoint( + struct bgp_attr_encap_subtlv *stlv, + struct bgp_encap_type_transmit_tunnel_endpoint *bet); + +extern int +tlv_to_bgp_encap_type_ipsec_in_tunnel_mode( + struct bgp_attr_encap_subtlv *stlv, /* subtlv chain */ + struct bgp_encap_type_ipsec_in_tunnel_mode *bet); /* caller-allocated */ + +extern int +tlv_to_bgp_encap_type_ip_in_ip_tunnel_with_ipsec_transport_mode( + struct bgp_attr_encap_subtlv *stlv, + struct bgp_encap_type_ip_in_ip_tunnel_with_ipsec_transport_mode *bet); + +extern int +tlv_to_bgp_encap_type_mpls_in_ip_tunnel_with_ipsec_transport_mode( + struct bgp_attr_encap_subtlv *stlv, + struct bgp_encap_type_mpls_in_ip_tunnel_with_ipsec_transport_mode *bet); + +extern int +tlv_to_bgp_encap_type_vxlan( + struct bgp_attr_encap_subtlv *stlv, + struct bgp_encap_type_vxlan *bet); + +extern int +tlv_to_bgp_encap_type_nvgre( + struct bgp_attr_encap_subtlv *stlv, + struct bgp_encap_type_nvgre *bet); + +extern int +tlv_to_bgp_encap_type_mpls( + struct bgp_attr_encap_subtlv *stlv, + struct bgp_encap_type_mpls *bet); + +extern int +tlv_to_bgp_encap_type_mpls( + struct bgp_attr_encap_subtlv *stlv, + struct bgp_encap_type_mpls *bet); + +extern int +tlv_to_bgp_encap_type_mpls_in_gre( + struct bgp_attr_encap_subtlv *stlv, + struct bgp_encap_type_mpls_in_gre *bet); + +extern int +tlv_to_bgp_encap_type_vxlan_gpe( + struct bgp_attr_encap_subtlv *stlv, + struct bgp_encap_type_vxlan_gpe *bet); + +extern int +tlv_to_bgp_encap_type_mpls_in_udp( + struct bgp_attr_encap_subtlv *stlv, + struct bgp_encap_type_mpls_in_udp *bet); + +extern int +tlv_to_bgp_encap_type_pbb( + struct bgp_attr_encap_subtlv *stlv, /* subtlv chain */ + struct bgp_encap_type_pbb *bet); /* caller-allocated */ + +#endif /* _QUAGGA_BGP_ENCAP_TLV_H */ diff --git a/bgpd/bgp_encap_types.h b/bgpd/bgp_encap_types.h new file mode 100644 index 000000000..603ff9d2d --- /dev/null +++ b/bgpd/bgp_encap_types.h @@ -0,0 +1,212 @@ +/* + * Copyright 2015, LabN Consulting, L.L.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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#ifndef _QUAGGA_BGP_ENCAP_TYPES_H +#define _QUAGGA_BGP_ENCAP_TYPES_H + +/* from http://www.iana.org/assignments/bgp-parameters/bgp-parameters.xhtml#tunnel-types */ +typedef enum { + BGP_ENCAP_TYPE_RESERVED=0, + BGP_ENCAP_TYPE_L2TPV3_OVER_IP=1, + BGP_ENCAP_TYPE_GRE=2, + BGP_ENCAP_TYPE_TRANSMIT_TUNNEL_ENDPOINT=3, + BGP_ENCAP_TYPE_IPSEC_IN_TUNNEL_MODE=4, + BGP_ENCAP_TYPE_IP_IN_IP_TUNNEL_WITH_IPSEC_TRANSPORT_MODE=5, + BGP_ENCAP_TYPE_MPLS_IN_IP_TUNNEL_WITH_IPSEC_TRANSPORT_MODE=6, + BGP_ENCAP_TYPE_IP_IN_IP=7, + BGP_ENCAP_TYPE_VXLAN=8, + BGP_ENCAP_TYPE_NVGRE=9, + BGP_ENCAP_TYPE_MPLS=10, + BGP_ENCAP_TYPE_MPLS_IN_GRE=11, + BGP_ENCAP_TYPE_VXLAN_GPE=12, + BGP_ENCAP_TYPE_MPLS_IN_UDP=13, + BGP_ENCAP_TYPE_PBB +} bgp_encap_types; + +typedef enum { + BGP_ENCAP_SUBTLV_TYPE_ENCAPSULATION=1, + BGP_ENCAP_SUBTLV_TYPE_PROTO_TYPE=2, + BGP_ENCAP_SUBTLV_TYPE_IPSEC_TA=3, + BGP_ENCAP_SUBTLV_TYPE_COLOR=4, + BGP_ENCAP_SUBTLV_TYPE_REMOTE_ENDPOINT=6 /* speculative, IANA assignment TBD */ +} bgp_encap_subtlv_types; + +/* + * Tunnel Encapsulation Attribute subtlvs + */ +struct bgp_tea_subtlv_encap_l2tpv3_over_ip { + uint32_t sessionid; + uint8_t cookie_length; + uint8_t cookie[8]; +}; + +struct bgp_tea_subtlv_encap_gre_key { + uint32_t gre_key; +}; + +struct bgp_tea_subtlv_encap_pbb { + uint32_t flag_isid:1; + uint32_t flag_vid:1; + uint32_t isid:24; + uint16_t vid:12; + uint8_t macaddr[6]; +}; + +struct bgp_tea_subtlv_proto_type { + uint16_t proto; /* ether-type */ +}; + +struct bgp_tea_subtlv_color { + uint32_t color; +}; + +/* per draft-rosen-idr-tunnel-encaps */ +struct bgp_tea_subtlv_remote_endpoint { + u_char family; /* IPv4 or IPv6 */ + union { + struct in_addr v4; + struct in6_addr v6; + } ip_address; + as_t as4; /* always 4 bytes */ +}; + +/* + * This is the length of the value part of the ipsec tunnel authenticator + * subtlv. Currently we only support the length for authenticator type 1. + */ +#define BGP_ENCAP_SUBTLV_IPSEC_TA_SIZE 20 + +struct bgp_tea_subtlv_ipsec_ta { + uint16_t authenticator_type; /* only type 1 is supported so far */ + uint8_t authenticator_length; /* octets in value field */ + uint8_t value[BGP_ENCAP_SUBTLV_IPSEC_TA_SIZE]; +}; + +/* + * Subtlv valid flags + * TBD change names to add "VALID" + */ +#define BGP_TEA_SUBTLV_ENCAP 0x00000001 +#define BGP_TEA_SUBTLV_PROTO_TYPE 0x00000002 +#define BGP_TEA_SUBTLV_COLOR 0x00000004 +#define BGP_TEA_SUBTLV_IPSEC_TA 0x00000008 +#define BGP_TEA_SUBTLV_REMOTE_ENDPOINT 0x00000010 + +#define CHECK_SUBTLV_FLAG(ptr, flag) CHECK_FLAG((ptr)->valid_subtlvs, (flag)) +#define SET_SUBTLV_FLAG(ptr, flag) SET_FLAG((ptr)->valid_subtlvs, (flag)) +#define UNSET_SUBTLV_FLAG(ptr, flag) UNSET_FLAG((ptr)->valid_subtlvs, (flag)) + +/* + * Tunnel Type-specific APIs + */ +struct bgp_encap_type_reserved { + uint32_t valid_subtlvs; + struct bgp_tea_subtlv_remote_endpoint st_endpoint; /* optional */ +}; + +struct bgp_encap_type_l2tpv3_over_ip { + uint32_t valid_subtlvs; + struct bgp_tea_subtlv_encap_l2tpv3_over_ip st_encap; + struct bgp_tea_subtlv_proto_type st_proto; /* optional */ + struct bgp_tea_subtlv_color st_color; /* optional */ + struct bgp_tea_subtlv_remote_endpoint st_endpoint; /* optional */ +}; + +struct bgp_encap_type_gre { + uint32_t valid_subtlvs; + struct bgp_tea_subtlv_encap_gre_key st_encap; /* optional */ + struct bgp_tea_subtlv_proto_type st_proto; /* optional */ + struct bgp_tea_subtlv_color st_color; /* optional */ + struct bgp_tea_subtlv_remote_endpoint st_endpoint; /* optional */ +}; + +struct bgp_encap_type_ip_in_ip { + uint32_t valid_subtlvs; + struct bgp_tea_subtlv_proto_type st_proto; /* optional */ + struct bgp_tea_subtlv_color st_color; /* optional */ + struct bgp_tea_subtlv_remote_endpoint st_endpoint; /* optional */ +}; + +struct bgp_encap_type_transmit_tunnel_endpoint { + uint32_t valid_subtlvs; + struct bgp_tea_subtlv_remote_endpoint st_endpoint; /* optional */ + /* No subtlvs defined in spec? */ +}; + +struct bgp_encap_type_ipsec_in_tunnel_mode { + uint32_t valid_subtlvs; + struct bgp_tea_subtlv_ipsec_ta st_ipsec_ta; /* optional */ + struct bgp_tea_subtlv_remote_endpoint st_endpoint; /* optional */ +}; + +struct bgp_encap_type_ip_in_ip_tunnel_with_ipsec_transport_mode { + uint32_t valid_subtlvs; + struct bgp_tea_subtlv_ipsec_ta st_ipsec_ta; /* optional */ + struct bgp_tea_subtlv_remote_endpoint st_endpoint; /* optional */ +}; + +struct bgp_encap_type_mpls_in_ip_tunnel_with_ipsec_transport_mode { + uint32_t valid_subtlvs; + struct bgp_tea_subtlv_ipsec_ta st_ipsec_ta; /* optional */ + struct bgp_tea_subtlv_remote_endpoint st_endpoint; /* optional */ +}; + +struct bgp_encap_type_vxlan { + uint32_t valid_subtlvs; + struct bgp_tea_subtlv_remote_endpoint st_endpoint; /* optional */ + /* No subtlvs defined in spec? */ +}; + +struct bgp_encap_type_nvgre { + uint32_t valid_subtlvs; + struct bgp_tea_subtlv_remote_endpoint st_endpoint; /* optional */ + /* No subtlvs defined in spec? */ +}; + +struct bgp_encap_type_mpls { + uint32_t valid_subtlvs; + struct bgp_tea_subtlv_remote_endpoint st_endpoint; /* optional */ + /* No subtlvs defined in spec? */ +}; + +struct bgp_encap_type_mpls_in_gre { + uint32_t valid_subtlvs; + struct bgp_tea_subtlv_remote_endpoint st_endpoint; /* optional */ + /* No subtlvs defined in spec? */ +}; + +struct bgp_encap_type_vxlan_gpe { + uint32_t valid_subtlvs; + struct bgp_tea_subtlv_remote_endpoint st_endpoint; /* optional */ + /* No subtlvs defined in spec? */ +}; + +struct bgp_encap_type_mpls_in_udp { + uint32_t valid_subtlvs; + struct bgp_tea_subtlv_remote_endpoint st_endpoint; /* optional */ + /* No subtlvs defined in spec? */ +}; + +struct bgp_encap_type_pbb { + uint32_t valid_subtlvs; + struct bgp_tea_subtlv_remote_endpoint st_endpoint; /* optional */ + struct bgp_tea_subtlv_encap_pbb st_encap; +}; + +#endif /* _QUAGGA_BGP_ENCAP_TYPES_H */ diff --git a/bgpd/bgp_filter.c b/bgpd/bgp_filter.c index 8ee62b013..ab1e504d6 100644 --- a/bgpd/bgp_filter.c +++ b/bgpd/bgp_filter.c @@ -24,6 +24,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "log.h" #include "memory.h" #include "buffer.h" +#include "filter.h" #include "bgpd/bgpd.h" #include "bgpd/bgp_aspath.h" @@ -65,18 +66,12 @@ struct as_filter char *reg_str; }; -enum as_list_type -{ - ACCESS_TYPE_STRING, - ACCESS_TYPE_NUMBER -}; - /* AS path filter list. */ struct as_list { char *name; - enum as_list_type type; + enum access_type type; struct as_list *next; struct as_list *prev; @@ -84,7 +79,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 +365,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 +407,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) { @@ -696,8 +691,6 @@ bgp_filter_init (void) install_element (VIEW_NODE, &show_ip_as_path_access_list_cmd); install_element (VIEW_NODE, &show_ip_as_path_access_list_all_cmd); - install_element (ENABLE_NODE, &show_ip_as_path_access_list_cmd); - install_element (ENABLE_NODE, &show_ip_as_path_access_list_all_cmd); } void diff --git a/bgpd/bgp_fsm.c b/bgpd/bgp_fsm.c index 813e59ef8..4198a8e42 100644 --- a/bgpd/bgp_fsm.c +++ b/bgpd/bgp_fsm.c @@ -30,6 +30,8 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "stream.h" #include "memory.h" #include "plist.h" +#include "workqueue.h" +#include "filter.h" #include "bgpd/bgpd.h" #include "bgpd/bgp_attr.h" @@ -40,10 +42,11 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "bgpd/bgp_route.h" #include "bgpd/bgp_dump.h" #include "bgpd/bgp_open.h" +#include "bgpd/bgp_nht.h" #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 @@ -65,7 +68,7 @@ static int bgp_start (struct peer *); static int bgp_start_jitter (int time) { - return ((rand () % (time + 1)) - (time / 2)); + return ((random () % (time + 1)) - (time / 2)); } /* Check if suppress start/restart of sessions to peer. */ @@ -100,19 +103,17 @@ bgp_timer_set (struct peer *peer) BGP_TIMER_OFF (peer->t_connect); BGP_TIMER_OFF (peer->t_holdtime); BGP_TIMER_OFF (peer->t_keepalive); - BGP_TIMER_OFF (peer->t_asorig); BGP_TIMER_OFF (peer->t_routeadv); break; case Connect: - /* After start timer is expired, the peer moves to Connnect + /* After start timer is expired, the peer moves to Connect status. Make sure start timer is off and connect timer is on. */ BGP_TIMER_OFF (peer->t_start); BGP_TIMER_ON (peer->t_connect, bgp_connect_timer, peer->v_connect); BGP_TIMER_OFF (peer->t_holdtime); BGP_TIMER_OFF (peer->t_keepalive); - BGP_TIMER_OFF (peer->t_asorig); BGP_TIMER_OFF (peer->t_routeadv); break; @@ -132,7 +133,6 @@ bgp_timer_set (struct peer *peer) } BGP_TIMER_OFF (peer->t_holdtime); BGP_TIMER_OFF (peer->t_keepalive); - BGP_TIMER_OFF (peer->t_asorig); BGP_TIMER_OFF (peer->t_routeadv); break; @@ -150,7 +150,6 @@ bgp_timer_set (struct peer *peer) BGP_TIMER_OFF (peer->t_holdtime); } BGP_TIMER_OFF (peer->t_keepalive); - BGP_TIMER_OFF (peer->t_asorig); BGP_TIMER_OFF (peer->t_routeadv); break; @@ -173,7 +172,6 @@ bgp_timer_set (struct peer *peer) BGP_TIMER_ON (peer->t_keepalive, bgp_keepalive_timer, peer->v_keepalive); } - BGP_TIMER_OFF (peer->t_asorig); BGP_TIMER_OFF (peer->t_routeadv); break; @@ -197,7 +195,6 @@ bgp_timer_set (struct peer *peer) BGP_TIMER_ON (peer->t_keepalive, bgp_keepalive_timer, peer->v_keepalive); } - BGP_TIMER_OFF (peer->t_asorig); break; case Deleted: BGP_TIMER_OFF (peer->t_gr_restart); @@ -208,7 +205,6 @@ bgp_timer_set (struct peer *peer) BGP_TIMER_OFF (peer->t_connect); BGP_TIMER_OFF (peer->t_holdtime); BGP_TIMER_OFF (peer->t_keepalive); - BGP_TIMER_OFF (peer->t_asorig); BGP_TIMER_OFF (peer->t_routeadv); } } @@ -406,7 +402,23 @@ bgp_fsm_change_status (struct peer *peer, int status) * (and must do so before actually changing into Deleted.. */ if (status >= Clearing) - bgp_clear_route_all (peer); + { + bgp_clear_route_all (peer); + + /* If no route was queued for the clear-node processing, generate the + * completion event here. This is needed because if there are no routes + * to trigger the background clear-node thread, the event won't get + * generated and the peer would be stuck in Clearing. Note that this + * event is for the peer and helps the peer transition out of Clearing + * state; it should not be generated per (AFI,SAFI). The event is + * directly posted here without calling clear_node_complete() as we + * shouldn't do an extra unlock. This event will get processed after + * the state change that happens below, so peer will be in Clearing + * (or Deleted). + */ + if (!work_queue_is_scheduled (peer->clear_node_queue)) + BGP_EVENT_ADD (peer, Clearing_Completed); + } /* Preserve old status and change into new status. */ peer->ostatus = peer->status; @@ -495,7 +507,7 @@ bgp_stop (struct peer *peer) /* Reset peer synctime */ peer->synctime = 0; } - + /* Stop read and write threads when exists. */ BGP_READ_OFF (peer->t_read); BGP_WRITE_OFF (peer->t_write); @@ -505,7 +517,6 @@ bgp_stop (struct peer *peer) BGP_TIMER_OFF (peer->t_connect); BGP_TIMER_OFF (peer->t_holdtime); BGP_TIMER_OFF (peer->t_keepalive); - BGP_TIMER_OFF (peer->t_asorig); BGP_TIMER_OFF (peer->t_routeadv); /* Stream reset. */ @@ -545,7 +556,7 @@ bgp_stop (struct peer *peer) /* ORF received prefix-filter pnt */ sprintf (orf_name, "%s.%d.%d", peer->host, afi, safi); - prefix_bgp_orf_remove_all (orf_name); + prefix_bgp_orf_remove_all (afi, orf_name); } /* Reset keepalive and holdtime */ @@ -576,29 +587,77 @@ bgp_stop (struct peer *peer) return 0; } +/* first-val * 2**x back-off, where x is the number of sucessive calls + * originally used for peer v_start back-off + */ +__attribute__((unused)) +static int +back_off_exp2 (const int first, int val, const int max) +{ + val <<= 1; + return (val < max ? val : max); +} + +/* exponential back off, but biased downward by the initial value. + * this bias is significant at lower values, and tends to + * insignificance fairly quickly, so it is equal to the previous at + * scale. Is below first-val * 1.7**x at x == 6, and below first-val + * * 1.75**x at x=10. + * + * I.e., this function is useful to get slower growth for the initial + * points of x. + */ +__attribute__((unused)) +static int +back_off_exp2_bias (const int first, int val, const int max) +{ + val = (val << 1) - (val > first ? first : 0); + return (val < max ? val : max); +} + /* BGP peer is stoped by the error. */ static int bgp_stop_with_error (struct peer *peer) { - /* Double start timer. */ - peer->v_start *= 2; + peer->v_start + = back_off_exp2_bias (BGP_INIT_START_TIMER, peer->v_start, 60); + bgp_stop (peer); + return 0; +} - /* Overflow check. */ - if (peer->v_start >= (60 * 2)) - peer->v_start = (60 * 2); - bgp_stop (peer); +/* 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 bgp_connect_success (struct peer *peer) { - char buf1[BUFSIZ]; - + struct peer *realpeer; + if (peer->fd < 0) { zlog_err ("bgp_connect_success peer's fd is negative value %d", @@ -612,15 +671,37 @@ bgp_connect_success (struct peer *peer) if (BGP_DEBUG (normal, NORMAL)) { + char buf1[SU_ADDRSTRLEN]; + if (! CHECK_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER)) zlog_debug ("%s open active, local address %s", peer->host, sockunion2str (peer->su_local, buf1, SU_ADDRSTRLEN)); else zlog_debug ("%s passive open", peer->host); } - - if (! CHECK_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER)) - bgp_open_send (peer); + + /* Generally we want to send OPEN ASAP. Except, some partial BGP + * implementations out there (e.g., conformance test tools / BGP + * traffic generators) seem to be a bit funny about connection collisions, + * and OPENs before they have sent. + * + * As a hack, delay sending OPEN on an inbound accept-peer session + * _IF_ we locally have an outbound connection in progress, i.e. + * we're in middle of a connection collision. If we delay, we delay until + * an Open is received - as per old Quagga behaviour. + */ + if (CHECK_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER)) + { + realpeer = peer_lookup (peer->bgp, &peer->su); + + if (realpeer->status > Idle && realpeer->status <= Established) + { + SET_FLAG (peer->sflags, PEER_STATUS_OPEN_DEFERRED); + return 0; + } + } + + bgp_open_send (peer); return 0; } @@ -639,6 +720,7 @@ int bgp_start (struct peer *peer) { int status; + int connected = 0; if (BGP_PEER_START_SUPPRESSED (peer)) { @@ -677,6 +759,12 @@ bgp_start (struct peer *peer) return 0; } + /* Register to be notified on peer up */ + if ((peer_ttl(peer) == 1 || peer->gtsm_hops == 1) && + ! CHECK_FLAG (peer->flags, PEER_FLAG_DISABLE_CONNECTED_CHECK)) + connected = 1; + + bgp_ensure_nexthop (NULL, peer, connected); status = bgp_connect (peer); switch (status) @@ -740,29 +828,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); + plog_debug (peer->log, "%s [FSM] Hold timer expire", peer->host); - /* 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; - } - - /* 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 +990,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 *); @@ -930,6 +1015,7 @@ static const struct { {bgp_ignore, Idle}, /* Receive_UPDATE_message */ {bgp_ignore, Idle}, /* Receive_NOTIFICATION_message */ {bgp_ignore, Idle}, /* Clearing_Completed */ + {bgp_ignore, Idle}, /* BGP_Stop_with_error */ }, { /* Connect */ @@ -947,6 +1033,7 @@ static const struct { {bgp_ignore, Idle}, /* Receive_UPDATE_message */ {bgp_stop, Idle}, /* Receive_NOTIFICATION_message */ {bgp_ignore, Idle}, /* Clearing_Completed */ + {bgp_stop_with_error, Idle},/* BGP_Stop_with_error */ }, { /* Active, */ @@ -964,6 +1051,7 @@ static const struct { {bgp_ignore, Idle}, /* Receive_UPDATE_message */ {bgp_stop_with_error, Idle}, /* Receive_NOTIFICATION_message */ {bgp_ignore, Idle}, /* Clearing_Completed */ + {bgp_stop_with_error, Idle},/* BGP_Stop_with_error */ }, { /* OpenSent, */ @@ -977,10 +1065,11 @@ 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 */ + {bgp_stop_with_error, Idle},/* BGP_Stop_with_error */ }, { /* OpenConfirm, */ @@ -998,6 +1087,7 @@ static const struct { {bgp_ignore, Idle}, /* Receive_UPDATE_message */ {bgp_stop_with_error, Idle}, /* Receive_NOTIFICATION_message */ {bgp_ignore, Idle}, /* Clearing_Completed */ + {bgp_stop_with_error, Idle},/* BGP_Stop_with_error */ }, { /* Established, */ @@ -1015,6 +1105,7 @@ static const struct { {bgp_fsm_update, Established}, /* Receive_UPDATE_message */ {bgp_stop_with_error, Clearing}, /* Receive_NOTIFICATION_message */ {bgp_ignore, Idle}, /* Clearing_Completed */ + {bgp_stop_with_error, Clearing}, /* BGP_Stop_with_error */ }, { /* Clearing, */ @@ -1032,6 +1123,7 @@ static const struct { {bgp_stop, Clearing}, /* Receive_UPDATE_message */ {bgp_stop, Clearing}, /* Receive_NOTIFICATION_message */ {bgp_clearing_completed, Idle}, /* Clearing_Completed */ + {bgp_stop_with_error, Clearing}, /* BGP_Stop_with_error */ }, { /* Deleted, */ @@ -1049,6 +1141,7 @@ static const struct { {bgp_ignore, Deleted}, /* Receive_UPDATE_message */ {bgp_ignore, Deleted}, /* Receive_NOTIFICATION_message */ {bgp_ignore, Deleted}, /* Clearing_Completed */ + {bgp_ignore, Deleted}, /* BGP_Stop_with_error */ }, }; @@ -1069,6 +1162,7 @@ static const char *bgp_event_str[] = "Receive_UPDATE_message", "Receive_NOTIFICATION_message", "Clearing_Completed", + "BGP_Stop_with_error", }; /* Execute event process. */ diff --git a/bgpd/bgp_fsm.h b/bgpd/bgp_fsm.h index a749f8ea3..752d6e2b9 100644 --- a/bgpd/bgp_fsm.h +++ b/bgpd/bgp_fsm.h @@ -26,7 +26,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #define BGP_READ_ON(T,F,V) \ do { \ if (!(T) && (peer->status != Deleted)) \ - THREAD_READ_ON(master,T,F,peer,V); \ + THREAD_READ_ON(bm->master,T,F,peer,V); \ } while (0) #define BGP_READ_OFF(T) \ @@ -38,7 +38,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #define BGP_WRITE_ON(T,F,V) \ do { \ if (!(T) && (peer->status != Deleted)) \ - THREAD_WRITE_ON(master,(T),(F),peer,(V)); \ + THREAD_WRITE_ON(bm->master,(T),(F),peer,(V)); \ } while (0) #define BGP_WRITE_OFF(T) \ @@ -50,7 +50,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #define BGP_TIMER_ON(T,F,V) \ do { \ if (!(T) && (peer->status != Deleted)) \ - THREAD_TIMER_ON(master,(T),(F),peer,(V)); \ + THREAD_TIMER_ON(bm->master,(T),(F),peer,(V)); \ } while (0) #define BGP_TIMER_OFF(T) \ @@ -62,13 +62,13 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #define BGP_EVENT_ADD(P,E) \ do { \ if ((P)->status != Deleted) \ - thread_add_event (master, bgp_event, (P), (E)); \ + thread_add_event (bm->master, bgp_event, (P), (E)); \ } while (0) #define BGP_EVENT_FLUSH(P) \ do { \ assert (peer); \ - thread_cancel_event (master, (P)); \ + thread_cancel_event (bm->master, (P)); \ } while (0) /* Prototypes. */ diff --git a/bgpd/bgp_fsm_4271.dot b/bgpd/bgp_fsm_4271.dot new file mode 100644 index 000000000..c03939fd9 --- /dev/null +++ b/bgpd/bgp_fsm_4271.dot @@ -0,0 +1,34 @@ +digraph { + rankdir=LR + //concentrate=true + nojustify="true" + + Idle -> Connect [ label="ManualStart\l|AutomaticStart" ] + Idle -> Active [ label="ManualStart_with_PassiveTcpEstablishment\l|AutomaticStart_with_PassiveTcpEstablishment" ] + + Connect -> Idle [ label="ManualStop"] + Connect -> Connect [ label="ConnectRetryTimer_Expires\l|TcpConnection_Valid\l|Tcp_CR_Invalid\l|Tcp_CR_Acked && DelayOpen == True\l|TcpConnectionConfirmed && DelayOpen == True\l" ] + Connect -> OpenSent [ label="DelayOpenTimer_Expires\l|Tcp_CR_Acked && DelayOpen == False\l|TcpConnectionConfirmed && DelayOpen == False\l" ] + Connect -> Active [ label="TcpConnectionFails && DelayOpenTimer == \"running\"\l" ] + Connect -> Idle [ label="TcpConnectionFails && DelayOpenTimer == \"not running\"\l" ] + Connect -> OpenConfirm [ label="BGPOpen && DelayOpenTimer == \"running\"" ] + Connect -> Idle [ label="NotifMsg|*\l" ] + + Active -> Idle [ label="ManualStop\l|TcpConnectionFails\l|NotifMsg|*" ] + Active -> Connect [ label="ConnectRetryTimer_Expires" ] + Active -> OpenSent [ label="DelayOpenTimer_Expires" ] + Active -> Active [ label="(Tcp_CR_Acked\l|TcpConnectionConfirmed)\l&& DelayOpen = True" ] + Active -> OpenSent [ label="(Tcp_CR_Acked|TcpConnectionConfirmed)\l&& DelayOpen = False" ] + Active -> OpenConfirm [ label="BGPOpen && DelayOpenTimer == \"running\"" ] + + OpenSent -> Idle [ label="ManualStop\l|AutomaticStop\l|HoldTimer_Expires\l|NotifMsg\l|OpenCollisionDump\l" ] + OpenSent -> Active [ label="TcpConnectionFails" ] + OpenSent -> OpenConfirm [ label="BGPOpen" ] + + OpenConfirm -> Idle [ label="ManualStop\l|AutomaticStop\l|HoldTimer_Expires\l|TcpConnectionFails\l|NotifMsg\l|BGPOpen|*\l"] + OpenConfirm -> Established [ label="KeepAliveMsg|"] + OpenConfirm -> OpenConfirm [ label="KeepaliveTimer_Expires" ] + + Established -> Idle [ label="OpenCollisionDump|*"] + Established -> Established [ label="Tcp_CR_Invalid|KeepAliveMsg|UpdateMsg"] +} \ No newline at end of file diff --git a/bgpd/bgp_fsm_quagga.dot b/bgpd/bgp_fsm_quagga.dot new file mode 100644 index 000000000..2b9bee849 --- /dev/null +++ b/bgpd/bgp_fsm_quagga.dot @@ -0,0 +1,59 @@ +digraph { + rankdir=LR + //concentrate=true + nojustify="true" + + Idle + Connect + Active + OpenSent + OpenConfirm + Established + Clearing + Idle -> Deleted + Configured -> Idle + + Idle -> Connect [ label="BGP_Start\l/bgp_start\l" ] + Idle -> Idle [ label="BGP_Stop\l|TCP_connection_open\l|TCP_connection_closed\l|TCP_fatal_error\l/bgp_stop\l"] + + Connect -> Connect [ label="ConnectRetry_timer_expired\l/bgp_reconnect\l" ] + Connect -> Idle [ label="BGP_Stop\l|TCP_connection_open\l|Receive_NOTIFICATION_message\l/bgp_stop\l" ] + Connect -> Idle [ label="TCP_fatal_error\l/bgp_connect_fail\l" ] + Connect -> Idle [ label="Hold_Timer_expired\l|KeepAlive_timer_expired\l|Receive_OPEN_message\l|Receive_KEEPALIVE_message\l|Receive_UPDATE_message\l|Clearing_Completed\l/bgp_ignore"] + Connect -> OpenSent [ label="TCP_connection_open\l/bgp_connect_success\l" ] + Connect -> Active [ label="TCP_connection_open_failed\l/bgp_connect_fail\l" ] + + Active -> Idle [ label="BGP_Stop\l|TCP_connection_closed\l/bgp_stop\l" ] + Active -> Idle [ label="Receive_NOTIFICATION_message\l/bgp_stop_with_error\l" ] + Active -> Idle [ label="TCP_fatal_error\l|Hold_Timer_expired\l|KeepAlive_timer_expired\l|Receive_OPEN_message\l|Receive_KEEPALIVE_message\l|Receive_UPDATE_message\l|Clearing_Completed\l/bgp_ignore\l" ] + Active -> OpenSent [ label="TCP_connection_open\l/bgp_connect_success\l" ] + Active -> Connect [ label="ConnectRetry_timer_expiredl/bgp_start\l" ] + + Accept -> Active [ label="Raise TCP_connection_open on Active" ] + + OpenSent -> Idle [ label="BGP_Stop\l/bgp_stop\l" ] + OpenSent -> Idle [ label="ConnectRetry_timer_expired\l|Clearing_Completed\l|KeepAlive_timer_expired\l/bgp_ignore\l" ] + OpenSent -> Idle [ label="Hold_Timer_expired\l/bgp_fsm_holdtime_expire\l"] + OpenSent -> Idle [ label="Receive_KEEPALIVE_message\l|Receive_UPDATE_message\l/bgp_fsm_event_error" ] + OpenSent -> Idle [ label="Receive_NOTIFICATION_message\l/bgp_stop_with_error" ] + OpenSent -> Active [ label="TCP_connection_open\l|TCP_connection_closed\l|TCP_connection_open_failed\l|TCP_fatal_error\l/bgp_stop\l"] + OpenSent -> OpenConfirm [ label="Receive_OPEN_message\l/bgp_fsm_open" ] + + OpenConfirm -> Idle [ label="BGP_Stop\l|TCP_connection_open\l|TCP_connection_closed\l|TCP_connection_open_failed\l|TCP_fatal_error\l/bgp_stop\l"] + OpenConfirm -> Idle [ label="Hold_Timer_expired\l/bgp_fsm_holdtime_expire" ] + OpenConfirm -> Idle [ label="ConnectRetry_timer_expired\l|Receive_OPEN_message\l|Receive_UPDATE_message\l|Clearing_Completed\l/bgp_ignore"] + OpenConfirm -> Idle [ label="Receive_NOTIFICATION_message\l/bgp_stop_with_error\l" ] + OpenConfirm -> Established [ label="Receive_KEEPALIVE_message\l/bgp_establish\l" ] + + Established -> Clearing [ label="BGP_Stop\l|TCP_connection_open\l|TCP_connection_closed\l|TCP_connection_open_failed\l|TCP_fatal_error\l|ConnectRetry_timer_expired\l|Hold_Timer_expired\l|Receive_OPEN_message\l/bgp_stop\l"] + Established -> Idle [ label="Clearing_Completed\l/bgp_ignore" ] + Established -> Clearing [ label="Receive_NOTIFICATION_message\l/bgp_stop_with_error"] + + Clearing -> Idle [ label="Clearing_Completed\l/bgp_clearing_completed" ] + subgraph cluster_pre_collision_detect { + label="Prior to collision detection" + bgcolor=lightgray + Connect Accept Active OpenSent OpenConfirm + } + +} \ No newline at end of file diff --git a/bgpd/bgp_lcommunity.c b/bgpd/bgp_lcommunity.c new file mode 100644 index 000000000..cc67e1245 --- /dev/null +++ b/bgpd/bgp_lcommunity.c @@ -0,0 +1,562 @@ +/* BGP Large Communities Attribute + +Copyright (C) 2016 Keyur Patel + +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 "hash.h" +#include "memory.h" +#include "prefix.h" +#include "command.h" +#include "filter.h" + +#include "bgpd/bgpd.h" +#include "bgpd/bgp_lcommunity.h" +#include "bgpd/bgp_aspath.h" + +/* Hash of community attribute. */ +static struct hash *lcomhash; + +/* Allocate a new lcommunities. */ +static struct lcommunity * +lcommunity_new (void) +{ + return (struct lcommunity *) XCALLOC (MTYPE_LCOMMUNITY, + sizeof (struct lcommunity)); +} + +/* Allocate lcommunities. */ +void +lcommunity_free (struct lcommunity **lcom) +{ + if ((*lcom)->val) + XFREE (MTYPE_LCOMMUNITY_VAL, (*lcom)->val); + if ((*lcom)->str) + XFREE (MTYPE_LCOMMUNITY_STR, (*lcom)->str); + XFREE (MTYPE_LCOMMUNITY, *lcom); + lcom = NULL; +} + +/* Add a new Large Communities value to Large Communities + Attribute structure. When the value is already exists in the + structure, we don't add the value. Newly added value is sorted by + numerical order. When the value is added to the structure return 1 + else return 0. */ +static int +lcommunity_add_val (struct lcommunity *lcom, struct lcommunity_val *lval) +{ + u_int8_t *p; + int ret; + int c; + + /* When this is fist value, just add it. */ + if (lcom->val == NULL) + { + lcom->size++; + lcom->val = XMALLOC (MTYPE_LCOMMUNITY_VAL, lcom_length (lcom)); + memcpy (lcom->val, lval->val, LCOMMUNITY_SIZE); + return 1; + } + + /* If the value already exists in the structure return 0. */ + c = 0; + for (p = lcom->val; c < lcom->size; p += LCOMMUNITY_SIZE, c++) + { + ret = memcmp (p, lval->val, LCOMMUNITY_SIZE); + if (ret == 0) + return 0; + if (ret > 0) + break; + } + + /* Add the value to the structure with numerical sorting. */ + lcom->size++; + lcom->val = XREALLOC (MTYPE_LCOMMUNITY_VAL, lcom->val, lcom_length (lcom)); + + memmove (lcom->val + (c + 1) * LCOMMUNITY_SIZE, + lcom->val + c * LCOMMUNITY_SIZE, + (lcom->size - 1 - c) * LCOMMUNITY_SIZE); + memcpy (lcom->val + c * LCOMMUNITY_SIZE, lval->val, LCOMMUNITY_SIZE); + + return 1; +} + +/* This function takes pointer to Large Communites strucutre then + create a new Large Communities structure by uniq and sort each + Large Communities value. */ +struct lcommunity * +lcommunity_uniq_sort (struct lcommunity *lcom) +{ + int i; + struct lcommunity *new; + struct lcommunity_val *lval; + + if (! lcom) + return NULL; + + new = lcommunity_new (); + + for (i = 0; i < lcom->size; i++) + { + lval = (struct lcommunity_val *) (lcom->val + (i * LCOMMUNITY_SIZE)); + lcommunity_add_val (new, lval); + } + return new; +} + +/* Parse Large Communites Attribute in BGP packet. */ +struct lcommunity * +lcommunity_parse (u_int8_t *pnt, u_short length) +{ + struct lcommunity tmp; + struct lcommunity *new; + + /* Length check. */ + if (length % LCOMMUNITY_SIZE) + return NULL; + + /* Prepare tmporary structure for making a new Large Communities + Attribute. */ + tmp.size = length / LCOMMUNITY_SIZE; + tmp.val = pnt; + + /* Create a new Large Communities Attribute by uniq and sort each + Large Communities value */ + new = lcommunity_uniq_sort (&tmp); + + return lcommunity_intern (new); +} + +/* Duplicate the Large Communities Attribute structure. */ +struct lcommunity * +lcommunity_dup (struct lcommunity *lcom) +{ + struct lcommunity *new; + + new = XCALLOC (MTYPE_LCOMMUNITY, sizeof (struct lcommunity)); + new->size = lcom->size; + if (new->size) + { + new->val = XMALLOC (MTYPE_LCOMMUNITY_VAL, lcom->size * LCOMMUNITY_SIZE); + memcpy (new->val, lcom->val, lcom->size * LCOMMUNITY_SIZE); + } + else + new->val = NULL; + return new; +} + +/* Retrun string representation of communities attribute. */ +char * +lcommunity_str (struct lcommunity *lcom) +{ + if (! lcom->str) + lcom->str = lcommunity_lcom2str (lcom, LCOMMUNITY_FORMAT_DISPLAY); + return lcom->str; +} + +/* Merge two Large Communities Attribute structure. */ +struct lcommunity * +lcommunity_merge (struct lcommunity *lcom1, struct lcommunity *lcom2) +{ + if (lcom1->val) + lcom1->val = XREALLOC (MTYPE_LCOMMUNITY_VAL, lcom1->val, + (lcom1->size + lcom2->size) * LCOMMUNITY_SIZE); + else + lcom1->val = XMALLOC (MTYPE_LCOMMUNITY_VAL, + (lcom1->size + lcom2->size) * LCOMMUNITY_SIZE); + + memcpy (lcom1->val + (lcom1->size * LCOMMUNITY_SIZE), + lcom2->val, lcom2->size * LCOMMUNITY_SIZE); + lcom1->size += lcom2->size; + + return lcom1; +} + +/* Intern Large Communities Attribute. */ +struct lcommunity * +lcommunity_intern (struct lcommunity *lcom) +{ + struct lcommunity *find; + + assert (lcom->refcnt == 0); + + find = (struct lcommunity *) hash_get (lcomhash, lcom, hash_alloc_intern); + + if (find != lcom) + lcommunity_free (&lcom); + + find->refcnt++; + + if (! find->str) + find->str = lcommunity_lcom2str (find, LCOMMUNITY_FORMAT_DISPLAY); + + return find; +} + +/* Unintern Large Communities Attribute. */ +void +lcommunity_unintern (struct lcommunity **lcom) +{ + struct lcommunity *ret; + + if ((*lcom)->refcnt) + (*lcom)->refcnt--; + + /* Pull off from hash. */ + if ((*lcom)->refcnt == 0) + { + /* Large community must be in the hash. */ + ret = (struct lcommunity *) hash_release (lcomhash, *lcom); + assert (ret != NULL); + + lcommunity_free (lcom); + } +} + +/* Utility function to make hash key. */ +unsigned int +lcommunity_hash_make (void *arg) +{ + const struct lcommunity *lcom = arg; + int size = lcom->size * LCOMMUNITY_SIZE; + u_int8_t *pnt = lcom->val; + unsigned int key = 0; + int c; + + for (c = 0; c < size; c += LCOMMUNITY_SIZE) + { + key += pnt[c]; + key += pnt[c + 1]; + key += pnt[c + 2]; + key += pnt[c + 3]; + key += pnt[c + 4]; + key += pnt[c + 5]; + key += pnt[c + 6]; + key += pnt[c + 7]; + key += pnt[c + 8]; + key += pnt[c + 9]; + key += pnt[c + 10]; + key += pnt[c + 11]; + } + + return key; +} + +/* Compare two Large Communities Attribute structure. */ +int +lcommunity_cmp (const void *arg1, const void *arg2) +{ + const struct lcommunity *lcom1 = arg1; + const struct lcommunity *lcom2 = arg2; + + return (lcom1->size == lcom2->size + && memcmp (lcom1->val, lcom2->val, lcom1->size * LCOMMUNITY_SIZE) == 0); +} + +/* Return communities hash. */ +struct hash * +lcommunity_hash (void) +{ + return lcomhash; +} + +/* Initialize Large Comminities related hash. */ +void +lcommunity_init (void) +{ + lcomhash = hash_create (lcommunity_hash_make, lcommunity_cmp); +} + +void +lcommunity_finish (void) +{ + hash_free (lcomhash); + lcomhash = NULL; +} + +/* Large Communities token enum. */ +enum lcommunity_token +{ + lcommunity_token_unknown = 0, + lcommunity_token_val, +}; + +/* Get next Large Communities token from the string. */ +static const char * +lcommunity_gettoken (const char *str, struct lcommunity_val *lval, + enum lcommunity_token *token) +{ + const char *p = str; + + /* Skip white space. */ + while (isspace ((int) *p)) + { + p++; + str++; + } + + /* Check the end of the line. */ + if (*p == '\0') + return NULL; + + /* Community value. */ + if (isdigit ((int) *p)) + { + int separator = 0; + int digit = 0; + u_int32_t globaladmin = 0; + u_int32_t localdata1 = 0; + u_int32_t localdata2 = 0; + + while (isdigit ((int) *p) || *p == ':') + { + if (*p == ':') + { + if (separator == 2) + { + *token = lcommunity_token_unknown; + return NULL; + } + else + { + separator++; + digit = 0; + if (separator == 1) { + globaladmin = localdata2; + } else { + localdata1 = localdata2; + } + localdata2 = 0; + } + } + else + { + digit = 1; + localdata2 *= 10; + localdata2 += (*p - '0'); + } + p++; + } + if (! digit) + { + *token = lcommunity_token_unknown; + return NULL; + } + + /* + * Copy the large comm. + */ + lval->val[0] = (globaladmin >> 24) & 0xff; + lval->val[1] = (globaladmin >> 16) & 0xff; + lval->val[2] = (globaladmin >> 8) & 0xff; + lval->val[3] = globaladmin & 0xff; + lval->val[4] = (localdata1 >> 24) & 0xff; + lval->val[5] = (localdata1 >> 16) & 0xff; + lval->val[6] = (localdata1 >> 8) & 0xff; + lval->val[7] = localdata1 & 0xff; + lval->val[8] = (localdata2 >> 24) & 0xff; + lval->val[9] = (localdata2 >> 16) & 0xff; + lval->val[10] = (localdata2 >> 8) & 0xff; + lval->val[11] = localdata2 & 0xff; + + *token = lcommunity_token_val; + return p; + } + *token = lcommunity_token_unknown; + return p; +} + +/* + Convert string to large community attribute. + When type is already known, please specify both str and type. + + When string includes keyword for each large community value. + Please specify keyword_included as non-zero value. +*/ +struct lcommunity * +lcommunity_str2com (const char *str) +{ + struct lcommunity *lcom = NULL; + enum lcommunity_token token = lcommunity_token_unknown; + struct lcommunity_val lval; + + while ((str = lcommunity_gettoken (str, &lval, &token))) + { + switch (token) + { + case lcommunity_token_val: + if (lcom == NULL) + lcom = lcommunity_new (); + lcommunity_add_val (lcom, &lval); + break; + case lcommunity_token_unknown: + default: + if (lcom) + lcommunity_free (&lcom); + return NULL; + } + } + return lcom; +} + +int +lcommunity_include (struct lcommunity *lcom, u_char *ptr) +{ + int i; + u_char *lcom_ptr; + + for (i = 0; i < lcom->size; i++) { + lcom_ptr = lcom->val + (i * LCOMMUNITY_SIZE); + if (memcmp (ptr, lcom_ptr, LCOMMUNITY_SIZE) == 0) + return 1; + } + return 0; +} + +/* Convert large community attribute to string. + The large coms will be in 65535:65531:0 format. +*/ +char * +lcommunity_lcom2str (struct lcommunity *lcom, int format) +{ + int i; + u_int8_t *pnt; +#define LCOMMUNITY_STR_DEFAULT_LEN 40 + int str_size; + int str_pnt; + char *str_buf; + int len = 0; + int first = 1; + u_int32_t globaladmin, localdata1, localdata2; + + if (lcom->size == 0) + { + str_buf = XMALLOC (MTYPE_LCOMMUNITY_STR, 1); + str_buf[0] = '\0'; + return str_buf; + } + + /* Prepare buffer. */ + str_buf = XMALLOC (MTYPE_LCOMMUNITY_STR, LCOMMUNITY_STR_DEFAULT_LEN + 1); + str_size = LCOMMUNITY_STR_DEFAULT_LEN + 1; + str_pnt = 0; + + for (i = 0; i < lcom->size; i++) + { + /* Make it sure size is enough. */ + while (str_pnt + LCOMMUNITY_STR_DEFAULT_LEN >= str_size) + { + str_size *= 2; + str_buf = XREALLOC (MTYPE_LCOMMUNITY_STR, str_buf, str_size); + } + + /* Space between each value. */ + if (! first) + str_buf[str_pnt++] = ' '; + + pnt = lcom->val + (i * 12); + + globaladmin = (*pnt++ << 24); + globaladmin |= (*pnt++ << 16); + globaladmin |= (*pnt++ << 8); + globaladmin |= (*pnt++); + + localdata1 = (*pnt++ << 24); + localdata1 |= (*pnt++ << 16); + localdata1 |= (*pnt++ << 8); + localdata1 |= (*pnt++); + + localdata2 = (*pnt++ << 24); + localdata2 |= (*pnt++ << 16); + localdata2 |= (*pnt++ << 8); + localdata2 |= (*pnt++); + + len = sprintf( str_buf + str_pnt, "%u:%u:%u", globaladmin, + localdata1, localdata2); + str_pnt += len; + first = 0; + } + return str_buf; +} + +int +lcommunity_match (const struct lcommunity *lcom1, + const struct lcommunity *lcom2) +{ + int i = 0; + int j = 0; + + if (lcom1 == NULL && lcom2 == NULL) + return 1; + + if (lcom1 == NULL || lcom2 == NULL) + return 0; + + if (lcom1->size < lcom2->size) + return 0; + + /* Every community on com2 needs to be on com1 for this to match */ + while (i < lcom1->size && j < lcom2->size) + { + if (memcmp (lcom1->val + (i*12), lcom2->val + (j*12), LCOMMUNITY_SIZE) == 0) + j++; + i++; + } + + if (j == lcom2->size) + return 1; + else + return 0; +} + +/* Delete one lcommunity. */ +void +lcommunity_del_val (struct lcommunity *lcom, u_char *ptr) +{ + int i = 0; + int c = 0; + + if (! lcom->val) + return; + + while (i < lcom->size) + { + if (memcmp (lcom->val + i*LCOMMUNITY_SIZE, ptr, LCOMMUNITY_SIZE) == 0) + { + c = lcom->size -i -1; + + if (c > 0) + memmove (lcom->val + i*LCOMMUNITY_SIZE, lcom->val + (i + 1)*LCOMMUNITY_SIZE, c * LCOMMUNITY_SIZE); + + lcom->size--; + + if (lcom->size > 0) + lcom->val = XREALLOC (MTYPE_COMMUNITY_VAL, lcom->val, + lcom_length (lcom)); + else + { + XFREE (MTYPE_COMMUNITY_VAL, lcom->val); + lcom->val = NULL; + } + return; + } + i++; + } +} diff --git a/bgpd/bgp_lcommunity.h b/bgpd/bgp_lcommunity.h new file mode 100644 index 000000000..7841b4b9a --- /dev/null +++ b/bgpd/bgp_lcommunity.h @@ -0,0 +1,75 @@ +/* BGP Large Communities Attribute. + +Copyright (C) 2016 Keyur Patel + +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 _QUAGGA_BGP_LCOMMUNITY_H +#define _QUAGGA_BGP_LCOMMUNITY_H + +/* Extended communities attribute string format. */ +#define LCOMMUNITY_FORMAT_ROUTE_MAP 0 +#define LCOMMUNITY_FORMAT_COMMUNITY_LIST 1 +#define LCOMMUNITY_FORMAT_DISPLAY 2 + +/* Large Communities value is twelve octets long. */ +#define LCOMMUNITY_SIZE 12 + +/* Large Communities attribute. */ +struct lcommunity +{ + /* Reference counter. */ + unsigned long refcnt; + + /* Size of Extended Communities attribute. */ + int size; + + /* Extended Communities value. */ + u_int8_t *val; + + /* Human readable format string. */ + char *str; +}; + +/* Extended community value is eight octet. */ +struct lcommunity_val +{ + char val[LCOMMUNITY_SIZE]; +}; + +#define lcom_length(X) ((X)->size * LCOMMUNITY_SIZE) + +extern void lcommunity_init (void); +extern void lcommunity_finish (void); +extern void lcommunity_free (struct lcommunity **); +extern struct lcommunity *lcommunity_parse (u_int8_t *, u_short); +extern struct lcommunity *lcommunity_dup (struct lcommunity *); +extern struct lcommunity *lcommunity_merge (struct lcommunity *, struct lcommunity *); +extern struct lcommunity *lcommunity_uniq_sort (struct lcommunity *); +extern struct lcommunity *lcommunity_intern (struct lcommunity *); +extern int lcommunity_cmp (const void *, const void *); +extern void lcommunity_unintern (struct lcommunity **); +extern unsigned int lcommunity_hash_make (void *); +extern struct hash *lcommunity_hash (void); +extern struct lcommunity *lcommunity_str2com (const char *); +extern char *lcommunity_lcom2str (struct lcommunity *, int); +extern int lcommunity_match (const struct lcommunity *, const struct lcommunity *); +extern char *lcommunity_str (struct lcommunity *); +extern int lcommunity_include (struct lcommunity *lcom, u_char *ptr); +extern void lcommunity_del_val (struct lcommunity *lcom, u_char *ptr); +#endif /* _QUAGGA_BGP_LCOMMUNITY_H */ diff --git a/bgpd/bgp_main.c b/bgpd/bgp_main.c index 8dede5871..af9c03052 100644 --- a/bgpd/bgp_main.c +++ b/bgpd/bgp_main.c @@ -35,6 +35,9 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "routemap.h" #include "filter.h" #include "plist.h" +#include "stream.h" +#include "vrf.h" +#include "workqueue.h" #include "bgpd/bgpd.h" #include "bgpd/bgp_attr.h" @@ -47,6 +50,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "bgpd/bgp_clist.h" #include "bgpd/bgp_debug.h" #include "bgpd/bgp_filter.h" +#include "bgpd/bgp_zebra.h" /* bgpd options, we use GNU getopt library. */ static const struct option longopts[] = @@ -63,6 +67,7 @@ static const struct option longopts[] = { "no_kernel", no_argument, NULL, 'n'}, { "user", required_argument, NULL, 'u'}, { "group", required_argument, NULL, 'g'}, + { "skip_runas", no_argument, NULL, 'S'}, { "version", no_argument, NULL, 'v'}, { "dryrun", no_argument, NULL, 'C'}, { "help", no_argument, NULL, 'h'}, @@ -102,9 +107,6 @@ char config_default[] = SYSCONFDIR BGP_DEFAULT_CONFIG; /* Route retain mode flag. */ static int retain_mode = 0; -/* Master of threads. */ -struct thread_master *master; - /* Manually specified configuration file name. */ char *config_file = NULL; @@ -133,7 +135,7 @@ struct zebra_privs_t bgpd_privs = .vty_group = VTY_GROUP, #endif .caps_p = _caps_p, - .cap_num_p = sizeof(_caps_p)/sizeof(_caps_p[0]), + .cap_num_p = array_size(_caps_p), .cap_num_i = 0, }; @@ -160,6 +162,7 @@ redistribution between different routing protocols.\n\n\ -n, --no_kernel Do not install route to kernel.\n\ -u, --user User to run as\n\ -g, --group Group to run as\n\ +-S, --skip_runas Skip user and group run as\n\ -v, --version Print program version\n\ -C, --dryrun Check configuration for validity and exit\n\ -h, --help Display this help and exit\n\ @@ -169,7 +172,7 @@ Report bugs to %s\n", progname, ZEBRA_BUG_ADDRESS); exit (status); } - + /* SIGHUP handler. */ void sighup (void) @@ -196,10 +199,13 @@ sigint (void) { zlog_notice ("Terminating on signal"); - if (! retain_mode) - bgp_terminate (); + if (! retain_mode) + { + bgp_terminate (); + if (bgpd_privs.user) /* NULL if skip_runas flag set */ + zprivs_terminate (&bgpd_privs); + } - zprivs_terminate (&bgpd_privs); bgp_exit (0); } @@ -224,8 +230,6 @@ bgp_exit (int status) struct listnode *node, *nnode; int *socket; struct interface *ifp; - extern struct zclient *zclient; - extern struct zclient *zlookup; /* it only makes sense for this to be called on a clean exit */ assert (status == 0); @@ -234,7 +238,27 @@ bgp_exit (int status) for (ALL_LIST_ELEMENTS (bm->bgp, node, nnode, bgp)) bgp_delete (bgp); list_free (bm->bgp); - + bm->bgp = NULL; + + /* + * bgp_delete can re-allocate the process queues after they were + * deleted in bgp_terminate. delete them again. + * + * It might be better to ensure the RIBs (including static routes) + * are cleared by bgp_terminate() during its call to bgp_cleanup_routes(), + * which currently only deletes the kernel routes. + */ + if (bm->process_main_queue) + { + work_queue_free (bm->process_main_queue); + bm->process_main_queue = NULL; + } + if (bm->process_rsclient_queue) + { + work_queue_free (bm->process_rsclient_queue); + bm->process_rsclient_queue = NULL; + } + /* reverse bgp_master_init */ for (ALL_LIST_ELEMENTS_RO(bm->listen_sockets, node, socket)) { @@ -246,17 +270,14 @@ bgp_exit (int status) /* reverse bgp_zebra_init/if_init */ if (retain_mode) if_add_hook (IF_DELETE_HOOK, NULL); - for (ALL_LIST_ELEMENTS (iflist, node, nnode, ifp)) + for (ALL_LIST_ELEMENTS_RO (iflist, node, ifp)) { struct listnode *c_node, *c_nnode; struct connected *c; for (ALL_LIST_ELEMENTS (ifp->connected, c_node, c_nnode, c)) bgp_connected_delete (c); - - if_delete (ifp); } - list_free (iflist); /* reverse bgp_attr_init */ bgp_attr_finish (); @@ -270,9 +291,6 @@ bgp_exit (int status) /* reverse bgp_route_map_init/route_map_init */ route_map_finish (); - /* reverse bgp_scan_init */ - bgp_scan_finish (); - /* reverse access_list_init */ access_list_add_hook (NULL); access_list_delete_hook (NULL); @@ -291,16 +309,23 @@ bgp_exit (int status) /* reverse community_list_init */ community_list_terminate (bgp_clist); + vrf_terminate (); cmd_terminate (); vty_terminate (); - if (zclient) - zclient_free (zclient); - if (zlookup) - zclient_free (zlookup); + bgp_address_destroy(); + bgp_scan_destroy(); + bgp_zebra_destroy(); + if (bgp_nexthop_buf) + stream_free (bgp_nexthop_buf); + if (bgp_ifindices_buf) + stream_free (bgp_ifindices_buf); + + /* reverse bgp_scan_init */ + bgp_scan_finish (); /* reverse bgp_master_init */ - if (master) - thread_master_free (master); + if (bm->master) + thread_master_free (bm->master); if (zlog_default) closezlog (zlog_default); @@ -310,7 +335,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 @@ -323,6 +348,7 @@ main (int argc, char **argv) char *progname; struct thread thread; int tmp_port; + int skip_runas = 0; /* Set umask before anything for security */ umask (0027); @@ -339,7 +365,7 @@ main (int argc, char **argv) /* Command line argument treatment. */ while (1) { - opt = getopt_long (argc, argv, "df:i:z:hp:l:A:P:rnu:g:vC", longopts, 0); + opt = getopt_long (argc, argv, "df:i:z:hp:l:A:P:rnu:g:vCS", longopts, 0); if (opt == EOF) break; @@ -397,6 +423,9 @@ main (int argc, char **argv) case 'g': bgpd_privs.group = optarg; break; + case 'S': /* skip run as = override bgpd_privs */ + skip_runas = 1; + break; case 'v': print_version (progname); exit (0); @@ -413,23 +442,20 @@ main (int argc, char **argv) } } - /* Make thread master. */ - master = bm->master; - /* Initializations. */ - srand (time (NULL)); - signal_init (master, Q_SIGC(bgp_signals), bgp_signals); + srandom (time (NULL)); + signal_init (bm->master, array_size(bgp_signals), bgp_signals); + if (skip_runas) + memset (&bgpd_privs, 0, sizeof (bgpd_privs)); zprivs_init (&bgpd_privs); cmd_init (1); - vty_init (master); + vty_init (bm->master); memory_init (); + vrf_init (); /* BGP related initialization. */ bgp_init (); - /* Sort CLI commands. */ - sort_node (); - /* Parse config file. */ vty_read_config (config_file, config_default); @@ -452,13 +478,14 @@ main (int argc, char **argv) vty_serv_sock (vty_addr, vty_port, BGP_VTYSH_PATH); /* Print banner. */ - zlog_notice ("BGPd %s starting: vty@%d, bgp@%s:%d", QUAGGA_VERSION, + zlog_notice ("BGPd %s starting: vty@%d, bgp@%s:%d pid %d", QUAGGA_VERSION, vty_port, (bm->address ? bm->address : ""), - bm->port); + bm->port, + getpid ()); /* Start finite state machine, here we go! */ - while (thread_fetch (master, &thread)) + while (thread_fetch (bm->master, &thread)) thread_call (&thread); /* Not reached. */ diff --git a/bgpd/bgp_mpath.c b/bgpd/bgp_mpath.c new file mode 100644 index 000000000..8195e47f4 --- /dev/null +++ b/bgpd/bgp_mpath.c @@ -0,0 +1,799 @@ +/* $QuaggaId: Format:%an, %ai, %h$ $ + * + * BGP Multipath + * Copyright (C) 2010 Google 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 "command.h" +#include "prefix.h" +#include "linklist.h" +#include "sockunion.h" +#include "memory.h" +#include "filter.h" + +#include "bgpd/bgpd.h" +#include "bgpd/bgp_table.h" +#include "bgpd/bgp_route.h" +#include "bgpd/bgp_attr.h" +#include "bgpd/bgp_debug.h" +#include "bgpd/bgp_aspath.h" +#include "bgpd/bgp_community.h" +#include "bgpd/bgp_ecommunity.h" +#include "bgpd/bgp_lcommunity.h" +#include "bgpd/bgp_mpath.h" + +bool +bgp_mpath_is_configured_sort (struct bgp *bgp, bgp_peer_sort_t sort, + afi_t afi, safi_t safi) +{ + struct bgp_maxpaths_cfg *cfg = &bgp->maxpaths[afi][safi]; + + /* XXX: BGP_DEFAULT_MAXPATHS is 1, and this test only seems to make sense + * if if it stays 1, so not sure the DEFAULT define is that useful. + */ + switch (sort) + { + case BGP_PEER_IBGP: + return cfg->maxpaths_ibgp != BGP_DEFAULT_MAXPATHS; + case BGP_PEER_EBGP: + return cfg->maxpaths_ebgp != BGP_DEFAULT_MAXPATHS; + default: + return false; + } +} + +bool +bgp_mpath_is_configured (struct bgp *bgp, afi_t afi, safi_t safi) +{ + return bgp_mpath_is_configured_sort (bgp, BGP_PEER_IBGP, afi, safi) + || bgp_mpath_is_configured_sort (bgp, BGP_PEER_EBGP, afi, safi); +} + +/* + * bgp_maximum_paths_set + * + * Record maximum-paths configuration for BGP instance + */ +int +bgp_maximum_paths_set (struct bgp *bgp, afi_t afi, safi_t safi, + int peertype, u_int16_t maxpaths) +{ + if (!bgp || (afi >= AFI_MAX) || (safi >= SAFI_MAX)) + return -1; + + switch (peertype) + { + case BGP_PEER_IBGP: + bgp->maxpaths[afi][safi].maxpaths_ibgp = maxpaths; + break; + case BGP_PEER_EBGP: + bgp->maxpaths[afi][safi].maxpaths_ebgp = maxpaths; + break; + default: + return -1; + } + + return 0; +} + +/* + * bgp_maximum_paths_unset + * + * Remove maximum-paths configuration from BGP instance + */ +int +bgp_maximum_paths_unset (struct bgp *bgp, afi_t afi, safi_t safi, + int peertype) +{ + if (!bgp || (afi >= AFI_MAX) || (safi >= SAFI_MAX)) + return -1; + + switch (peertype) + { + case BGP_PEER_IBGP: + bgp->maxpaths[afi][safi].maxpaths_ibgp = BGP_DEFAULT_MAXPATHS; + break; + case BGP_PEER_EBGP: + bgp->maxpaths[afi][safi].maxpaths_ebgp = BGP_DEFAULT_MAXPATHS; + break; + default: + return -1; + } + + return 0; +} + +/* + * bgp_info_nexthop_cmp + * + * Compare the nexthops of two paths. Return value is less than, equal to, + * or greater than zero if bi1 is respectively less than, equal to, + * or greater than bi2. + */ +static int +bgp_info_nexthop_cmp (struct bgp_info *bi1, struct bgp_info *bi2) +{ + struct attr_extra *ae1, *ae2; + int compare; + + ae1 = bi1->attr->extra; + ae2 = bi2->attr->extra; + + compare = IPV4_ADDR_CMP (&bi1->attr->nexthop, &bi2->attr->nexthop); + + if (!compare && ae1 && ae2) + { + if (ae1->mp_nexthop_len == ae2->mp_nexthop_len) + { + switch (ae1->mp_nexthop_len) + { + case 4: + case 12: + compare = IPV4_ADDR_CMP (&ae1->mp_nexthop_global_in, + &ae2->mp_nexthop_global_in); + break; + case 16: + compare = IPV6_ADDR_CMP (&ae1->mp_nexthop_global, + &ae2->mp_nexthop_global); + break; + case 32: + compare = IPV6_ADDR_CMP (&ae1->mp_nexthop_global, + &ae2->mp_nexthop_global); + if (!compare) + compare = IPV6_ADDR_CMP (&ae1->mp_nexthop_local, + &ae2->mp_nexthop_local); + break; + } + } + + /* This can happen if one IPv6 peer sends you global and link-local + * nexthops but another IPv6 peer only sends you global + */ + else if (ae1->mp_nexthop_len == 16 || ae1->mp_nexthop_len == 32) + { + compare = IPV6_ADDR_CMP (&ae1->mp_nexthop_global, + &ae2->mp_nexthop_global); + if (!compare) + { + if (ae1->mp_nexthop_len < ae2->mp_nexthop_len) + compare = -1; + else + compare = 1; + } + } + } + + return compare; +} + +/* + * bgp_info_mpath_cmp + * + * This function determines our multipath list ordering. By ordering + * the list we can deterministically select which paths are included + * in the multipath set. The ordering also helps in detecting changes + * in the multipath selection so we can detect whether to send an + * update to zebra. + * + * The order of paths is determined first by received nexthop, and then + * by peer address if the nexthops are the same. + */ +static int +bgp_info_mpath_cmp (void *val1, void *val2) +{ + struct bgp_info *bi1, *bi2; + int compare; + + bi1 = val1; + bi2 = val2; + + compare = bgp_info_nexthop_cmp (bi1, bi2); + + if (!compare) + compare = sockunion_cmp (bi1->peer->su_remote, bi2->peer->su_remote); + + return compare; +} + +/* + * bgp_mp_list_init + * + * Initialize the mp_list, which holds the list of multipaths + * selected by bgp_best_selection + */ +void +bgp_mp_list_init (struct list *mp_list) +{ + assert (mp_list); + memset (mp_list, 0, sizeof (struct list)); + mp_list->cmp = bgp_info_mpath_cmp; +} + +/* + * bgp_mp_list_clear + * + * Clears all entries out of the mp_list + */ +void +bgp_mp_list_clear (struct list *mp_list) +{ + assert (mp_list); + list_delete_all_node (mp_list); +} + +/* + * bgp_mp_list_add + * + * Adds a multipath entry to the mp_list + */ +void +bgp_mp_list_add (struct list *mp_list, struct bgp_info *mpinfo) +{ + assert (mp_list && mpinfo); + listnode_add_sort (mp_list, mpinfo); +} + +/* + * bgp_info_mpath_new + * + * Allocate and zero memory for a new bgp_info_mpath element + */ +static struct bgp_info_mpath * +bgp_info_mpath_new (void) +{ + struct bgp_info_mpath *new_mpath; + new_mpath = XCALLOC (MTYPE_BGP_MPATH_INFO, sizeof (struct bgp_info_mpath)); + return new_mpath; +} + +/* + * bgp_info_mpath_free + * + * Release resources for a bgp_info_mpath element and zero out pointer + */ +void +bgp_info_mpath_free (struct bgp_info_mpath **mpath) +{ + if (mpath && *mpath) + { + if ((*mpath)->mp_attr) + bgp_attr_unintern (&(*mpath)->mp_attr); + XFREE (MTYPE_BGP_MPATH_INFO, *mpath); + *mpath = NULL; + } +} + +/* + * bgp_info_mpath_get + * + * Fetch the mpath element for the given bgp_info. Used for + * doing lazy allocation. + */ +static struct bgp_info_mpath * +bgp_info_mpath_get (struct bgp_info *binfo) +{ + struct bgp_info_mpath *mpath; + if (!binfo->mpath) + { + mpath = bgp_info_mpath_new(); + if (!mpath) + return NULL; + binfo->mpath = mpath; + mpath->mp_info = binfo; + } + return binfo->mpath; +} + +/* + * bgp_info_mpath_enqueue + * + * Enqueue a path onto the multipath list given the previous multipath + * list entry + */ +static void +bgp_info_mpath_enqueue (struct bgp_info *prev_info, struct bgp_info *binfo) +{ + struct bgp_info_mpath *prev, *mpath; + + prev = bgp_info_mpath_get (prev_info); + mpath = bgp_info_mpath_get (binfo); + if (!prev || !mpath) + return; + + mpath->mp_next = prev->mp_next; + mpath->mp_prev = prev; + if (prev->mp_next) + prev->mp_next->mp_prev = mpath; + prev->mp_next = mpath; + + SET_FLAG (binfo->flags, BGP_INFO_MULTIPATH); +} + +/* + * bgp_info_mpath_dequeue + * + * Remove a path from the multipath list + */ +void +bgp_info_mpath_dequeue (struct bgp_info *binfo) +{ + struct bgp_info_mpath *mpath = binfo->mpath; + if (!mpath) + return; + if (mpath->mp_prev) + mpath->mp_prev->mp_next = mpath->mp_next; + if (mpath->mp_next) + mpath->mp_next->mp_prev = mpath->mp_prev; + mpath->mp_next = mpath->mp_prev = NULL; + UNSET_FLAG (binfo->flags, BGP_INFO_MULTIPATH); +} + +/* + * bgp_info_mpath_next + * + * Given a bgp_info, return the next multipath entry + */ +struct bgp_info * +bgp_info_mpath_next (struct bgp_info *binfo) +{ + if (!binfo->mpath || !binfo->mpath->mp_next) + return NULL; + return binfo->mpath->mp_next->mp_info; +} + +/* + * bgp_info_mpath_first + * + * Given bestpath bgp_info, return the first multipath entry. + */ +struct bgp_info * +bgp_info_mpath_first (struct bgp_info *binfo) +{ + return bgp_info_mpath_next (binfo); +} + +/* + * bgp_info_mpath_count + * + * Given the bestpath bgp_info, return the number of multipath entries + */ +u_int32_t +bgp_info_mpath_count (struct bgp_info *binfo) +{ + if (!binfo->mpath) + return 0; + return binfo->mpath->mp_count; +} + +/* + * bgp_info_mpath_count_set + * + * Sets the count of multipaths into bestpath's mpath element + */ +static void +bgp_info_mpath_count_set (struct bgp_info *binfo, u_int32_t count) +{ + struct bgp_info_mpath *mpath; + if (!count && !binfo->mpath) + return; + mpath = bgp_info_mpath_get (binfo); + if (!mpath) + return; + mpath->mp_count = count; +} + +/* + * bgp_info_mpath_attr + * + * Given bestpath bgp_info, return aggregated attribute set used + * for advertising the multipath route + */ +struct attr * +bgp_info_mpath_attr (struct bgp_info *binfo) +{ + if (!binfo->mpath) + return NULL; + return binfo->mpath->mp_attr; +} + +/* + * bgp_info_mpath_attr_set + * + * Sets the aggregated attribute into bestpath's mpath element + */ +static void +bgp_info_mpath_attr_set (struct bgp_info *binfo, struct attr *attr) +{ + struct bgp_info_mpath *mpath; + if (!attr && !binfo->mpath) + return; + mpath = bgp_info_mpath_get (binfo); + if (!mpath) + return; + mpath->mp_attr = attr; +} + +/* + * bgp_info_mpath_update + * + * Compare and sync up the multipath list with the mp_list generated by + * bgp_best_selection + */ +void +bgp_info_mpath_update (struct bgp_node *rn, struct bgp_info *new_best, + struct bgp_info *old_best, struct list *mp_list, + afi_t afi, safi_t safi) +{ + u_int16_t maxpaths, mpath_count, old_mpath_count; + struct listnode *mp_node, *mp_next_node; + struct bgp_info *cur_mpath, *new_mpath, *next_mpath, *prev_mpath; + int mpath_changed, debug; + char pfx_buf[INET6_ADDRSTRLEN], nh_buf[2][INET6_ADDRSTRLEN]; + struct bgp_maxpaths_cfg *mpath_cfg = NULL; + + mpath_changed = 0; + maxpaths = BGP_DEFAULT_MAXPATHS; + mpath_count = 0; + cur_mpath = NULL; + old_mpath_count = 0; + prev_mpath = new_best; + mp_node = listhead (mp_list); + + debug = BGP_DEBUG (events, EVENTS); + + if (debug) + prefix2str (&rn->p, pfx_buf, sizeof (pfx_buf)); + + if (new_best) + { + mpath_cfg = &new_best->peer->bgp->maxpaths[afi][safi]; + mpath_count++; + if (new_best != old_best) + bgp_info_mpath_dequeue (new_best); + maxpaths = (new_best->peer->sort == BGP_PEER_IBGP) ? + mpath_cfg->maxpaths_ibgp : mpath_cfg->maxpaths_ebgp; + } + + if (old_best) + { + cur_mpath = bgp_info_mpath_first (old_best); + old_mpath_count = bgp_info_mpath_count (old_best); + bgp_info_mpath_count_set (old_best, 0); + bgp_info_mpath_dequeue (old_best); + } + + /* + * We perform an ordered walk through both lists in parallel. + * The reason for the ordered walk is that if there are paths + * that were previously multipaths and are still multipaths, the walk + * should encounter them in both lists at the same time. Otherwise + * there will be paths that are in one list or another, and we + * will deal with these separately. + * + * Note that new_best might be somewhere in the mp_list, so we need + * to skip over it + */ + while (mp_node || cur_mpath) + { + /* + * We can bail out of this loop if all existing paths on the + * multipath list have been visited (for cleanup purposes) and + * the maxpath requirement is fulfulled + */ + if (!cur_mpath && (mpath_count >= maxpaths)) + break; + + mp_next_node = mp_node ? listnextnode (mp_node) : NULL; + next_mpath = cur_mpath ? bgp_info_mpath_next (cur_mpath) : NULL; + + /* + * If equal, the path was a multipath and is still a multipath. + * Insert onto new multipath list if maxpaths allows. + */ + if (mp_node && (listgetdata (mp_node) == cur_mpath)) + { + list_delete_node (mp_list, mp_node); + bgp_info_mpath_dequeue (cur_mpath); + if ((mpath_count < maxpaths) && + bgp_info_nexthop_cmp (prev_mpath, cur_mpath)) + { + bgp_info_mpath_enqueue (prev_mpath, cur_mpath); + prev_mpath = cur_mpath; + mpath_count++; + } + else + { + mpath_changed = 1; + if (debug) + zlog_debug ("%s remove mpath nexthop %s peer %s", pfx_buf, + inet_ntop (AF_INET, &cur_mpath->attr->nexthop, + nh_buf[0], sizeof (nh_buf[0])), + sockunion2str (cur_mpath->peer->su_remote, + nh_buf[1], sizeof (nh_buf[1]))); + } + mp_node = mp_next_node; + cur_mpath = next_mpath; + continue; + } + + if (cur_mpath && (!mp_node || + (bgp_info_mpath_cmp (cur_mpath, + listgetdata (mp_node)) < 0))) + { + /* + * If here, we have an old multipath and either the mp_list + * is finished or the next mp_node points to a later + * multipath, so we need to purge this path from the + * multipath list + */ + bgp_info_mpath_dequeue (cur_mpath); + mpath_changed = 1; + if (debug) + zlog_debug ("%s remove mpath nexthop %s peer %s", pfx_buf, + inet_ntop (AF_INET, &cur_mpath->attr->nexthop, + nh_buf[0], sizeof (nh_buf[0])), + sockunion2str (cur_mpath->peer->su_remote, + nh_buf[1], sizeof (nh_buf[1]))); + cur_mpath = next_mpath; + } + else + { + /* + * If here, we have a path on the mp_list that was not previously + * a multipath (due to non-equivalance or maxpaths exceeded), + * or the matching multipath is sorted later in the multipath + * list. Before we enqueue the path on the new multipath list, + * make sure its not on the old_best multipath list or referenced + * via next_mpath: + * - If next_mpath points to this new path, update next_mpath to + * point to the multipath after this one + * - Dequeue the path from the multipath list just to make sure + */ + new_mpath = listgetdata (mp_node); + list_delete_node (mp_list, mp_node); + if ((mpath_count < maxpaths) && (new_mpath != new_best) && + bgp_info_nexthop_cmp (prev_mpath, new_mpath)) + { + if (new_mpath == next_mpath) + next_mpath = bgp_info_mpath_next (new_mpath); + bgp_info_mpath_dequeue (new_mpath); + + bgp_info_mpath_enqueue (prev_mpath, new_mpath); + prev_mpath = new_mpath; + mpath_changed = 1; + mpath_count++; + if (debug) + zlog_debug ("%s add mpath nexthop %s peer %s", pfx_buf, + inet_ntop (AF_INET, &new_mpath->attr->nexthop, + nh_buf[0], sizeof (nh_buf[0])), + sockunion2str (new_mpath->peer->su_remote, + nh_buf[1], sizeof (nh_buf[1]))); + } + mp_node = mp_next_node; + } + } + + if (new_best) + { + bgp_info_mpath_count_set (new_best, mpath_count-1); + if (mpath_changed || (bgp_info_mpath_count (new_best) != old_mpath_count)) + SET_FLAG (new_best->flags, BGP_INFO_MULTIPATH_CHG); + } +} + +/* + * bgp_mp_dmed_deselect + * + * Clean up multipath information for BGP_INFO_DMED_SELECTED path that + * is not selected as best path + */ +void +bgp_mp_dmed_deselect (struct bgp_info *dmed_best) +{ + struct bgp_info *mpinfo, *mpnext; + + if (!dmed_best) + return; + + for (mpinfo = bgp_info_mpath_first (dmed_best); mpinfo; mpinfo = mpnext) + { + mpnext = bgp_info_mpath_next (mpinfo); + bgp_info_mpath_dequeue (mpinfo); + } + + bgp_info_mpath_count_set (dmed_best, 0); + UNSET_FLAG (dmed_best->flags, BGP_INFO_MULTIPATH_CHG); + assert (bgp_info_mpath_first (dmed_best) == 0); +} + +/* + * bgp_info_mpath_aggregate_update + * + * Set the multipath aggregate attribute. We need to see if the + * aggregate has changed and then set the ATTR_CHANGED flag on the + * bestpath info so that a peer update will be generated. The + * change is detected by generating the current attribute, + * interning it, and then comparing the interned pointer with the + * current value. We can skip this generate/compare step if there + * is no change in multipath selection and no attribute change in + * any multipath. + */ +void +bgp_info_mpath_aggregate_update (struct bgp_info *new_best, + struct bgp_info *old_best) +{ + struct bgp_info *mpinfo; + struct aspath *aspath; + struct aspath *asmerge; + struct attr *new_attr, *old_attr; + u_char origin, attr_chg; + struct community *community, *commerge; + struct ecommunity *ecomm, *ecommerge; + struct lcommunity *lcomm, *lcommerge; + struct attr_extra *ae; + struct attr attr = { 0 }; + + if (old_best && (old_best != new_best) && + (old_attr = bgp_info_mpath_attr (old_best))) + { + bgp_attr_unintern (&old_attr); + bgp_info_mpath_attr_set (old_best, NULL); + } + + if (!new_best) + return; + + if (!bgp_info_mpath_count (new_best)) + { + if ((new_attr = bgp_info_mpath_attr (new_best))) + { + bgp_attr_unintern (&new_attr); + bgp_info_mpath_attr_set (new_best, NULL); + SET_FLAG (new_best->flags, BGP_INFO_ATTR_CHANGED); + } + return; + } + + /* + * Bail out here if the following is true: + * - MULTIPATH_CHG bit is not set on new_best, and + * - No change in bestpath, and + * - ATTR_CHANGED bit is not set on new_best or any of the multipaths + */ + if (!CHECK_FLAG (new_best->flags, BGP_INFO_MULTIPATH_CHG) && + (old_best == new_best)) + { + attr_chg = 0; + + if (CHECK_FLAG (new_best->flags, BGP_INFO_ATTR_CHANGED)) + attr_chg = 1; + else + for (mpinfo = bgp_info_mpath_first (new_best); mpinfo; + mpinfo = bgp_info_mpath_next (mpinfo)) + { + if (CHECK_FLAG (mpinfo->flags, BGP_INFO_ATTR_CHANGED)) + { + attr_chg = 1; + break; + } + } + + if (!attr_chg) + { + assert (bgp_info_mpath_attr (new_best)); + return; + } + } + + bgp_attr_dup (&attr, new_best->attr); + + /* aggregate attribute from multipath constituents */ + aspath = aspath_dup (attr.aspath); + origin = attr.origin; + community = attr.community ? community_dup (attr.community) : NULL; + ae = attr.extra; + ecomm = (ae && ae->ecommunity) ? ecommunity_dup (ae->ecommunity) : NULL; + + lcomm = (ae && ae->lcommunity) ? lcommunity_dup (ae->lcommunity) : NULL; + + for (mpinfo = bgp_info_mpath_first (new_best); mpinfo; + mpinfo = bgp_info_mpath_next (mpinfo)) + { + asmerge = aspath_aggregate_mpath (aspath, mpinfo->attr->aspath); + aspath_free (aspath); + aspath = asmerge; + + if (origin < mpinfo->attr->origin) + origin = mpinfo->attr->origin; + + if (mpinfo->attr->community) + { + if (community) + { + commerge = community_merge (community, mpinfo->attr->community); + community = community_uniq_sort (commerge); + community_free (commerge); + } + else + community = community_dup (mpinfo->attr->community); + } + + ae = mpinfo->attr->extra; + if (ae && ae->ecommunity) + { + if (ecomm) + { + ecommerge = ecommunity_merge (ecomm, ae->ecommunity); + ecomm = ecommunity_uniq_sort (ecommerge); + ecommunity_free (&ecommerge); + } + else + ecomm = ecommunity_dup (ae->ecommunity); + } + + if (ae && ae->lcommunity) + { + if (lcomm) + { + lcommerge = lcommunity_merge (lcomm, ae->lcommunity); + lcomm = lcommunity_uniq_sort (lcommerge); + lcommunity_free (&lcommerge); + } + else + lcomm = lcommunity_dup (ae->lcommunity); + } + } + + attr.aspath = aspath; + attr.origin = origin; + if (community) + { + attr.community = community; + attr.flag |= ATTR_FLAG_BIT (BGP_ATTR_COMMUNITIES); + } + if (ecomm) + { + ae = bgp_attr_extra_get (&attr); + ae->ecommunity = ecomm; + attr.flag |= ATTR_FLAG_BIT (BGP_ATTR_EXT_COMMUNITIES); + } + + /* Zap multipath attr nexthop so we set nexthop to self */ + attr.nexthop.s_addr = 0; + if (attr.extra) + memset (&attr.extra->mp_nexthop_global, 0, sizeof (struct in6_addr)); + + /* TODO: should we set ATOMIC_AGGREGATE and AGGREGATOR? */ + + new_attr = bgp_attr_intern (&attr); + bgp_attr_extra_free (&attr); + + if (new_attr != bgp_info_mpath_attr (new_best)) + { + if ((old_attr = bgp_info_mpath_attr (new_best))) + bgp_attr_unintern (&old_attr); + bgp_info_mpath_attr_set (new_best, new_attr); + SET_FLAG (new_best->flags, BGP_INFO_ATTR_CHANGED); + } + else + bgp_attr_unintern (&new_attr); +} diff --git a/bgpd/bgp_mpath.h b/bgpd/bgp_mpath.h new file mode 100644 index 000000000..2a84d5e1e --- /dev/null +++ b/bgpd/bgp_mpath.h @@ -0,0 +1,82 @@ +/* $QuaggaId: Format:%an, %ai, %h$ $ + * + * BGP Multipath + * Copyright (C) 2010 Google 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. + */ + +#ifndef _QUAGGA_BGP_MPATH_H +#define _QUAGGA_BGP_MPATH_H + +/* BGP default maximum-paths */ +#define BGP_DEFAULT_MAXPATHS 1 + +/* Supplemental information linked to bgp_info for keeping track of + * multipath selections, lazily allocated to save memory + */ +struct bgp_info_mpath +{ + /* Points to the first multipath (on bestpath) or the next multipath */ + struct bgp_info_mpath *mp_next; + + /* Points to the previous multipath or NULL on bestpath */ + struct bgp_info_mpath *mp_prev; + + /* Points to bgp_info associated with this multipath info */ + struct bgp_info *mp_info; + + /* When attached to best path, the number of selected multipaths */ + u_int32_t mp_count; + + /* Aggregated attribute for advertising multipath route */ + struct attr *mp_attr; +}; + +/* Functions to support maximum-paths configuration */ +extern int bgp_maximum_paths_set (struct bgp *, afi_t, safi_t, int, u_int16_t); +extern int bgp_maximum_paths_unset (struct bgp *, afi_t, safi_t, int); +bool bgp_mpath_is_configured_sort (struct bgp *, bgp_peer_sort_t, afi_t, safi_t); +bool bgp_mpath_is_configured (struct bgp *, afi_t, safi_t); + +/* Functions used by bgp_best_selection to record current + * multipath selections + */ +extern void bgp_mp_list_init (struct list *); +extern void bgp_mp_list_clear (struct list *); +extern void bgp_mp_list_add (struct list *, struct bgp_info *); +extern void bgp_mp_dmed_deselect (struct bgp_info *); +extern void bgp_info_mpath_update (struct bgp_node *, struct bgp_info *, + struct bgp_info *, struct list *, + afi_t, safi_t); +extern void bgp_info_mpath_aggregate_update (struct bgp_info *, + struct bgp_info *); + +/* Unlink and free multipath information associated with a bgp_info */ +extern void bgp_info_mpath_dequeue (struct bgp_info *); +extern void bgp_info_mpath_free (struct bgp_info_mpath **); + +/* Walk list of multipaths associated with a best path */ +extern struct bgp_info *bgp_info_mpath_first (struct bgp_info *); +extern struct bgp_info *bgp_info_mpath_next (struct bgp_info *); + +/* Accessors for multipath information */ +extern u_int32_t bgp_info_mpath_count (struct bgp_info *); +extern struct attr *bgp_info_mpath_attr (struct bgp_info *); + +#endif /* _QUAGGA_BGP_MPATH_H */ diff --git a/bgpd/bgp_mplsvpn.c b/bgpd/bgp_mplsvpn.c index c1f1fbb37..ac3ee0808 100644 --- a/bgpd/bgp_mplsvpn.c +++ b/bgpd/bgp_mplsvpn.c @@ -25,12 +25,15 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "log.h" #include "memory.h" #include "stream.h" +#include "filter.h" #include "bgpd/bgpd.h" #include "bgpd/bgp_table.h" #include "bgpd/bgp_route.h" +#include "bgpd/bgp_packet.h" #include "bgpd/bgp_attr.h" #include "bgpd/bgp_mplsvpn.h" +#include "bgpd/bgp_packet.h" static u_int16_t decode_rd_type (u_char *pnt) @@ -53,6 +56,7 @@ decode_label (u_char *pnt) return l; } +/* type == RD_TYPE_AS */ static void decode_rd_as (u_char *pnt, struct rd_as *rd_as) { @@ -65,6 +69,20 @@ decode_rd_as (u_char *pnt, struct rd_as *rd_as) rd_as->val |= (u_int32_t) *pnt; } +/* type == RD_TYPE_AS4 */ +static void +decode_rd_as4 (u_char *pnt, struct rd_as *rd_as) +{ + rd_as->as = (u_int32_t) *pnt++ << 24; + rd_as->as |= (u_int32_t) *pnt++ << 16; + rd_as->as |= (u_int32_t) *pnt++ << 8; + rd_as->as |= (u_int32_t) *pnt++; + + rd_as->val = ((u_int16_t) *pnt++ << 8); + rd_as->val |= (u_int16_t) *pnt; +} + +/* type == RD_TYPE_IP */ static void decode_rd_ip (u_char *pnt, struct rd_ip *rd_ip) { @@ -76,15 +94,14 @@ decode_rd_ip (u_char *pnt, struct rd_ip *rd_ip) } int -bgp_nlri_parse_vpnv4 (struct peer *peer, struct attr *attr, - struct bgp_nlri *packet) +bgp_nlri_parse_vpn (struct peer *peer, struct attr *attr, + struct bgp_nlri *packet) { u_char *pnt; u_char *lim; struct prefix p; - int psize; + int psize = 0; int prefixlen; - u_int32_t label; u_int16_t type; struct rd_as rd_as; struct rd_ip rd_ip; @@ -102,6 +119,7 @@ bgp_nlri_parse_vpnv4 (struct peer *peer, struct attr *attr, pnt = packet->nlri; lim = pnt + packet->length; +#define VPN_PREFIXLEN_MIN_BYTES (3 + 8) /* label + RD */ for (; pnt < lim; pnt += psize) { /* Clear prefix structure. */ @@ -109,19 +127,53 @@ bgp_nlri_parse_vpnv4 (struct peer *peer, struct attr *attr, /* Fetch prefix length. */ prefixlen = *pnt++; - p.family = AF_INET; + p.family = afi2family (packet->afi); psize = PSIZE (prefixlen); - - if (prefixlen < 88) - { - zlog_err ("prefix length is less than 88: %d", prefixlen); - return -1; - } - - label = decode_label (pnt); - + + /* sanity check against packet data */ + if (prefixlen < VPN_PREFIXLEN_MIN_BYTES*8) + { + plog_err (peer->log, + "%s [Error] Update packet error / VPNv4" + " (prefix length %d less than VPNv4 min length)", + peer->host, prefixlen); + return -1; + } + if ((pnt + psize) > lim) + { + plog_err (peer->log, + "%s [Error] Update packet error / VPNv4" + " (psize %u exceeds packet size (%u)", + peer->host, + prefixlen, (uint)(lim-pnt)); + return -1; + } + + /* sanity check against storage for the IP address portion */ + if ((psize - VPN_PREFIXLEN_MIN_BYTES) > (ssize_t) sizeof(p.u)) + { + plog_err (peer->log, + "%s [Error] Update packet error / VPNv4" + " (psize %u exceeds storage size (%zu)", + peer->host, + prefixlen - VPN_PREFIXLEN_MIN_BYTES*8, sizeof(p.u)); + return -1; + } + + /* Sanity check against max bitlen of the address family */ + if ((psize - VPN_PREFIXLEN_MIN_BYTES) > prefix_blen (&p)) + { + plog_err (peer->log, + "%s [Error] Update packet error / VPNv4" + " (psize %u exceeds family (%u) max byte len %u)", + peer->host, + prefixlen - VPN_PREFIXLEN_MIN_BYTES*8, + p.family, prefix_blen (&p)); + return -1; + } + /* Copyr label to prefix. */ - tagpnt = pnt;; + tagpnt = pnt; /* Copy routing distinguisher to rd. */ memcpy (&prd.val, pnt + 3, 8); @@ -129,55 +181,59 @@ bgp_nlri_parse_vpnv4 (struct peer *peer, struct attr *attr, /* Decode RD type. */ type = decode_rd_type (pnt + 3); - /* Decode RD value. */ - if (type == RD_TYPE_AS) - decode_rd_as (pnt + 5, &rd_as); - else if (type == RD_TYPE_IP) - decode_rd_ip (pnt + 5, &rd_ip); - else - { - zlog_err ("Invalid RD type %d", type); - return -1; - } + switch (type) + { + case RD_TYPE_AS: + decode_rd_as (pnt + 5, &rd_as); + break; - p.prefixlen = prefixlen - 88; - memcpy (&p.u.prefix, pnt + 11, psize - 11); + case RD_TYPE_AS4: + decode_rd_as4 (pnt + 5, &rd_as); + break; -#if 0 - if (type == RD_TYPE_AS) - zlog_info ("prefix %ld:%ld:%ld:%s/%d", label, rd_as.as, rd_as.val, - inet_ntoa (p.u.prefix4), p.prefixlen); - else if (type == RD_TYPE_IP) - zlog_info ("prefix %ld:%s:%ld:%s/%d", label, inet_ntoa (rd_ip.ip), - rd_ip.val, inet_ntoa (p.u.prefix4), p.prefixlen); -#endif /* 0 */ + case RD_TYPE_IP: + decode_rd_ip (pnt + 5, &rd_ip); + break; - if (pnt + psize > lim) - return -1; + default: + zlog_err ("Unknown RD type %d", type); + break; /* just report */ + } + + p.prefixlen = prefixlen - VPN_PREFIXLEN_MIN_BYTES*8; + memcpy (&p.u.prefix, pnt + VPN_PREFIXLEN_MIN_BYTES, + psize - VPN_PREFIXLEN_MIN_BYTES); if (attr) - bgp_update (peer, &p, attr, AFI_IP, SAFI_MPLS_VPN, - ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, &prd, tagpnt, 0); + bgp_update (peer, &p, attr, packet->afi, SAFI_MPLS_VPN, + ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, &prd, tagpnt, 0); else - bgp_withdraw (peer, &p, attr, AFI_IP, SAFI_MPLS_VPN, - ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, &prd, tagpnt); + bgp_withdraw (peer, &p, attr, packet->afi, SAFI_MPLS_VPN, + ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, &prd, tagpnt); } - /* Packet length consistency check. */ if (pnt != lim) - return -1; - + { + plog_err (peer->log, + "%s [Error] Update packet error / VPNv4" + " (%zu data remaining after parsing)", + peer->host, lim - pnt); + return -1; + } + return 0; +#undef VPN_PREFIXLEN_MIN_BYTES } int str2prefix_rd (const char *str, struct prefix_rd *prd) { - int ret; + int ret; /* ret of called functions */ + int lret; /* local ret, of this func */ char *p; char *p2; - struct stream *s; - char *half; + struct stream *s = NULL; + char *half = NULL; struct in_addr addr; s = stream_new (8); @@ -185,12 +241,13 @@ str2prefix_rd (const char *str, struct prefix_rd *prd) prd->family = AF_UNSPEC; prd->prefixlen = 64; + lret = 0; p = strchr (str, ':'); if (! p) - return 0; + goto out; if (! all_digit (p + 1)) - return 0; + goto out; half = XMALLOC (MTYPE_TMP, (p - str) + 1); memcpy (half, str, (p - str)); @@ -201,10 +258,8 @@ str2prefix_rd (const char *str, struct prefix_rd *prd) if (! p2) { if (! all_digit (half)) - { - XFREE (MTYPE_TMP, half); - return 0; - } + goto out; + stream_putw (s, RD_TYPE_AS); stream_putw (s, atoi (half)); stream_putl (s, atol (p + 1)); @@ -213,17 +268,21 @@ str2prefix_rd (const char *str, struct prefix_rd *prd) { ret = inet_aton (half, &addr); if (! ret) - { - XFREE (MTYPE_TMP, half); - return 0; - } + goto out; + stream_putw (s, RD_TYPE_IP); stream_put_in_addr (s, &addr); stream_putw (s, atol (p + 1)); } memcpy (prd->val, s->data, 8); - - return 1; + lret = 1; + +out: + if (s) + stream_free (s); + if (half) + XFREE(MTYPE_TMP, half); + return lret; } int @@ -272,13 +331,18 @@ prefix_rd2str (struct prefix_rd *prd, char *buf, size_t size) snprintf (buf, size, "%u:%d", rd_as.as, rd_as.val); return buf; } + else if (type == RD_TYPE_AS4) + { + decode_rd_as4 (pnt + 2, &rd_as); + snprintf (buf, size, "%u:%d", rd_as.as, rd_as.val); + return buf; + } else if (type == RD_TYPE_IP) { decode_rd_ip (pnt + 2, &rd_ip); snprintf (buf, size, "%s:%d", inet_ntoa (rd_ip.ip), rd_ip.val); return buf; } - return NULL; } @@ -293,7 +357,22 @@ DEFUN (vpnv4_network, "BGP tag\n" "tag value\n") { - return bgp_static_set_vpnv4 (vty, argv[0], argv[1], argv[2]); + return bgp_static_set_safi (SAFI_MPLS_VPN, vty, argv[0], argv[1], argv[2], NULL); +} + +DEFUN (vpnv4_network_route_map, + vpnv4_network_route_map_cmd, + "network A.B.C.D/M rd ASN:nn_or_IP-address:nn tag WORD route-map WORD", + "Specify a network to announce via BGP\n" + "IP prefix /, e.g., 35.0.0.0/8\n" + "Specify Route Distinguisher\n" + "VPN Route Distinguisher\n" + "BGP tag\n" + "tag value\n" + "route map\n" + "route map name\n") +{ + return bgp_static_set_safi (SAFI_MPLS_VPN, vty, argv[0], argv[1], argv[2], argv[3]); } /* For testing purpose, static route of MPLS-VPN. */ @@ -308,7 +387,7 @@ DEFUN (no_vpnv4_network, "BGP tag\n" "tag value\n") { - return bgp_static_unset_vpnv4 (vty, argv[0], argv[1], argv[2]); + return bgp_static_unset_safi (SAFI_MPLS_VPN, vty, argv[0], argv[1], argv[2]); } static int @@ -369,6 +448,8 @@ show_adj_route_vpn (struct vty *vty, struct peer *peer, struct prefix_rd *prd) /* Decode RD value. */ if (type == RD_TYPE_AS) decode_rd_as (pnt + 2, &rd_as); + else if (type == RD_TYPE_AS4) + decode_rd_as4 (pnt + 2, &rd_as); else if (type == RD_TYPE_IP) decode_rd_ip (pnt + 2, &rd_ip); @@ -376,6 +457,8 @@ show_adj_route_vpn (struct vty *vty, struct peer *peer, struct prefix_rd *prd) if (type == RD_TYPE_AS) vty_out (vty, "%u:%d", rd_as.as, rd_as.val); + else if (type == RD_TYPE_AS4) + vty_out (vty, "%u:%d", rd_as.as, rd_as.val); else if (type == RD_TYPE_IP) vty_out (vty, "%s:%d", inet_ntoa (rd_ip.ip), rd_ip.val); @@ -406,8 +489,13 @@ enum bgp_show_type }; static int -bgp_show_mpls_vpn (struct vty *vty, struct prefix_rd *prd, enum bgp_show_type type, - void *output_arg, int tags) +bgp_show_mpls_vpn( + struct vty *vty, + afi_t afi, + struct prefix_rd *prd, + enum bgp_show_type type, + void *output_arg, + int tags) { struct bgp *bgp; struct bgp_table *table; @@ -419,6 +507,9 @@ bgp_show_mpls_vpn (struct vty *vty, struct prefix_rd *prd, enum bgp_show_type ty char v4_header[] = " Network Next Hop Metric LocPrf Weight Path%s"; char v4_header_tag[] = " Network Next Hop In tag/Out tag%s"; + unsigned long output_count = 0; + unsigned long total_count = 0; + bgp = bgp_get_default (); if (bgp == NULL) { @@ -426,7 +517,13 @@ bgp_show_mpls_vpn (struct vty *vty, struct prefix_rd *prd, enum bgp_show_type ty return CMD_WARNING; } - for (rn = bgp_table_top (bgp->rib[AFI_IP][SAFI_MPLS_VPN]); rn; rn = bgp_route_next (rn)) + if ((afi != AFI_IP) && (afi != AFI_IP6)) + { + vty_out (vty, "Afi %d not supported%s", afi, VTY_NEWLINE); + return CMD_WARNING; + } + + for (rn = bgp_table_top (bgp->rib[afi][SAFI_MPLS_VPN]); rn; rn = bgp_route_next (rn)) { if (prd && memcmp (rn->p.u.val, prd->val, 8) != 0) continue; @@ -438,6 +535,7 @@ bgp_show_mpls_vpn (struct vty *vty, struct prefix_rd *prd, enum bgp_show_type ty for (rm = bgp_table_top (table); rm; rm = bgp_route_next (rm)) for (ri = rm->info; ri; ri = ri->next) { + total_count++; if (type == bgp_show_type_neighbor) { union sockunion *su = output_arg; @@ -476,15 +574,19 @@ bgp_show_mpls_vpn (struct vty *vty, struct prefix_rd *prd, enum bgp_show_type ty /* Decode RD value. */ if (type == RD_TYPE_AS) decode_rd_as (pnt + 2, &rd_as); + else if (type == RD_TYPE_AS4) + decode_rd_as4 (pnt + 2, &rd_as); else if (type == RD_TYPE_IP) decode_rd_ip (pnt + 2, &rd_ip); vty_out (vty, "Route Distinguisher: "); if (type == RD_TYPE_AS) - vty_out (vty, "%u:%d", rd_as.as, rd_as.val); + vty_out (vty, "as2 %u:%d", rd_as.as, rd_as.val); + else if (type == RD_TYPE_AS4) + vty_out (vty, "as4 %u:%d", rd_as.as, rd_as.val); else if (type == RD_TYPE_IP) - vty_out (vty, "%s:%d", inet_ntoa (rd_ip.ip), rd_ip.val); + vty_out (vty, "ip %s:%d", inet_ntoa (rd_ip.ip), rd_ip.val); vty_out (vty, "%s", VTY_NEWLINE); rd_header = 0; @@ -493,31 +595,73 @@ bgp_show_mpls_vpn (struct vty *vty, struct prefix_rd *prd, enum bgp_show_type ty route_vty_out_tag (vty, &rm->p, ri, 0, SAFI_MPLS_VPN); else route_vty_out (vty, &rm->p, ri, 0, SAFI_MPLS_VPN); + output_count++; } } } + + if (output_count == 0) + { + vty_out (vty, "No prefixes displayed, %ld exist%s", total_count, VTY_NEWLINE); + } + else + vty_out (vty, "%sDisplayed %ld out of %ld total prefixes%s", + VTY_NEWLINE, output_count, total_count, VTY_NEWLINE); + return CMD_SUCCESS; } -DEFUN (show_ip_bgp_vpnv4_all, - show_ip_bgp_vpnv4_all_cmd, - "show ip bgp vpnv4 all", +DEFUN (show_bgp_ipv4_vpn, + show_bgp_ipv4_vpn_cmd, + "show bgp ipv4 vpn", + SHOW_STR + BGP_STR + "Address Family\n" + "Display VPN NLRI specific information\n") +{ + return bgp_show_mpls_vpn (vty, AFI_IP, NULL, bgp_show_type_normal, NULL, 0); +} + +DEFUN (show_bgp_ipv6_vpn, + show_bgp_ipv6_vpn_cmd, + "show bgp ipv6 vpn", + SHOW_STR + BGP_STR + "Address Family\n" + "Display VPN NLRI specific information\n") +{ + return bgp_show_mpls_vpn (vty, AFI_IP6, NULL, bgp_show_type_normal, NULL, 0); +} + +DEFUN (show_bgp_ipv4_vpn_rd, + show_bgp_ipv4_vpn_rd_cmd, + "show bgp ipv4 vpn rd ASN:nn_or_IP-address:nn", SHOW_STR - IP_STR BGP_STR - "Display VPNv4 NLRI specific information\n" - "Display information about all VPNv4 NLRIs\n") + "Address Family\n" + "Display VPN NLRI specific information\n" + "Display information for a route distinguisher\n" + "VPN Route Distinguisher\n") { - return bgp_show_mpls_vpn (vty, NULL, bgp_show_type_normal, NULL, 0); + int ret; + struct prefix_rd prd; + + ret = str2prefix_rd (argv[0], &prd); + if (! ret) + { + vty_out (vty, "%% Malformed Route Distinguisher%s", VTY_NEWLINE); + return CMD_WARNING; + } + return bgp_show_mpls_vpn (vty, AFI_IP, &prd, bgp_show_type_normal, NULL, 0); } -DEFUN (show_ip_bgp_vpnv4_rd, - show_ip_bgp_vpnv4_rd_cmd, - "show ip bgp vpnv4 rd ASN:nn_or_IP-address:nn", +DEFUN (show_bgp_ipv6_vpn_rd, + show_bgp_ipv6_vpn_rd_cmd, + "show bgp ipv6 vpn rd ASN:nn_or_IP-address:nn", SHOW_STR - IP_STR BGP_STR - "Display VPNv4 NLRI specific information\n" + "Address Family\n" + "Display VPN NLRI specific information\n" "Display information for a route distinguisher\n" "VPN Route Distinguisher\n") { @@ -530,29 +674,40 @@ DEFUN (show_ip_bgp_vpnv4_rd, vty_out (vty, "%% Malformed Route Distinguisher%s", VTY_NEWLINE); return CMD_WARNING; } - return bgp_show_mpls_vpn (vty, &prd, bgp_show_type_normal, NULL, 0); + return bgp_show_mpls_vpn (vty, AFI_IP6, &prd, bgp_show_type_normal, NULL, 0); } -DEFUN (show_ip_bgp_vpnv4_all_tags, - show_ip_bgp_vpnv4_all_tags_cmd, - "show ip bgp vpnv4 all tags", + +DEFUN (show_bgp_ipv4_vpn_tags, + show_bgp_ipv4_vpn_tags_cmd, + "show bgp ipv4 vpn tags", SHOW_STR - IP_STR BGP_STR - "Display VPNv4 NLRI specific information\n" - "Display information about all VPNv4 NLRIs\n" + "Address Family\n" + "Display VPN NLRI specific information\n" "Display BGP tags for prefixes\n") { - return bgp_show_mpls_vpn (vty, NULL, bgp_show_type_normal, NULL, 1); + return bgp_show_mpls_vpn (vty, AFI_IP, NULL, bgp_show_type_normal, NULL, 1); +} +DEFUN (show_bgp_ipv6_vpn_tags, + show_bgp_ipv6_vpn_tags_cmd, + "show bgp ipv6 vpn tags", + SHOW_STR + BGP_STR + "Address Family\n" + "Display VPN NLRI specific information\n" + "Display BGP tags for prefixes\n") +{ + return bgp_show_mpls_vpn (vty, AFI_IP6, NULL, bgp_show_type_normal, NULL, 1); } -DEFUN (show_ip_bgp_vpnv4_rd_tags, - show_ip_bgp_vpnv4_rd_tags_cmd, - "show ip bgp vpnv4 rd ASN:nn_or_IP-address:nn tags", +DEFUN (show_bgp_ipv4_vpn_rd_tags, + show_bgp_ipv4_vpn_rd_tags_cmd, + "show bgp ipv4 vpn rd ASN:nn_or_IP-address:nn tags", SHOW_STR - IP_STR BGP_STR - "Display VPNv4 NLRI specific information\n" + "Address Family\n" + "Display VPN NLRI specific information\n" "Display information for a route distinguisher\n" "VPN Route Distinguisher\n" "Display BGP tags for prefixes\n") @@ -566,91 +721,135 @@ DEFUN (show_ip_bgp_vpnv4_rd_tags, vty_out (vty, "%% Malformed Route Distinguisher%s", VTY_NEWLINE); return CMD_WARNING; } - return bgp_show_mpls_vpn (vty, &prd, bgp_show_type_normal, NULL, 1); + return bgp_show_mpls_vpn (vty, AFI_IP, &prd, bgp_show_type_normal, NULL, 1); } +DEFUN (show_bgp_ipv6_vpn_rd_tags, + show_bgp_ipv6_vpn_rd_tags_cmd, + "show bgp ipv6 vpn rd ASN:nn_or_IP-address:nn tags", + SHOW_STR + BGP_STR + "Address Family\n" + "Display VPN NLRI specific information\n" + "Display information for a route distinguisher\n" + "VPN Route Distinguisher\n" + "Display BGP tags for prefixes\n") +{ + int ret; + struct prefix_rd prd; -DEFUN (show_ip_bgp_vpnv4_all_neighbor_routes, - show_ip_bgp_vpnv4_all_neighbor_routes_cmd, - "show ip bgp vpnv4 all neighbors A.B.C.D routes", + ret = str2prefix_rd (argv[0], &prd); + if (! ret) + { + vty_out (vty, "%% Malformed Route Distinguisher%s", VTY_NEWLINE); + return CMD_WARNING; + } + return bgp_show_mpls_vpn (vty, AFI_IP6, &prd, bgp_show_type_normal, NULL, 1); +} + +DEFUN (show_bgp_ipv4_vpn_neighbor_routes, + show_bgp_ipv4_vpn_neighbor_routes_cmd, + "show bgp ipv4 vpn neighbors (A.B.C.D|X:X::X:X) routes", SHOW_STR - IP_STR BGP_STR - "Display VPNv4 NLRI specific information\n" - "Display information about all VPNv4 NLRIs\n" + "Address Family\n" + "Display VPN NLRI specific information\n" "Detailed information on TCP and BGP neighbor connections\n" "Neighbor to display information about\n" + "Neighbor to display information about\n" "Display routes learned from neighbor\n") { - union sockunion *su; + union sockunion su; struct peer *peer; - - su = sockunion_str2su (argv[0]); - if (su == NULL) + int ret; + + ret = str2sockunion (argv[0], &su); + if (ret < 0) { vty_out (vty, "Malformed address: %s%s", argv[0], VTY_NEWLINE); - return CMD_WARNING; + return CMD_WARNING; } - peer = peer_lookup (NULL, su); + peer = peer_lookup (NULL, &su); if (! peer || ! peer->afc[AFI_IP][SAFI_MPLS_VPN]) { vty_out (vty, "%% No such neighbor or address family%s", VTY_NEWLINE); return CMD_WARNING; } - return bgp_show_mpls_vpn (vty, NULL, bgp_show_type_neighbor, su, 0); + return bgp_show_mpls_vpn (vty, AFI_IP, NULL, bgp_show_type_neighbor, &su, 0); } -DEFUN (show_ip_bgp_vpnv4_rd_neighbor_routes, - show_ip_bgp_vpnv4_rd_neighbor_routes_cmd, - "show ip bgp vpnv4 rd ASN:nn_or_IP-address:nn neighbors A.B.C.D routes", +DEFUN (show_bgp_ipv6_vpn_neighbor_routes, + show_bgp_ipv6_vpn_neighbor_routes_cmd, + "show bgp ipv6 vpn neighbors (A.B.C.D|X:X::X:X) routes", SHOW_STR - IP_STR BGP_STR - "Display VPNv4 NLRI specific information\n" - "Display information for a route distinguisher\n" - "VPN Route Distinguisher\n" + "Address Family\n" + "Display VPN NLRI specific information\n" "Detailed information on TCP and BGP neighbor connections\n" "Neighbor to display information about\n" + "Neighbor to display information about\n" "Display routes learned from neighbor\n") { - int ret; - union sockunion *su; + union sockunion su; struct peer *peer; - struct prefix_rd prd; - ret = str2prefix_rd (argv[0], &prd); - if (! ret) + int ret; + + ret = str2sockunion (argv[0], &su); + if (ret < 0) { - vty_out (vty, "%% Malformed Route Distinguisher%s", VTY_NEWLINE); + vty_out (vty, "Malformed address: %s%s", argv[0], VTY_NEWLINE); return CMD_WARNING; } - su = sockunion_str2su (argv[1]); - if (su == NULL) + peer = peer_lookup (NULL, &su); + if (! peer || ! peer->afc[AFI_IP6][SAFI_MPLS_VPN]) { - vty_out (vty, "Malformed address: %s%s", argv[0], VTY_NEWLINE); - return CMD_WARNING; + vty_out (vty, "%% No such neighbor or address family%s", VTY_NEWLINE); + return CMD_WARNING; } - peer = peer_lookup (NULL, su); + return bgp_show_mpls_vpn (vty, AFI_IP6, NULL, bgp_show_type_neighbor, &su, 0); +} + +DEFUN (show_bgp_ipv4_vpn_neighbor_advertised_routes, + show_bgp_ipv4_vpn_neighbor_advertised_routes_cmd, + "show bgp ipv4 vpn neighbors (A.B.C.D|X:X::X:X) advertised-routes", + SHOW_STR + BGP_STR + "Address Family\n" + "Display VPN NLRI specific information\n" + "Detailed information on TCP and BGP neighbor connections\n" + "Neighbor to display information about\n" + "Display the routes advertised to a BGP neighbor\n") +{ + int ret; + struct peer *peer; + union sockunion su; + + ret = str2sockunion (argv[0], &su); + if (ret < 0) + { + vty_out (vty, "%% Malformed address: %s%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + peer = peer_lookup (NULL, &su); if (! peer || ! peer->afc[AFI_IP][SAFI_MPLS_VPN]) { vty_out (vty, "%% No such neighbor or address family%s", VTY_NEWLINE); return CMD_WARNING; } - return bgp_show_mpls_vpn (vty, &prd, bgp_show_type_neighbor, su, 0); + return show_adj_route_vpn (vty, peer, NULL); } - -DEFUN (show_ip_bgp_vpnv4_all_neighbor_advertised_routes, - show_ip_bgp_vpnv4_all_neighbor_advertised_routes_cmd, - "show ip bgp vpnv4 all neighbors A.B.C.D advertised-routes", +DEFUN (show_bgp_ipv6_vpn_neighbor_advertised_routes, + show_bgp_ipv6_vpn_neighbor_advertised_routes_cmd, + "show bgp ipv6 vpn neighbors (A.B.C.D|X:X::X:X) advertised-routes", SHOW_STR - IP_STR BGP_STR - "Display VPNv4 NLRI specific information\n" - "Display information about all VPNv4 NLRIs\n" + "Address Family\n" + "Display VPN NLRI specific information\n" "Detailed information on TCP and BGP neighbor connections\n" "Neighbor to display information about\n" "Display the routes advertised to a BGP neighbor\n") @@ -666,7 +865,7 @@ DEFUN (show_ip_bgp_vpnv4_all_neighbor_advertised_routes, return CMD_WARNING; } peer = peer_lookup (NULL, &su); - if (! peer || ! peer->afc[AFI_IP][SAFI_MPLS_VPN]) + if (! peer || ! peer->afc[AFI_IP6][SAFI_MPLS_VPN]) { vty_out (vty, "%% No such neighbor or address family%s", VTY_NEWLINE); return CMD_WARNING; @@ -676,27 +875,27 @@ DEFUN (show_ip_bgp_vpnv4_all_neighbor_advertised_routes, } DEFUN (show_ip_bgp_vpnv4_rd_neighbor_advertised_routes, - show_ip_bgp_vpnv4_rd_neighbor_advertised_routes_cmd, - "show ip bgp vpnv4 rd ASN:nn_or_IP-address:nn neighbors A.B.C.D advertised-routes", + show_bgp_ipv4_vpn_rd_neighbor_advertised_routes_cmd, + "show bgp ipv4 vpn rd ASN:nn_or_IP-address:nn neighbors (A.B.C.D|X:X::X:X) advertised-routes", SHOW_STR - IP_STR BGP_STR - "Display VPNv4 NLRI specific information\n" + "Address Family\n" + "Display VPN NLRI specific information\n" "Display information for a route distinguisher\n" "VPN Route Distinguisher\n" "Detailed information on TCP and BGP neighbor connections\n" "Neighbor to display information about\n" + "Neighbor to display information about\n" "Display the routes advertised to a BGP neighbor\n") { int ret; struct peer *peer; struct prefix_rd prd; union sockunion su; - ret = str2sockunion (argv[1], &su); if (ret < 0) { - vty_out (vty, "%% Malformed address: %s%s", argv[0], VTY_NEWLINE); + vty_out (vty, "%% Malformed address: %s%s", argv[1], VTY_NEWLINE); return CMD_WARNING; } peer = peer_lookup (NULL, &su); @@ -715,29 +914,150 @@ DEFUN (show_ip_bgp_vpnv4_rd_neighbor_advertised_routes, return show_adj_route_vpn (vty, peer, &prd); } +DEFUN (show_ip_bgp_vpnv6_rd_neighbor_advertised_routes, + show_bgp_ipv6_vpn_rd_neighbor_advertised_routes_cmd, + "show bgp ipv6 vpn rd ASN:nn_or_IP-address:nn neighbors (A.B.C.D|X:X::X:X) advertised-routes", + SHOW_STR + BGP_STR + "Address Family\n" + "Display VPN NLRI specific information\n" + "Display information for a route distinguisher\n" + "VPN Route Distinguisher\n" + "Detailed information on TCP and BGP neighbor connections\n" + "Neighbor to display information about\n" + "Neighbor to display information about\n" + "Display the routes advertised to a BGP neighbor\n") +{ + int ret; + struct peer *peer; + struct prefix_rd prd; + union sockunion su; + ret = str2sockunion (argv[1], &su); + if (ret < 0) + { + vty_out (vty, "%% Malformed address: %s%s", argv[1], VTY_NEWLINE); + return CMD_WARNING; + } + peer = peer_lookup (NULL, &su); + if (! peer || ! peer->afc[AFI_IP6][SAFI_MPLS_VPN]) + { + vty_out (vty, "%% No such neighbor or address family%s", VTY_NEWLINE); + return CMD_WARNING; + } + + ret = str2prefix_rd (argv[0], &prd); + if (! ret) + { + vty_out (vty, "%% Malformed Route Distinguisher%s", VTY_NEWLINE); + return CMD_WARNING; + } + + return show_adj_route_vpn (vty, peer, &prd); +} + +DEFUN (show_bgp_ipv4_vpn_rd_neighbor_routes, + show_bgp_ipv4_vpn_rd_neighbor_routes_cmd, + "show bgp ipv4 vpn rd ASN:nn_or_IP-address:nn neighbors (A.B.C.D|X:X::X:X) routes", + SHOW_STR + BGP_STR + "Address Family\n" + "Address Family modifier\n" + "Display information for a route distinguisher\n" + "VPN Route Distinguisher\n" + "Detailed information on TCP and BGP neighbor connections\n" + "Neighbor to display information about\n" + "Display routes learned from neighbor\n") +{ + int ret; + union sockunion su; + struct peer *peer; + struct prefix_rd prd; + + ret = str2prefix_rd (argv[0], &prd); + if (! ret) + { + vty_out (vty, "%% Malformed Route Distinguisher%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (str2sockunion(argv[1], &su)) + { + vty_out (vty, "Malformed address: %s%s", argv[1], VTY_NEWLINE); + return CMD_WARNING; + } + + peer = peer_lookup (NULL, &su); + if (! peer || ! peer->afc[AFI_IP][SAFI_MPLS_VPN]) + { + vty_out (vty, "%% No such neighbor or address family%s", VTY_NEWLINE); + return CMD_WARNING; + } + + return bgp_show_mpls_vpn (vty, AFI_IP, &prd, bgp_show_type_neighbor, &su, 0); +} +DEFUN (show_bgp_ipv6_vpn_rd_neighbor_routes, + show_bgp_ipv6_vpn_rd_neighbor_routes_cmd, + "show bgp ipv6 vpn rd ASN:nn_or_IP-address:nn neighbors (A.B.C.D|X:X::X:X) routes", + SHOW_STR + BGP_STR + "Address Family\n" + "Address Family modifier\n" + "Display information for a route distinguisher\n" + "VPN Route Distinguisher\n" + "Detailed information on TCP and BGP neighbor connections\n" + "Neighbor to display information about\n" + "Display routes learned from neighbor\n") +{ + int ret; + union sockunion su; + struct peer *peer; + struct prefix_rd prd; + + ret = str2prefix_rd (argv[0], &prd); + if (! ret) + { + vty_out (vty, "%% Malformed Route Distinguisher%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (str2sockunion(argv[1], &su)) + { + vty_out (vty, "Malformed address: %s%s", argv[1], VTY_NEWLINE); + return CMD_WARNING; + } + + peer = peer_lookup (NULL, &su); + if (! peer || ! peer->afc[AFI_IP6][SAFI_MPLS_VPN]) + { + vty_out (vty, "%% No such neighbor or address family%s", VTY_NEWLINE); + return CMD_WARNING; + } + + return bgp_show_mpls_vpn (vty, AFI_IP6, &prd, bgp_show_type_neighbor, &su, 0); +} void bgp_mplsvpn_init (void) { install_element (BGP_VPNV4_NODE, &vpnv4_network_cmd); + install_element (BGP_VPNV4_NODE, &vpnv4_network_route_map_cmd); install_element (BGP_VPNV4_NODE, &no_vpnv4_network_cmd); - - install_element (VIEW_NODE, &show_ip_bgp_vpnv4_all_cmd); - install_element (VIEW_NODE, &show_ip_bgp_vpnv4_rd_cmd); - install_element (VIEW_NODE, &show_ip_bgp_vpnv4_all_tags_cmd); - install_element (VIEW_NODE, &show_ip_bgp_vpnv4_rd_tags_cmd); - install_element (VIEW_NODE, &show_ip_bgp_vpnv4_all_neighbor_routes_cmd); - install_element (VIEW_NODE, &show_ip_bgp_vpnv4_rd_neighbor_routes_cmd); - install_element (VIEW_NODE, &show_ip_bgp_vpnv4_all_neighbor_advertised_routes_cmd); - install_element (VIEW_NODE, &show_ip_bgp_vpnv4_rd_neighbor_advertised_routes_cmd); - - install_element (ENABLE_NODE, &show_ip_bgp_vpnv4_all_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_vpnv4_rd_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_vpnv4_all_tags_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_vpnv4_rd_tags_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_vpnv4_all_neighbor_routes_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_vpnv4_rd_neighbor_routes_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_vpnv4_all_neighbor_advertised_routes_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_vpnv4_rd_neighbor_advertised_routes_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_vpn_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_vpn_rd_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_vpn_tags_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_vpn_rd_tags_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_vpn_neighbor_routes_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_vpn_neighbor_advertised_routes_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_vpn_rd_neighbor_advertised_routes_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_vpn_rd_neighbor_routes_cmd); + + install_element (VIEW_NODE, &show_bgp_ipv6_vpn_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_vpn_rd_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_vpn_tags_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_vpn_rd_tags_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_vpn_neighbor_routes_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_vpn_neighbor_advertised_routes_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_vpn_rd_neighbor_advertised_routes_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_vpn_rd_neighbor_routes_cmd); } diff --git a/bgpd/bgp_mplsvpn.h b/bgpd/bgp_mplsvpn.h index b221c3bdd..3299b9cb9 100644 --- a/bgpd/bgp_mplsvpn.h +++ b/bgpd/bgp_mplsvpn.h @@ -23,6 +23,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #define RD_TYPE_AS 0 #define RD_TYPE_IP 1 +#define RD_TYPE_AS4 2 #define RD_ADDRSTRLEN 28 @@ -41,7 +42,7 @@ struct rd_ip }; extern void bgp_mplsvpn_init (void); -extern int bgp_nlri_parse_vpnv4 (struct peer *, struct attr *, struct bgp_nlri *); +extern int bgp_nlri_parse_vpn (struct peer *, struct attr *, struct bgp_nlri *); extern u_int32_t decode_label (u_char *); extern int str2prefix_rd (const char *, struct prefix_rd *); extern int str2tag (const char *, u_char *); diff --git a/bgpd/bgp_network.c b/bgpd/bgp_network.c index a7dca5313..16042392c 100644 --- a/bgpd/bgp_network.c +++ b/bgpd/bgp_network.c @@ -31,6 +31,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "privs.h" #include "linklist.h" #include "network.h" +#include "filter.h" #include "bgpd/bgpd.h" #include "bgpd/bgp_fsm.h" @@ -47,7 +48,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 +123,64 @@ 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)); + } + } +} + +void +bgp_set_socket_ttl (struct peer *peer, int bgp_sock) +{ + char buf[INET_ADDRSTRLEN]; + int ret, ttl, minttl; + + if (bgp_sock < 0) + return; + + if (peer->gtsm_hops) + { + ttl = 255; + minttl = 256 - peer->gtsm_hops; + } + else + { + ttl = peer_ttl (peer); + minttl = 0; + } + + ret = sockopt_ttl (peer->su.sa.sa_family, bgp_sock, 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); + + ret = sockopt_minttl (peer->su.sa.sa_family, bgp_sock, minttl); + if (ret && (errno != ENOTSUP || minttl)) + 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) @@ -142,7 +200,7 @@ bgp_accept (struct thread *thread) zlog_err ("accept_sock is nevative value %d", accept_sock); return -1; } - listener->thread = thread_add_read (master, bgp_accept, listener, accept_sock); + listener->thread = thread_add_read (bm->master, bgp_accept, listener, accept_sock); /* Accept client connection. */ bgp_sock = sockunion_accept (accept_sock, &su); @@ -153,12 +211,19 @@ 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)); + zlog_debug ("[Event] BGP connection from host %s:%d", + inet_sutop (&su, buf), sockunion_get_port (&su)); /* Check remote IP address */ peer1 = peer_lookup (NULL, &su); - if (! peer1 || peer1->status == Idle) + /* We could perhaps just drop new connections from already Established + * peers here. + */ + if (! peer1 || peer1->status == Idle || peer1->status > Established) { if (BGP_DEBUG (events, EVENTS)) { @@ -166,39 +231,47 @@ bgp_accept (struct thread *thread) zlog_debug ("[Event] BGP connection IP address %s is not configured", inet_sutop (&su, buf)); else - zlog_debug ("[Event] BGP connection IP address %s is Idle state", - inet_sutop (&su, buf)); + zlog_debug ("[Event] BGP connection IP address %s is %s state", + inet_sutop (&su, buf), + LOOKUP (bgp_status_msg, peer1->status)); } close (bgp_sock); return -1; } - /* In case of peer is EBGP, we should set TTL for this connection. */ - if (peer_sort (peer1) == 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)) zlog_debug ("[Event] Make dummy peer structure until read Open packet"); { - char buf[SU_ADDRSTRLEN + 1]; + char buf[SU_ADDRSTRLEN]; peer = peer_create_accept (peer1->bgp); - SET_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER); peer->su = su; peer->fd = bgp_sock; peer->status = Active; + + /* Config state that should affect OPEN packet must be copied over */ peer->local_id = peer1->local_id; peer->v_holdtime = peer1->v_holdtime; peer->v_keepalive = peer1->v_keepalive; - + peer->local_as = peer1->local_as; + peer->change_local_as = peer1->change_local_as; + peer->flags = peer1->flags; + peer->sflags = peer1->sflags; + #define PEER_ARRAY_COPY(D,S,A) \ + memcpy ((D)->A, (S)->A, sizeof (((D)->A)[0][0])*AFI_MAX*SAFI_MAX); + PEER_ARRAY_COPY(peer, peer1, afc); + PEER_ARRAY_COPY(peer, peer1, af_flags); + #undef PEER_ARRAY_COPY + /* Make peer's address string. */ sockunion2str (&su, buf, SU_ADDRSTRLEN); peer->host = XSTRDUP (MTYPE_BGP_PEER_HOST, buf); + + SET_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER); } BGP_EVENT_ADD (peer, TCP_connection_open); @@ -213,6 +286,7 @@ bgp_bind (struct peer *peer) #ifdef SO_BINDTODEVICE int ret; struct ifreq ifreq; + int myerrno; if (! peer->ifname) return 0; @@ -224,13 +298,15 @@ bgp_bind (struct peer *peer) ret = setsockopt (peer->fd, SOL_SOCKET, SO_BINDTODEVICE, &ifreq, sizeof (ifreq)); - + myerrno = errno; + if (bgpd_privs.change (ZPRIVS_LOWER) ) zlog_err ("bgp_bind: could not lower privs"); if (ret < 0) { - zlog (peer->log, LOG_INFO, "bind to interface %s failed", peer->ifname); + zlog (peer->log, LOG_INFO, "bind to interface %s failed, errno=%d", + peer->ifname, myerrno); return ret; } #endif /* SO_BINDTODEVICE */ @@ -241,28 +317,27 @@ static int bgp_update_address (struct interface *ifp, const union sockunion *dst, union sockunion *addr) { - struct prefix *p, *sel, *d; + struct prefix *p, *sel, d; struct connected *connected; struct listnode *node; int common; - d = sockunion2hostprefix (dst); + sockunion2hostprefix (dst, &d); sel = NULL; common = -1; for (ALL_LIST_ELEMENTS_RO (ifp->connected, node, connected)) { p = connected->address; - if (p->family != d->family) + if (p->family != d.family) continue; - if (prefix_common_bits (p, d) > common) + if (prefix_common_bits (p, &d) > common) { sel = p; - common = prefix_common_bits (sel, d); + common = prefix_common_bits (sel, &d); } } - prefix_free (d); if (!sel) return 1; @@ -299,19 +374,19 @@ bgp_update_source (struct peer *peer) int bgp_connect (struct peer *peer) { - unsigned int ifindex = 0; + ifindex_t ifindex = 0; /* Make socket for the peer. */ peer->fd = sockunion_socket (&peer->su); if (peer->fd < 0) return -1; - /* If we can get socket for the peer, adjest TTL and make connection. */ - if (peer_sort (peer) == 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); @@ -321,10 +396,8 @@ bgp_connect (struct peer *peer) zlog_err ("%s: could not raise privs", __func__); if (sockunion_family (&peer->su) == AF_INET) setsockopt_ipv4_tos (peer->fd, IPTOS_PREC_INTERNETCONTROL); -# ifdef HAVE_IPV6 else if (sockunion_family (&peer->su) == AF_INET6) setsockopt_ipv6_tclass (peer->fd, IPTOS_PREC_INTERNETCONTROL); -# endif if (bgpd_privs.change (ZPRIVS_LOWER)) zlog_err ("%s: could not lower privs", __func__); #endif @@ -338,10 +411,8 @@ bgp_connect (struct peer *peer) /* Update source bind. */ bgp_update_source (peer); -#ifdef HAVE_IPV6 if (peer->ifname) - ifindex = if_nametoindex (peer->ifname); -#endif /* HAVE_IPV6 */ + ifindex = ifname2ifindex (peer->ifname); if (BGP_DEBUG (events, EVENTS)) plog_debug (peer->log, "%s [Event] Connect start to %s fd %d", @@ -389,10 +460,8 @@ bgp_listener (int sock, struct sockaddr *sa, socklen_t salen) #ifdef IPTOS_PREC_INTERNETCONTROL if (sa->sa_family == AF_INET) setsockopt_ipv4_tos (sock, IPTOS_PREC_INTERNETCONTROL); -# ifdef HAVE_IPV6 else if (sa->sa_family == AF_INET6) setsockopt_ipv6_tclass (sock, IPTOS_PREC_INTERNETCONTROL); -# endif #endif sockopt_v6only (sa->sa_family, sock); @@ -418,14 +487,13 @@ bgp_listener (int sock, struct sockaddr *sa, socklen_t salen) listener = XMALLOC (MTYPE_BGP_LISTENER, sizeof(*listener)); listener->fd = sock; memcpy(&listener->su, sa, salen); - listener->thread = thread_add_read (master, bgp_accept, listener, sock); + listener->thread = thread_add_read (bm->master, bgp_accept, listener, sock); listnode_add (bm->listen_sockets, listener); return 0; } /* IPv6 supported version of BGP server socket setup. */ -#if defined (HAVE_IPV6) && ! defined (NRL) int bgp_socket (unsigned short port, const char *address) { @@ -482,50 +550,6 @@ bgp_socket (unsigned short port, const char *address) return 0; } -#else -/* Traditional IPv4 only version. */ -int -bgp_socket (unsigned short port, const char *address) -{ - int sock; - int socklen; - struct sockaddr_in sin; - int ret, en; - - sock = socket (AF_INET, SOCK_STREAM, 0); - if (sock < 0) - { - zlog_err ("socket: %s", safe_strerror (errno)); - return sock; - } - - /* if we intend to implement ttl-security, this socket needs ttl=255 */ - sockopt_ttl (AF_INET, sock, MAXTTL); - - memset (&sin, 0, sizeof (struct sockaddr_in)); - sin.sin_family = AF_INET; - sin.sin_port = htons (port); - socklen = sizeof (struct sockaddr_in); - - if (address && ((ret = inet_aton(address, &sin.sin_addr)) < 1)) - { - zlog_err("bgp_socket: could not parse ip address %s: %s", - address, safe_strerror (errno)); - return ret; - } -#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN - sin.sin_len = socklen; -#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */ - - ret = bgp_listener (sock, (struct sockaddr *) &sin, socklen); - if (ret < 0) - { - close (sock); - return ret; - } - return sock; -} -#endif /* HAVE_IPV6 && !NRL */ void bgp_close (void) diff --git a/bgpd/bgp_network.h b/bgpd/bgp_network.h index 5bf2e5ff1..31995caf4 100644 --- a/bgpd/bgp_network.h +++ b/bgpd/bgp_network.h @@ -21,11 +21,14 @@ 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 *); extern void bgp_getsockname (struct peer *); +extern void bgp_set_socket_ttl (struct peer *peer, int bgp_sock); extern int bgp_md5_set (struct peer *); #endif /* _QUAGGA_BGP_NETWORK_H */ diff --git a/bgpd/bgp_nexthop.c b/bgpd/bgp_nexthop.c index fdf251b2a..479ef9493 100644 --- a/bgpd/bgp_nexthop.c +++ b/bgpd/bgp_nexthop.c @@ -28,61 +28,38 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "network.h" #include "log.h" #include "memory.h" +#include "hash.h" +#include "jhash.h" +#include "filter.h" +#include "nexthop.h" #include "bgpd/bgpd.h" #include "bgpd/bgp_table.h" #include "bgpd/bgp_route.h" #include "bgpd/bgp_attr.h" #include "bgpd/bgp_nexthop.h" +#include "bgpd/bgp_nht.h" #include "bgpd/bgp_debug.h" #include "bgpd/bgp_damp.h" #include "zebra/rib.h" #include "zebra/zserv.h" /* For ZEBRA_SERV_PATH. */ -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; - -/* BGP import thread */ -static struct thread *bgp_import_thread = NULL; - -/* BGP scan interval. */ -static int bgp_scan_interval; - -/* BGP import interval. */ -static int bgp_import_interval; /* Route table for next-hop lookup cache. */ -static struct bgp_table *bgp_nexthop_cache_table[AFI_MAX]; +struct bgp_table *bgp_nexthop_cache_table[AFI_MAX]; static struct bgp_table *cache1_table[AFI_MAX]; -static struct bgp_table *cache2_table[AFI_MAX]; /* Route table for connected route. */ 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) +char * +bnc_str (struct bgp_nexthop_cache *bnc, char *buf, int size) { - struct nexthop *last; - - for (last = bnc->nexthop; last && last->next; last = last->next) - ; - if (last) - last->next = nexthop; - else - bnc->nexthop = nexthop; - nexthop->prev = last; + prefix2str(&(bnc->node->p), buf, size); + return buf; } -static void +void bnc_nexthop_free (struct bgp_nexthop_cache *bnc) { struct nexthop *nexthop; @@ -95,79 +72,22 @@ bnc_nexthop_free (struct bgp_nexthop_cache *bnc) } } -static struct bgp_nexthop_cache * +struct bgp_nexthop_cache * bnc_new (void) { - return XCALLOC (MTYPE_BGP_NEXTHOP_CACHE, sizeof (struct bgp_nexthop_cache)); + struct bgp_nexthop_cache *bnc; + + bnc = XCALLOC (MTYPE_BGP_NEXTHOP_CACHE, sizeof (struct bgp_nexthop_cache)); + LIST_INIT(&(bnc->paths)); + return bnc; } -static void +void 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) -{ - if (next1->type != next2->type) - return 0; - - switch (next1->type) - { - case ZEBRA_NEXTHOP_IPV4: - if (! IPV4_ADDR_SAME (&next1->gate.ipv4, &next2->gate.ipv4)) - return 0; - break; - case ZEBRA_NEXTHOP_IFINDEX: - case ZEBRA_NEXTHOP_IFNAME: - if (next1->ifindex != next2->ifindex) - return 0; - break; -#ifdef HAVE_IPV6 - case ZEBRA_NEXTHOP_IPV6: - if (! IPV6_ADDR_SAME (&next1->gate.ipv6, &next2->gate.ipv6)) - return 0; - break; - case ZEBRA_NEXTHOP_IPV6_IFINDEX: - case ZEBRA_NEXTHOP_IPV6_IFNAME: - if (! IPV6_ADDR_SAME (&next1->gate.ipv6, &next2->gate.ipv6)) - return 0; - if (next1->ifindex != next2->ifindex) - return 0; - break; -#endif /* HAVE_IPV6 */ - default: - /* do nothing */ - break; - } - return 1; -} - -static int -bgp_nexthop_cache_different (struct bgp_nexthop_cache *bnc1, - struct bgp_nexthop_cache *bnc2) -{ - int i; - struct nexthop *next1, *next2; - - if (bnc1->nexthop_num != bnc2->nexthop_num) - return 1; - - next1 = bnc1->nexthop; - next2 = bnc2->nexthop; - - for (i = 0; i < bnc1->nexthop_num; i++) - { - if (! bgp_nexthop_same (next1, next2)) - return 1; - - next1 = next1->next; - next2 = next2->next; - } - return 0; -} /* If nexthop exists on connected network return 1. */ int @@ -175,10 +95,6 @@ bgp_nexthop_onlink (afi_t afi, struct attr *attr) { struct bgp_node *rn; - /* If zebra is not enabled return */ - if (zlookup->sock < 0) - return 1; - /* Lookup the address is onlink or not. */ if (afi == AFI_IP) { @@ -189,7 +105,6 @@ bgp_nexthop_onlink (afi_t afi, struct attr *attr) return 1; } } -#ifdef HAVE_IPV6 else if (afi == AFI_IP6) { if (attr->extra->mp_nexthop_len == 32) @@ -208,323 +123,104 @@ bgp_nexthop_onlink (afi_t afi, struct attr *attr) } } } -#endif /* HAVE_IPV6 */ return 0; } -#ifdef HAVE_IPV6 -/* Check specified next-hop is reachable or not. */ -static int -bgp_nexthop_lookup_ipv6 (struct peer *peer, struct bgp_info *ri, int *changed, - int *metricchanged) +/* BGP own address structure */ +struct bgp_addr { - struct bgp_node *rn; - struct prefix p; - struct bgp_nexthop_cache *bnc; - struct attr *attr; - - /* If lookup is not enabled, return valid. */ - if (zlookup->sock < 0) - { - if (ri->extra) - ri->extra->igpmetric = 0; - return 1; - } - - /* Only check IPv6 global address only nexthop. */ - attr = ri->attr; - - if (attr->extra->mp_nexthop_len != 16 - || IN6_IS_ADDR_LINKLOCAL (&attr->extra->mp_nexthop_global)) - return 1; - - memset (&p, 0, sizeof (struct prefix)); - p.family = AF_INET6; - p.prefixlen = IPV6_MAX_BITLEN; - p.u.prefix6 = attr->extra->mp_nexthop_global; - - /* IBGP or ebgp-multihop */ - rn = bgp_node_get (bgp_nexthop_cache_table[AFI_IP6], &p); - - if (rn->info) - { - bnc = rn->info; - bgp_unlock_node (rn); - } - else - { - if (NULL == (bnc = zlookup_query_ipv6 (&attr->extra->mp_nexthop_global))) - bnc = bnc_new (); - else - { - if (changed) - { - struct bgp_table *old; - struct bgp_node *oldrn; - - if (bgp_nexthop_cache_table[AFI_IP6] == cache1_table[AFI_IP6]) - old = cache2_table[AFI_IP6]; - else - old = cache1_table[AFI_IP6]; - - oldrn = bgp_node_lookup (old, &p); - if (oldrn) - { - struct bgp_nexthop_cache *oldbnc = oldrn->info; - - bnc->changed = bgp_nexthop_cache_different (bnc, oldbnc); - - if (bnc->metric != oldbnc->metric) - bnc->metricchanged = 1; - - bgp_unlock_node (oldrn); - } - } - } - rn->info = bnc; - } + struct in_addr addr; + int refcnt; +}; - if (changed) - *changed = bnc->changed; +static struct hash *bgp_address_hash; - if (metricchanged) - *metricchanged = bnc->metricchanged; +static void * +bgp_address_hash_alloc (void *p) +{ + struct in_addr *val = p; + struct bgp_addr *addr; - if (bnc->valid && bnc->metric) - (bgp_info_extra_get (ri))->igpmetric = bnc->metric; - else if (ri->extra) - ri->extra->igpmetric = 0; + addr = XMALLOC (MTYPE_BGP_ADDR, sizeof (struct bgp_addr)); + addr->refcnt = 0; + addr->addr.s_addr = val->s_addr; - return bnc->valid; + return addr; } -#endif /* HAVE_IPV6 */ -/* Check specified next-hop is reachable or not. */ -int -bgp_nexthop_lookup (afi_t afi, struct peer *peer, struct bgp_info *ri, - int *changed, int *metricchanged) +static unsigned int +bgp_address_hash_key_make (void *p) { - struct bgp_node *rn; - struct prefix p; - struct bgp_nexthop_cache *bnc; - struct in_addr addr; - - /* If lookup is not enabled, return valid. */ - if (zlookup->sock < 0) - { - if (ri->extra) - ri->extra->igpmetric = 0; - return 1; - } - -#ifdef HAVE_IPV6 - if (afi == AFI_IP6) - return bgp_nexthop_lookup_ipv6 (peer, ri, changed, metricchanged); -#endif /* HAVE_IPV6 */ - - addr = ri->attr->nexthop; - - memset (&p, 0, sizeof (struct prefix)); - p.family = AF_INET; - p.prefixlen = IPV4_MAX_BITLEN; - p.u.prefix4 = addr; - - /* IBGP or ebgp-multihop */ - rn = bgp_node_get (bgp_nexthop_cache_table[AFI_IP], &p); - - if (rn->info) - { - bnc = rn->info; - bgp_unlock_node (rn); - } - else - { - if (NULL == (bnc = zlookup_query (addr))) - bnc = bnc_new (); - else - { - if (changed) - { - struct bgp_table *old; - struct bgp_node *oldrn; - - if (bgp_nexthop_cache_table[AFI_IP] == cache1_table[AFI_IP]) - old = cache2_table[AFI_IP]; - else - old = cache1_table[AFI_IP]; - - oldrn = bgp_node_lookup (old, &p); - if (oldrn) - { - struct bgp_nexthop_cache *oldbnc = oldrn->info; - - bnc->changed = bgp_nexthop_cache_different (bnc, oldbnc); + const struct bgp_addr *addr = p; - if (bnc->metric != oldbnc->metric) - bnc->metricchanged = 1; - - bgp_unlock_node (oldrn); - } - } - } - rn->info = bnc; - } - - if (changed) - *changed = bnc->changed; - - if (metricchanged) - *metricchanged = bnc->metricchanged; + return jhash_1word(addr->addr.s_addr, 0); +} - if (bnc->valid && bnc->metric) - (bgp_info_extra_get(ri))->igpmetric = bnc->metric; - else if (ri->extra) - ri->extra->igpmetric = 0; +static int +bgp_address_hash_cmp (const void *p1, const void *p2) +{ + const struct bgp_addr *addr1 = p1; + const struct bgp_addr *addr2 = p2; - return bnc->valid; + return addr1->addr.s_addr == addr2->addr.s_addr; } -/* Reset and free all BGP nexthop cache. */ -static void -bgp_nexthop_cache_reset (struct bgp_table *table) +void +bgp_address_init (void) { - struct bgp_node *rn; - struct bgp_nexthop_cache *bnc; - - for (rn = bgp_table_top (table); rn; rn = bgp_route_next (rn)) - if ((bnc = rn->info) != NULL) - { - bnc_free (bnc); - rn->info = NULL; - bgp_unlock_node (rn); - } + bgp_address_hash = hash_create (bgp_address_hash_key_make, + bgp_address_hash_cmp); } -static void -bgp_scan (afi_t afi, safi_t safi) +void +bgp_address_destroy (void) { - struct bgp_node *rn; - struct bgp *bgp; - struct bgp_info *bi; - struct bgp_info *next; - struct peer *peer; - struct listnode *node, *nnode; - int valid; - int current; - int changed; - int metricchanged; - - /* Change cache. */ - if (bgp_nexthop_cache_table[afi] == cache1_table[afi]) - bgp_nexthop_cache_table[afi] = cache2_table[afi]; - else - bgp_nexthop_cache_table[afi] = cache1_table[afi]; - - /* Get default bgp. */ - bgp = bgp_get_default (); - if (bgp == NULL) + if (bgp_address_hash == NULL) return; - /* Maximum prefix check */ - for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer)) - { - if (peer->status != Established) - continue; - - if (peer->afc[afi][SAFI_UNICAST]) - bgp_maximum_prefix_overflow (peer, afi, SAFI_UNICAST, 1); - if (peer->afc[afi][SAFI_MULTICAST]) - bgp_maximum_prefix_overflow (peer, afi, SAFI_MULTICAST, 1); - if (peer->afc[afi][SAFI_MPLS_VPN]) - bgp_maximum_prefix_overflow (peer, afi, SAFI_MPLS_VPN, 1); - } + hash_clean(bgp_address_hash, NULL); + hash_free(bgp_address_hash); + bgp_address_hash = NULL; +} - for (rn = bgp_table_top (bgp->rib[afi][SAFI_UNICAST]); rn; - rn = bgp_route_next (rn)) - { - for (bi = rn->info; bi; bi = next) - { - next = bi->next; +static void +bgp_address_add (struct prefix *p) +{ + struct bgp_addr tmp; + struct bgp_addr *addr; - if (bi->type == ZEBRA_ROUTE_BGP && bi->sub_type == BGP_ROUTE_NORMAL) - { - changed = 0; - metricchanged = 0; + tmp.addr = p->u.prefix4; - if (peer_sort (bi->peer) == BGP_PEER_EBGP && bi->peer->ttl == 1) - valid = bgp_nexthop_onlink (afi, bi->attr); - else - valid = bgp_nexthop_lookup (afi, bi->peer, bi, - &changed, &metricchanged); + addr = hash_get (bgp_address_hash, &tmp, bgp_address_hash_alloc); + if (!addr) + return; - current = CHECK_FLAG (bi->flags, BGP_INFO_VALID) ? 1 : 0; + addr->refcnt++; +} - if (changed) - SET_FLAG (bi->flags, BGP_INFO_IGP_CHANGED); - else - UNSET_FLAG (bi->flags, BGP_INFO_IGP_CHANGED); +static void +bgp_address_del (struct prefix *p) +{ + struct bgp_addr tmp; + struct bgp_addr *addr; - if (valid != current) - { - if (CHECK_FLAG (bi->flags, BGP_INFO_VALID)) - { - bgp_aggregate_decrement (bgp, &rn->p, bi, - afi, SAFI_UNICAST); - bgp_info_unset_flag (rn, bi, BGP_INFO_VALID); - } - else - { - bgp_info_set_flag (rn, bi, BGP_INFO_VALID); - bgp_aggregate_increment (bgp, &rn->p, bi, - afi, SAFI_UNICAST); - } - } + tmp.addr = p->u.prefix4; - if (CHECK_FLAG (bgp->af_flags[afi][SAFI_UNICAST], - BGP_CONFIG_DAMPENING) - && bi->extra && bi->extra->damp_info ) - if (bgp_damp_scan (bi, afi, SAFI_UNICAST)) - bgp_aggregate_increment (bgp, &rn->p, bi, - afi, SAFI_UNICAST); - } - } - bgp_process (bgp, rn, afi, SAFI_UNICAST); - } + addr = hash_lookup (bgp_address_hash, &tmp); + /* may have been deleted earlier by bgp_interface_down() */ + if (addr == NULL) + return; - /* Flash old cache. */ - if (bgp_nexthop_cache_table[afi] == cache1_table[afi]) - bgp_nexthop_cache_reset (cache2_table[afi]); - else - bgp_nexthop_cache_reset (cache1_table[afi]); + addr->refcnt--; - if (BGP_DEBUG (events, EVENTS)) + if (addr->refcnt == 0) { - if (afi == AFI_IP) - zlog_debug ("scanning IPv4 Unicast routing tables"); - else if (afi == AFI_IP6) - zlog_debug ("scanning IPv6 Unicast routing tables"); + hash_release (bgp_address_hash, addr); + XFREE (MTYPE_BGP_ADDR, addr); } } -/* BGP scan thread. This thread check nexthop reachability. */ -static int -bgp_scan_timer (struct thread *t) -{ - bgp_scan_thread = - thread_add_timer (master, bgp_scan_timer, NULL, bgp_scan_interval); - - if (BGP_DEBUG (events, EVENTS)) - zlog_debug ("Performing BGP general scanning"); - - bgp_scan (AFI_IP, SAFI_UNICAST); -#ifdef HAVE_IPV6 - bgp_scan (AFI_IP6, SAFI_UNICAST); -#endif /* HAVE_IPV6 */ - - return 0; -} - struct bgp_connected_ref { unsigned int refcnt; @@ -549,14 +245,16 @@ bgp_connected_add (struct connected *ifc) addr = ifc->address; + p = *(CONNECTED_PREFIX(ifc)); if (addr->family == AF_INET) { - PREFIX_COPY_IPV4(&p, CONNECTED_PREFIX(ifc)); apply_mask_ipv4 ((struct prefix_ipv4 *) &p); if (prefix_ipv4_any ((struct prefix_ipv4 *) &p)) return; + bgp_address_add (addr); + rn = bgp_node_get (bgp_connected_table[AFI_IP], (struct prefix *) &p); if (rn->info) { @@ -570,10 +268,8 @@ bgp_connected_add (struct connected *ifc) rn->info = bc; } } -#ifdef HAVE_IPV6 else if (addr->family == AF_INET6) { - PREFIX_COPY_IPV6(&p, CONNECTED_PREFIX(ifc)); apply_mask_ipv6 ((struct prefix_ipv6 *) &p); if (IN6_IS_ADDR_UNSPECIFIED (&p.u.prefix6)) @@ -595,7 +291,6 @@ bgp_connected_add (struct connected *ifc) rn->info = bc; } } -#endif /* HAVE_IPV6 */ } void @@ -614,14 +309,16 @@ bgp_connected_delete (struct connected *ifc) addr = ifc->address; + p = *(CONNECTED_PREFIX(ifc)); if (addr->family == AF_INET) { - PREFIX_COPY_IPV4(&p, CONNECTED_PREFIX(ifc)); apply_mask_ipv4 ((struct prefix_ipv4 *) &p); if (prefix_ipv4_any ((struct prefix_ipv4 *) &p)) return; + bgp_address_del (addr); + rn = bgp_node_lookup (bgp_connected_table[AFI_IP], &p); if (! rn) return; @@ -636,10 +333,8 @@ bgp_connected_delete (struct connected *ifc) bgp_unlock_node (rn); bgp_unlock_node (rn); } -#ifdef HAVE_IPV6 else if (addr->family == AF_INET6) { - PREFIX_COPY_IPV6(&p, CONNECTED_PREFIX(ifc)); apply_mask_ipv6 ((struct prefix_ipv6 *) &p); if (IN6_IS_ADDR_UNSPECIFIED (&p.u.prefix6)) @@ -662,725 +357,211 @@ bgp_connected_delete (struct connected *ifc) bgp_unlock_node (rn); bgp_unlock_node (rn); } -#endif /* HAVE_IPV6 */ } int -bgp_nexthop_self (afi_t afi, struct attr *attr) -{ - struct listnode *node; - struct listnode *node2; - struct interface *ifp; - struct connected *ifc; - struct prefix *p; - - for (ALL_LIST_ELEMENTS_RO (iflist, node, ifp)) - { - for (ALL_LIST_ELEMENTS_RO (ifp->connected, node2, ifc)) - { - p = ifc->address; - - if (p && p->family == AF_INET - && IPV4_ADDR_SAME (&p->u.prefix4, &attr->nexthop)) - return 1; - } - } - return 0; -} - -static struct bgp_nexthop_cache * -zlookup_read (void) +bgp_nexthop_self (struct attr *attr) { - struct stream *s; - uint16_t length; - u_char marker; - u_char version; - uint16_t command; - int nbytes; - struct in_addr raddr; - uint32_t metric; - int i; - u_char nexthop_num; - struct nexthop *nexthop; - struct bgp_nexthop_cache *bnc; - - s = zlookup->ibuf; - stream_reset (s); + struct bgp_addr tmp, *addr; - nbytes = stream_read (s, zlookup->sock, 2); - length = stream_getw (s); + tmp.addr = attr->nexthop; - nbytes = stream_read (s, zlookup->sock, length - 2); - 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 NULL; - } - - command = stream_getw (s); - - raddr.s_addr = stream_get_ipv4 (s); - metric = stream_getl (s); - nexthop_num = stream_getc (s); - - if (nexthop_num) - { - bnc = bnc_new (); - bnc->valid = 1; - bnc->metric = metric; - bnc->nexthop_num = nexthop_num; - - for (i = 0; i < nexthop_num; i++) - { - nexthop = XCALLOC (MTYPE_NEXTHOP, sizeof (struct nexthop)); - nexthop->type = stream_getc (s); - switch (nexthop->type) - { - case ZEBRA_NEXTHOP_IPV4: - nexthop->gate.ipv4.s_addr = stream_get_ipv4 (s); - break; - case ZEBRA_NEXTHOP_IFINDEX: - case ZEBRA_NEXTHOP_IFNAME: - nexthop->ifindex = stream_getl (s); - break; - default: - /* do nothing */ - break; - } - bnc_nexthop_add (bnc, nexthop); - } - } - else - return NULL; - - return bnc; -} - -struct bgp_nexthop_cache * -zlookup_query (struct in_addr addr) -{ - int ret; - struct stream *s; - - /* Check socket. */ - if (zlookup->sock < 0) - return NULL; - - s = zlookup->obuf; - stream_reset (s); - zclient_create_header (s, ZEBRA_IPV4_NEXTHOP_LOOKUP); - 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 ("can't write to zlookup->sock"); - close (zlookup->sock); - zlookup->sock = -1; - return NULL; - } - if (ret == 0) - { - zlog_err ("zlookup->sock connection closed"); - close (zlookup->sock); - zlookup->sock = -1; - return NULL; - } - - return zlookup_read (); -} - -#ifdef HAVE_IPV6 -static struct bgp_nexthop_cache * -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; - u_char nexthop_num; - struct nexthop *nexthop; - struct bgp_nexthop_cache *bnc; - - s = zlookup->ibuf; - stream_reset (s); - - nbytes = stream_read (s, zlookup->sock, 2); - length = stream_getw (s); - - nbytes = stream_read (s, zlookup->sock, length - 2); - 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 NULL; - } - - command = stream_getw (s); - - stream_get (&raddr, s, 16); - - metric = stream_getl (s); - nexthop_num = stream_getc (s); - - if (nexthop_num) - { - bnc = bnc_new (); - bnc->valid = 1; - bnc->metric = metric; - bnc->nexthop_num = nexthop_num; - - for (i = 0; i < nexthop_num; i++) - { - nexthop = XCALLOC (MTYPE_NEXTHOP, sizeof (struct nexthop)); - nexthop->type = stream_getc (s); - switch (nexthop->type) - { - case ZEBRA_NEXTHOP_IPV6: - stream_get (&nexthop->gate.ipv6, s, 16); - break; - case ZEBRA_NEXTHOP_IPV6_IFINDEX: - case ZEBRA_NEXTHOP_IPV6_IFNAME: - stream_get (&nexthop->gate.ipv6, s, 16); - nexthop->ifindex = stream_getl (s); - break; - case ZEBRA_NEXTHOP_IFINDEX: - case ZEBRA_NEXTHOP_IFNAME: - nexthop->ifindex = stream_getl (s); - break; - default: - /* do nothing */ - break; - } - bnc_nexthop_add (bnc, nexthop); - } - } - else - return NULL; - - return bnc; -} - -struct bgp_nexthop_cache * -zlookup_query_ipv6 (struct in6_addr *addr) -{ - int ret; - struct stream *s; - - /* Check socket. */ - if (zlookup->sock < 0) - return NULL; - - s = zlookup->obuf; - stream_reset (s); - zclient_create_header (s, ZEBRA_IPV6_NEXTHOP_LOOKUP); - stream_put (s, addr, 16); - stream_putw_at (s, 0, stream_get_endp (s)); - - ret = writen (zlookup->sock, s->data, stream_get_endp (s)); - if (ret < 0) - { - zlog_err ("can't write to zlookup->sock"); - close (zlookup->sock); - zlookup->sock = -1; - return NULL; - } - if (ret == 0) - { - zlog_err ("zlookup->sock connection closed"); - close (zlookup->sock); - zlookup->sock = -1; - return NULL; - } - - return zlookup_read_ipv6 (); -} -#endif /* HAVE_IPV6 */ - -static int -bgp_import_check (struct prefix *p, u_int32_t *igpmetric, - struct in_addr *igpnexthop) -{ - struct stream *s; - int ret; - u_int16_t length, command; - u_char version, marker; - int nbytes; - struct in_addr addr; - struct in_addr nexthop; - u_int32_t metric = 0; - u_char nexthop_num; - u_char nexthop_type; - - /* If lookup connection is not available return valid. */ - if (zlookup->sock < 0) - { - if (igpmetric) - *igpmetric = 0; - return 1; - } - - /* Send query to the lookup connection */ - s = zlookup->obuf; - stream_reset (s); - zclient_create_header (s, ZEBRA_IPV4_IMPORT_LOOKUP); - - stream_putc (s, p->prefixlen); - stream_put_in_addr (s, &p->u.prefix4); - - stream_putw_at (s, 0, stream_get_endp (s)); - - /* Write the packet. */ - ret = writen (zlookup->sock, s->data, stream_get_endp (s)); - - if (ret < 0) - { - zlog_err ("can't write to zlookup->sock"); - close (zlookup->sock); - zlookup->sock = -1; - return 1; - } - if (ret == 0) - { - zlog_err ("zlookup->sock connection closed"); - close (zlookup->sock); - zlookup->sock = -1; - return 1; - } - - /* Get result. */ - stream_reset (s); - - /* Fetch length. */ - nbytes = stream_read (s, zlookup->sock, 2); - length = stream_getw (s); - - /* Fetch whole data. */ - nbytes = stream_read (s, zlookup->sock, length - 2); - 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 0; - } - - command = stream_getw (s); - - addr.s_addr = stream_get_ipv4 (s); - metric = stream_getl (s); - nexthop_num = stream_getc (s); - - /* Set IGP metric value. */ - if (igpmetric) - *igpmetric = metric; - - /* If there is nexthop then this is active route. */ - if (nexthop_num) - { - nexthop.s_addr = 0; - nexthop_type = stream_getc (s); - if (nexthop_type == ZEBRA_NEXTHOP_IPV4) - { - nexthop.s_addr = stream_get_ipv4 (s); - if (igpnexthop) - *igpnexthop = nexthop; - } - else - *igpnexthop = nexthop; - - return 1; - } - else - return 0; -} - -/* Scan all configured BGP route then check the route exists in IGP or - not. */ -static int -bgp_import (struct thread *t) -{ - struct bgp *bgp; - struct bgp_node *rn; - struct bgp_static *bgp_static; - struct listnode *node, *nnode; - int valid; - u_int32_t metric; - struct in_addr nexthop; - afi_t afi; - safi_t safi; - - bgp_import_thread = - thread_add_timer (master, bgp_import, NULL, bgp_import_interval); - - if (BGP_DEBUG (events, EVENTS)) - zlog_debug ("Import timer expired."); - - for (ALL_LIST_ELEMENTS (bm->bgp, node, nnode, bgp)) - { - for (afi = AFI_IP; afi < AFI_MAX; afi++) - for (safi = SAFI_UNICAST; safi < SAFI_MPLS_VPN; safi++) - for (rn = bgp_table_top (bgp->route[afi][safi]); rn; - rn = bgp_route_next (rn)) - if ((bgp_static = rn->info) != NULL) - { - if (bgp_static->backdoor) - continue; - - valid = bgp_static->valid; - metric = bgp_static->igpmetric; - nexthop = bgp_static->igpnexthop; - - if (bgp_flag_check (bgp, BGP_FLAG_IMPORT_CHECK) - && afi == AFI_IP && safi == SAFI_UNICAST) - bgp_static->valid = bgp_import_check (&rn->p, &bgp_static->igpmetric, - &bgp_static->igpnexthop); - else - { - bgp_static->valid = 1; - bgp_static->igpmetric = 0; - bgp_static->igpnexthop.s_addr = 0; - } - - if (bgp_static->valid != valid) - { - if (bgp_static->valid) - bgp_static_update (bgp, &rn->p, bgp_static, afi, safi); - else - bgp_static_withdraw (bgp, &rn->p, afi, safi); - } - else if (bgp_static->valid) - { - if (bgp_static->igpmetric != metric - || bgp_static->igpnexthop.s_addr != nexthop.s_addr - || bgp_static->rmap.name) - bgp_static_update (bgp, &rn->p, bgp_static, afi, safi); - } - } - } - return 0; -} - -/* Connect to zebra for nexthop lookup. */ -static int -zlookup_connect (struct thread *t) -{ - struct zclient *zlookup; - - zlookup = THREAD_ARG (t); - zlookup->t_connect = NULL; - - if (zlookup->sock != -1) - return 0; - - if (zclient_socket_connect (zlookup) < 0) - return -1; + addr = hash_lookup (bgp_address_hash, &tmp); + if (addr) + return 1; return 0; } -/* Check specified multiaccess next-hop. */ int -bgp_multiaccess_check_v4 (struct in_addr nexthop, char *peer) +bgp_multiaccess_check_v4 (struct in_addr nexthop, struct peer *peer) { struct bgp_node *rn1; struct bgp_node *rn2; - struct prefix p1; - struct prefix p2; - struct in_addr addr; + struct prefix p; int ret; - ret = inet_aton (peer, &addr); - if (! ret) - return 0; + p.family = AF_INET; + p.prefixlen = IPV4_MAX_BITLEN; + p.u.prefix4 = nexthop; - memset (&p1, 0, sizeof (struct prefix)); - p1.family = AF_INET; - p1.prefixlen = IPV4_MAX_BITLEN; - p1.u.prefix4 = nexthop; - memset (&p2, 0, sizeof (struct prefix)); - p2.family = AF_INET; - p2.prefixlen = IPV4_MAX_BITLEN; - p2.u.prefix4 = addr; - - /* If bgp scan is not enabled, return invalid. */ - if (zlookup->sock < 0) + rn1 = bgp_node_match (bgp_connected_table[AFI_IP], &p); + if (!rn1) return 0; - rn1 = bgp_node_match (bgp_connected_table[AFI_IP], &p1); - if (! rn1) - return 0; - bgp_unlock_node (rn1); - - rn2 = bgp_node_match (bgp_connected_table[AFI_IP], &p2); - if (! rn2) - return 0; - bgp_unlock_node (rn2); - - /* This is safe, even with above unlocks, since we are just - comparing pointers to the objects, not the objects themselves. */ - if (rn1 == rn2) - return 1; - - return 0; -} - -DEFUN (bgp_scan_time, - bgp_scan_time_cmd, - "bgp scan-time <5-60>", - "BGP specific commands\n" - "Configure background scanner interval\n" - "Scanner interval (seconds)\n") -{ - bgp_scan_interval = atoi (argv[0]); + p.family = AF_INET; + p.prefixlen = IPV4_MAX_BITLEN; + p.u.prefix4 = peer->su.sin.sin_addr; - if (bgp_scan_thread) + rn2 = bgp_node_match (bgp_connected_table[AFI_IP], &p); + if (!rn2) { - thread_cancel (bgp_scan_thread); - bgp_scan_thread = - thread_add_timer (master, bgp_scan_timer, NULL, bgp_scan_interval); + bgp_unlock_node(rn1); + return 0; } - return CMD_SUCCESS; -} - -DEFUN (no_bgp_scan_time, - no_bgp_scan_time_cmd, - "no bgp scan-time", - NO_STR - "BGP specific commands\n" - "Configure background scanner interval\n") -{ - bgp_scan_interval = BGP_SCAN_INTERVAL_DEFAULT; + ret = (rn1 == rn2) ? 1 : 0; - if (bgp_scan_thread) - { - thread_cancel (bgp_scan_thread); - bgp_scan_thread = - thread_add_timer (master, bgp_scan_timer, NULL, bgp_scan_interval); - } + bgp_unlock_node(rn1); + bgp_unlock_node(rn2); - return CMD_SUCCESS; + return (ret); } -ALIAS (no_bgp_scan_time, - no_bgp_scan_time_val_cmd, - "no bgp scan-time <5-60>", - NO_STR - "BGP specific commands\n" - "Configure background scanner interval\n" - "Scanner interval (seconds)\n") - static int -show_ip_bgp_scan_tables (struct vty *vty, const char detail) +show_ip_bgp_nexthop_table (struct vty *vty, int detail) { struct bgp_node *rn; struct bgp_nexthop_cache *bnc; char buf[INET6_ADDRSTRLEN]; - u_char i; - - if (bgp_scan_thread) - vty_out (vty, "BGP scan is running%s", VTY_NEWLINE); - else - vty_out (vty, "BGP scan is not running%s", VTY_NEWLINE); - vty_out (vty, "BGP scan interval is %d%s", bgp_scan_interval, VTY_NEWLINE); + struct nexthop *nexthop; + time_t tbuf; + afi_t afi; vty_out (vty, "Current BGP nexthop cache:%s", VTY_NEWLINE); - for (rn = bgp_table_top (bgp_nexthop_cache_table[AFI_IP]); rn; rn = bgp_route_next (rn)) - if ((bnc = rn->info) != NULL) - { - if (bnc->valid) - { - vty_out (vty, " %s valid [IGP metric %d]%s", - inet_ntop (AF_INET, &rn->p.u.prefix4, buf, INET6_ADDRSTRLEN), bnc->metric, VTY_NEWLINE); - if (detail) - for (i = 0; i < bnc->nexthop_num; i++) - switch (bnc->nexthop[i].type) - { - 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_IFINDEX: - vty_out (vty, " ifidx %u%s", bnc->nexthop[i].ifindex, VTY_NEWLINE); - break; - default: - vty_out (vty, " invalid nexthop type %u%s", bnc->nexthop[i].type, VTY_NEWLINE); - } - } - else - vty_out (vty, " %s invalid%s", - inet_ntop (AF_INET, &rn->p.u.prefix4, buf, INET6_ADDRSTRLEN), VTY_NEWLINE); - } - -#ifdef HAVE_IPV6 - { - for (rn = bgp_table_top (bgp_nexthop_cache_table[AFI_IP6]); - rn; - rn = bgp_route_next (rn)) - if ((bnc = rn->info) != NULL) + for (afi = AFI_IP ; afi < AFI_MAX ; afi++) + { + if (!bgp_nexthop_cache_table[afi]) + continue; + + for (rn = bgp_table_top (bgp_nexthop_cache_table[afi]); rn; rn = bgp_route_next (rn)) { - if (bnc->valid) - { - vty_out (vty, " %s valid [IGP metric %d]%s", - inet_ntop (AF_INET6, &rn->p.u.prefix6, buf, INET6_ADDRSTRLEN), - bnc->metric, VTY_NEWLINE); - if (detail) - for (i = 0; i < bnc->nexthop_num; i++) - switch (bnc->nexthop[i].type) + if ((bnc = rn->info) != NULL) + { + if (CHECK_FLAG(bnc->flags, BGP_NEXTHOP_VALID)) { - case NEXTHOP_TYPE_IPV6: - vty_out (vty, " gate %s%s", inet_ntop (AF_INET6, &bnc->nexthop[i].gate.ipv6, buf, INET6_ADDRSTRLEN), VTY_NEWLINE); - break; - case NEXTHOP_TYPE_IFINDEX: - vty_out (vty, " ifidx %u%s", bnc->nexthop[i].ifindex, VTY_NEWLINE); - break; - default: - vty_out (vty, " invalid nexthop type %u%s", bnc->nexthop[i].type, VTY_NEWLINE); + vty_out (vty, " %s valid [IGP metric %d], #paths %d%s", + inet_ntop (rn->p.family, &rn->p.u.prefix, buf, sizeof (buf)), + bnc->metric, bnc->path_count, VTY_NEWLINE); + if (detail) + for (nexthop = bnc->nexthop ; nexthop; nexthop = nexthop->next) + switch (nexthop->type) + { + case NEXTHOP_TYPE_IPV6: + vty_out (vty, " gate %s%s", + inet_ntop (AF_INET6, &nexthop->gate.ipv6, + buf, INET6_ADDRSTRLEN), VTY_NEWLINE); + break; + case NEXTHOP_TYPE_IPV6_IFINDEX: + vty_out(vty, " gate %s, if %s%s", + inet_ntop(AF_INET6, &nexthop->gate.ipv6, buf, + INET6_ADDRSTRLEN), + ifindex2ifname(nexthop->ifindex), + VTY_NEWLINE); + break; + case NEXTHOP_TYPE_IPV4: + vty_out (vty, " gate %s%s", + inet_ntop (AF_INET, &nexthop->gate.ipv4, buf, + INET6_ADDRSTRLEN), VTY_NEWLINE); + break; + case NEXTHOP_TYPE_IFINDEX: + vty_out (vty, " if %s%s", + ifindex2ifname(nexthop->ifindex), VTY_NEWLINE); + break; + case NEXTHOP_TYPE_IPV4_IFINDEX: + vty_out (vty, " gate %s, if %s%s", + inet_ntop(AF_INET, &nexthop->gate.ipv4, buf, + INET6_ADDRSTRLEN), + ifindex2ifname(nexthop->ifindex), VTY_NEWLINE); + break; + default: + vty_out (vty, " invalid nexthop type %u%s", + nexthop->type, VTY_NEWLINE); + } } - } - else - vty_out (vty, " %s invalid%s", - inet_ntop (AF_INET6, &rn->p.u.prefix6, buf, INET6_ADDRSTRLEN), - VTY_NEWLINE); + else + vty_out (vty, " %s invalid%s", + inet_ntop (AF_INET, &rn->p.u.prefix, buf, sizeof (buf)), VTY_NEWLINE); +#ifdef HAVE_CLOCK_MONOTONIC + tbuf = time(NULL) - (bgp_clock() - bnc->last_update); + vty_out (vty, " Last update: %s", ctime(&tbuf)); +#else + vty_out (vty, " Last update: %s", ctime(&bnc->uptime)); +#endif /* HAVE_CLOCK_MONOTONIC */ + vty_out(vty, "%s", VTY_NEWLINE); + } } - } -#endif /* HAVE_IPV6 */ - - vty_out (vty, "BGP connected route:%s", VTY_NEWLINE); - for (rn = bgp_table_top (bgp_connected_table[AFI_IP]); - rn; - rn = bgp_route_next (rn)) - if (rn->info != NULL) - vty_out (vty, " %s/%d%s", inet_ntoa (rn->p.u.prefix4), rn->p.prefixlen, - VTY_NEWLINE); - -#ifdef HAVE_IPV6 - { - for (rn = bgp_table_top (bgp_connected_table[AFI_IP6]); - rn; - rn = bgp_route_next (rn)) - if (rn->info != NULL) - vty_out (vty, " %s/%d%s", - inet_ntop (AF_INET6, &rn->p.u.prefix6, buf, INET6_ADDRSTRLEN), - rn->p.prefixlen, - VTY_NEWLINE); - } -#endif /* HAVE_IPV6 */ - + } return CMD_SUCCESS; } -DEFUN (show_ip_bgp_scan, - show_ip_bgp_scan_cmd, - "show ip bgp scan", +DEFUN (show_ip_bgp_nexthop, + show_ip_bgp_nexthop_cmd, + "show ip bgp nexthop", SHOW_STR IP_STR BGP_STR - "BGP scan status\n") + "BGP nexthop table\n") { - return show_ip_bgp_scan_tables (vty, 0); + return show_ip_bgp_nexthop_table (vty, 0); } -DEFUN (show_ip_bgp_scan_detail, - show_ip_bgp_scan_detail_cmd, - "show ip bgp scan detail", +DEFUN (show_ip_bgp_nexthop_detail, + show_ip_bgp_nexthop_detail_cmd, + "show ip bgp nexthop detail", SHOW_STR IP_STR BGP_STR - "BGP scan status\n" - "More detailed output\n") + "BGP nexthop table\n") { - return show_ip_bgp_scan_tables (vty, 1); -} - -int -bgp_config_write_scan_time (struct vty *vty) -{ - if (bgp_scan_interval != BGP_SCAN_INTERVAL_DEFAULT) - vty_out (vty, " bgp scan-time %d%s", bgp_scan_interval, VTY_NEWLINE); - return CMD_SUCCESS; + return show_ip_bgp_nexthop_table (vty, 1); } void bgp_scan_init (void) { - zlookup = zclient_new (); - zlookup->sock = -1; - zlookup->t_connect = thread_add_event (master, zlookup_connect, zlookup, 0); - - bgp_scan_interval = BGP_SCAN_INTERVAL_DEFAULT; - bgp_import_interval = BGP_IMPORT_INTERVAL_DEFAULT; - cache1_table[AFI_IP] = bgp_table_init (AFI_IP, SAFI_UNICAST); - cache2_table[AFI_IP] = bgp_table_init (AFI_IP, SAFI_UNICAST); bgp_nexthop_cache_table[AFI_IP] = cache1_table[AFI_IP]; bgp_connected_table[AFI_IP] = bgp_table_init (AFI_IP, SAFI_UNICAST); -#ifdef HAVE_IPV6 cache1_table[AFI_IP6] = bgp_table_init (AFI_IP6, SAFI_UNICAST); - cache2_table[AFI_IP6] = bgp_table_init (AFI_IP6, SAFI_UNICAST); bgp_nexthop_cache_table[AFI_IP6] = cache1_table[AFI_IP6]; bgp_connected_table[AFI_IP6] = bgp_table_init (AFI_IP6, SAFI_UNICAST); -#endif /* HAVE_IPV6 */ - - /* Make BGP scan thread. */ - bgp_scan_thread = thread_add_timer (master, bgp_scan_timer, - NULL, bgp_scan_interval); - /* Make BGP import there. */ - bgp_import_thread = thread_add_timer (master, bgp_import, NULL, 0); - - install_element (BGP_NODE, &bgp_scan_time_cmd); - install_element (BGP_NODE, &no_bgp_scan_time_cmd); - install_element (BGP_NODE, &no_bgp_scan_time_val_cmd); - install_element (VIEW_NODE, &show_ip_bgp_scan_cmd); - install_element (VIEW_NODE, &show_ip_bgp_scan_detail_cmd); - install_element (RESTRICTED_NODE, &show_ip_bgp_scan_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_scan_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_scan_detail_cmd); + + cache1_table[AFI_ETHER] = bgp_table_init (AFI_ETHER, SAFI_UNICAST); + bgp_nexthop_cache_table[AFI_ETHER] = cache1_table[AFI_ETHER]; + bgp_connected_table[AFI_ETHER] = bgp_table_init (AFI_ETHER, SAFI_UNICAST); } void -bgp_scan_finish (void) +bgp_scan_vty_init() { - /* Only the current one needs to be reset. */ - bgp_nexthop_cache_reset (bgp_nexthop_cache_table[AFI_IP]); + install_element (VIEW_NODE, &show_ip_bgp_nexthop_cmd); + install_element (VIEW_NODE, &show_ip_bgp_nexthop_detail_cmd); +} - bgp_table_unlock (cache1_table[AFI_IP]); +void +bgp_scan_finish (void) +{ + if (cache1_table[AFI_IP]) + bgp_table_unlock (cache1_table[AFI_IP]); cache1_table[AFI_IP] = NULL; - bgp_table_unlock (cache2_table[AFI_IP]); - cache2_table[AFI_IP] = NULL; - - bgp_table_unlock (bgp_connected_table[AFI_IP]); + if (bgp_connected_table[AFI_IP]) + bgp_table_unlock (bgp_connected_table[AFI_IP]); bgp_connected_table[AFI_IP] = NULL; -#ifdef HAVE_IPV6 - /* Only the current one needs to be reset. */ - bgp_nexthop_cache_reset (bgp_nexthop_cache_table[AFI_IP6]); - - bgp_table_unlock (cache1_table[AFI_IP6]); + if (cache1_table[AFI_IP6]) + bgp_table_unlock (cache1_table[AFI_IP6]); cache1_table[AFI_IP6] = NULL; - bgp_table_unlock (cache2_table[AFI_IP6]); - cache2_table[AFI_IP6] = NULL; - - bgp_table_unlock (bgp_connected_table[AFI_IP6]); + if (bgp_connected_table[AFI_IP6]) + bgp_table_unlock (bgp_connected_table[AFI_IP6]); bgp_connected_table[AFI_IP6] = NULL; -#endif /* HAVE_IPV6 */ + + if (cache1_table[AFI_ETHER]) + bgp_table_unlock (cache1_table[AFI_ETHER]); + cache1_table[AFI_ETHER] = NULL; + + if (bgp_connected_table[AFI_ETHER]) + bgp_table_unlock (bgp_connected_table[AFI_ETHER]); + bgp_connected_table[AFI_ETHER] = NULL; + +} + +void +bgp_scan_destroy (void) +{ + bgp_scan_finish(); } diff --git a/bgpd/bgp_nexthop.h b/bgpd/bgp_nexthop.h index 874f3bba0..fe4f5ad4b 100644 --- a/bgpd/bgp_nexthop.h +++ b/bgpd/bgp_nexthop.h @@ -22,39 +22,63 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #define _QUAGGA_BGP_NEXTHOP_H #include "if.h" +#include "queue.h" +#include "prefix.h" -#define BGP_SCAN_INTERVAL_DEFAULT 60 -#define BGP_IMPORT_INTERVAL_DEFAULT 15 +#define NEXTHOP_FAMILY(nexthop_len) ( \ + ((nexthop_len) == 4 || \ + (nexthop_len) == 12 ? AF_INET : \ + ((nexthop_len) == 16 || \ + (nexthop_len) == 24 || \ + (nexthop_len) == 48 ? AF_INET6 : \ + AF_UNSPEC)) \ +) /* BGP nexthop cache value structure. */ struct bgp_nexthop_cache { - /* This nexthop exists in IGP. */ - u_char valid; - - /* Nexthop is changed. */ - u_char changed; - - /* Nexthop is changed. */ - u_char metricchanged; - /* IGP route's metric. */ u_int32_t metric; /* Nexthop number and nexthop linked list.*/ u_char nexthop_num; struct nexthop *nexthop; + time_t last_update; + u_int16_t flags; + +#define BGP_NEXTHOP_VALID (1 << 0) +#define BGP_NEXTHOP_REGISTERED (1 << 1) +#define BGP_NEXTHOP_CONNECTED (1 << 2) +#define BGP_NEXTHOP_PEER_NOTIFIED (1 << 3) + + u_int16_t change_flags; + +#define BGP_NEXTHOP_CHANGED (1 << 0) +#define BGP_NEXTHOP_METRIC_CHANGED (1 << 1) +#define BGP_NEXTHOP_CONNECTED_CHANGED (1 << 2) + + struct bgp_node *node; + void *nht_info; /* In BGP, peer session */ + LIST_HEAD(path_list, bgp_info) paths; + unsigned int path_count; }; -extern void bgp_scan_init (void); -extern void bgp_scan_finish (void); extern int bgp_nexthop_lookup (afi_t, struct peer *peer, struct bgp_info *, int *, int *); extern void bgp_connected_add (struct connected *c); extern void bgp_connected_delete (struct connected *c); -extern int bgp_multiaccess_check_v4 (struct in_addr, char *); +extern int bgp_multiaccess_check_v4 (struct in_addr, struct peer *); extern int bgp_config_write_scan_time (struct vty *); extern int bgp_nexthop_onlink (afi_t, struct attr *); -extern int bgp_nexthop_self (afi_t, struct attr *); +extern int bgp_nexthop_self (struct attr *); +extern void bgp_address_init (void); +extern void bgp_address_destroy (void); +extern void bgp_scan_destroy (void); +extern struct bgp_nexthop_cache *bnc_new(void); +extern void bnc_free(struct bgp_nexthop_cache *bnc); +extern void bnc_nexthop_free(struct bgp_nexthop_cache *bnc); +extern char *bnc_str(struct bgp_nexthop_cache *bnc, char *buf, int size); +extern void bgp_scan_init (void); +extern void bgp_scan_vty_init (void); #endif /* _QUAGGA_BGP_NEXTHOP_H */ diff --git a/bgpd/bgp_nht.c b/bgpd/bgp_nht.c new file mode 100644 index 000000000..1158ab152 --- /dev/null +++ b/bgpd/bgp_nht.c @@ -0,0 +1,612 @@ +/* BGP Nexthop tracking + * Copyright (C) 2013 Cumulus Networks, Inc. + * + * 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 "command.h" +#include "thread.h" +#include "prefix.h" +#include "zclient.h" +#include "stream.h" +#include "network.h" +#include "log.h" +#include "memory.h" +#include "nexthop.h" +#include "filter.h" + +#include "bgpd/bgpd.h" +#include "bgpd/bgp_table.h" +#include "bgpd/bgp_route.h" +#include "bgpd/bgp_attr.h" +#include "bgpd/bgp_nexthop.h" +#include "bgpd/bgp_debug.h" +#include "bgpd/bgp_nht.h" +#include "bgpd/bgp_fsm.h" +#include "bgpd/bgp_zebra.h" + +extern struct zclient *zclient; +extern struct bgp_table *bgp_nexthop_cache_table[AFI_MAX]; + +static void register_nexthop(struct bgp_nexthop_cache *bnc); +static void unregister_nexthop (struct bgp_nexthop_cache *bnc); +static void evaluate_paths(struct bgp_nexthop_cache *bnc); +static int make_prefix(int afi, struct bgp_info *ri, struct prefix *p); +static void path_nh_map(struct bgp_info *path, struct bgp_nexthop_cache *bnc, + int keep); + +int +bgp_nexthop_check (struct bgp_info *path, int connected) +{ + struct bgp_nexthop_cache *bnc = path->nexthop; + + if (!bnc) + return 0; + + if (BGP_DEBUG(nht, NHT)) + { + char buf[INET6_ADDRSTRLEN]; + zlog_debug("%s: NHT checking %s", + __FUNCTION__, + bnc_str (bnc, buf, INET6_ADDRSTRLEN)); + } + + if (connected && !(CHECK_FLAG(bnc->flags, BGP_NEXTHOP_CONNECTED))) + return 0; + + return (bgp_zebra_num_connects() == 0 || + CHECK_FLAG(bnc->flags, BGP_NEXTHOP_VALID)); +} + +/* Helper to get the rn for the appropriate nexthop for path or peer. + * returns the locked rn - caller must bump down the refcnt. + * + * may return NULL in error cases. + */ +static +struct bgp_node * +bgp_get_nexthop_rn (struct bgp_info *path, struct peer *peer) +{ + struct prefix p; + afi_t afi; + + assert (path || peer); + + if (!(path || peer)) + return NULL; + + if (path) + { + afi = family2afi (path->net->p.family); + if (make_prefix(afi, path, &p) < 0) + return NULL; + } + else + { + afi = family2afi(peer->su.sa.sa_family); + if (afi == AFI_IP) + { + p.family = AF_INET; + p.prefixlen = IPV4_MAX_BITLEN; + p.u.prefix4 = peer->su.sin.sin_addr; + } + else if (afi == AFI_IP6) + { + p.family = AF_INET6; + p.prefixlen = IPV6_MAX_BITLEN; + p.u.prefix6 = peer->su.sin6.sin6_addr; + } + else + return NULL; + } + + return bgp_node_get (bgp_nexthop_cache_table[afi], &p); +} + +static +struct bgp_nexthop_cache * +bgp_find_nexthop (struct bgp_info *path, struct peer *peer) +{ + struct bgp_nexthop_cache *bnc = NULL; + struct bgp_node *rn = bgp_get_nexthop_rn (path, peer); + + if (!rn) + return NULL; + + bnc = rn->info; + bgp_unlock_node (rn); + + return bnc; +} + +static void +bgp_unlink_nexthop_check (struct bgp_nexthop_cache *bnc) +{ + if (LIST_EMPTY(&(bnc->paths)) && !bnc->nht_info) + { + if (BGP_DEBUG(nht, NHT)) + { + char buf[INET6_ADDRSTRLEN]; + zlog_debug("bgp_unlink_nexthop: freeing bnc %s", + bnc_str (bnc, buf, INET6_ADDRSTRLEN)); + } + unregister_nexthop(bnc); + bnc->node->info = NULL; + bgp_unlock_node (bnc->node); + bnc->node = NULL; + bnc_free (bnc); + } +} + +void +bgp_unlink_nexthop (struct bgp_info *path) +{ + struct bgp_nexthop_cache *bnc = path->nexthop; + + if (!bnc) + return; + + if (BGP_DEBUG(nht, NHT)) + { + char buf[INET6_ADDRSTRLEN]; + zlog_debug("%s: NHT unlinking %s", + __FUNCTION__, bnc_str (bnc, buf, INET6_ADDRSTRLEN)); + } + + path_nh_map(path, NULL, 0); + + bgp_unlink_nexthop_check (bnc); +} + +void +bgp_unlink_nexthop_by_peer (struct peer *peer) +{ + struct bgp_nexthop_cache *bnc = bgp_find_nexthop (NULL, peer); + + if (!bnc) + return; + + if (BGP_DEBUG(nht, NHT)) + zlog_debug("%s: NHT unlinking %s", + __FUNCTION__, peer->host); + + bnc->nht_info = NULL; + + bgp_unlink_nexthop_check (bnc); +} + +int +bgp_ensure_nexthop (struct bgp_info *ri, struct peer *peer, + int connected) +{ + struct bgp_node *rn; + struct bgp_nexthop_cache *bnc; + + rn = bgp_get_nexthop_rn (ri, peer); + + if (!rn) + { + zlog_debug("%s: NHT could not ensure, failed to get rn!", + __FUNCTION__); + return 0; + } + + if (!rn->info) + { + bnc = bnc_new(); + rn->info = bnc; + bnc->node = rn; + bgp_lock_node(rn); + if (connected) + SET_FLAG(bnc->flags, BGP_NEXTHOP_CONNECTED); + } + + bnc = rn->info; + bgp_unlock_node (rn); + + if (!CHECK_FLAG(bnc->flags, BGP_NEXTHOP_REGISTERED)) + register_nexthop(bnc); + + if (ri) + { + path_nh_map(ri, bnc, 1); /* updates NHT ri list reference */ + + if (CHECK_FLAG(bnc->flags, BGP_NEXTHOP_VALID) && bnc->metric) + (bgp_info_extra_get(ri))->igpmetric = bnc->metric; + else if (ri->extra) + ri->extra->igpmetric = 0; + } + else if (peer) + bnc->nht_info = (void *)peer; /* NHT peer reference */ + + if (BGP_DEBUG(nht, NHT)) + { + char buf[INET6_ADDRSTRLEN]; + zlog_debug("%s: NHT ensured %s", + __FUNCTION__, bnc_str (bnc, buf, INET6_ADDRSTRLEN)); + } + + return (bgp_zebra_num_connects() == 0 || + CHECK_FLAG(bnc->flags, BGP_NEXTHOP_VALID)); +} + +void +bgp_parse_nexthop_update (void) +{ + struct stream *s; + struct bgp_node *rn; + struct bgp_nexthop_cache *bnc; + struct nexthop *nexthop; + struct nexthop *oldnh; + struct nexthop *nhlist_head = NULL; + struct nexthop *nhlist_tail = NULL; + uint32_t metric; + u_char nexthop_num; + struct prefix p; + int i; + + s = zclient->ibuf; + + memset(&p, 0, sizeof(struct prefix)); + p.family = stream_getw(s); + p.prefixlen = stream_getc(s); + switch (p.family) + { + case AF_INET: + p.u.prefix4.s_addr = stream_get_ipv4 (s); + break; + case AF_INET6: + stream_get(&p.u.prefix6, s, 16); + break; + default: + break; + } + + rn = bgp_node_lookup(bgp_nexthop_cache_table[family2afi(p.family)], &p); + if (!rn || !rn->info) + { + if (BGP_DEBUG(nht, NHT)) + { + char buf[INET6_ADDRSTRLEN]; + prefix2str(&p, buf, INET6_ADDRSTRLEN); + zlog_debug("parse nexthop update(%s): rn not found", buf); + } + if (rn) + bgp_unlock_node (rn); + return; + } + + bnc = rn->info; + bgp_unlock_node (rn); + bnc->last_update = bgp_clock(); + bnc->change_flags = 0; + metric = stream_getl (s); + nexthop_num = stream_getc (s); + + /* debug print the input */ + if (BGP_DEBUG(nht, NHT)) + { + char buf[INET6_ADDRSTRLEN]; + prefix2str(&p, buf, INET6_ADDRSTRLEN); + zlog_debug("parse nexthop update(%s): metric=%d, #nexthop=%d", buf, + metric, nexthop_num); + } + + if (metric != bnc->metric) + bnc->change_flags |= BGP_NEXTHOP_METRIC_CHANGED; + + if(nexthop_num != bnc->nexthop_num) + bnc->change_flags |= BGP_NEXTHOP_CHANGED; + + if (nexthop_num) + { + bnc->flags |= BGP_NEXTHOP_VALID; + bnc->metric = metric; + bnc->nexthop_num = nexthop_num; + + for (i = 0; i < nexthop_num; i++) + { + nexthop = nexthop_new(); + nexthop->type = stream_getc (s); + switch (nexthop->type) + { + case ZEBRA_NEXTHOP_IPV4: + nexthop->gate.ipv4.s_addr = stream_get_ipv4 (s); + break; + case ZEBRA_NEXTHOP_IFINDEX: + case ZEBRA_NEXTHOP_IFNAME: + nexthop->ifindex = stream_getl (s); + break; + case ZEBRA_NEXTHOP_IPV4_IFINDEX: + case ZEBRA_NEXTHOP_IPV4_IFNAME: + nexthop->gate.ipv4.s_addr = stream_get_ipv4 (s); + nexthop->ifindex = stream_getl (s); + break; +#ifdef HAVE_IPV6 + case ZEBRA_NEXTHOP_IPV6: + stream_get (&nexthop->gate.ipv6, s, 16); + break; + case ZEBRA_NEXTHOP_IPV6_IFINDEX: + case ZEBRA_NEXTHOP_IPV6_IFNAME: + stream_get (&nexthop->gate.ipv6, s, 16); + nexthop->ifindex = stream_getl (s); + break; +#endif + default: + /* do nothing */ + break; + } + + if (nhlist_tail) + { + nhlist_tail->next = nexthop; + nhlist_tail = nexthop; + } + else + { + nhlist_tail = nexthop; + nhlist_head = nexthop; + } + + /* No need to evaluate the nexthop if we have already determined + * that there has been a change. + */ + if (bnc->change_flags & BGP_NEXTHOP_CHANGED) + continue; + + for (oldnh = bnc->nexthop; oldnh; oldnh = oldnh->next) + if (nexthop_same_no_recurse(oldnh, nexthop)) + break; + + if (!oldnh) + bnc->change_flags |= BGP_NEXTHOP_CHANGED; + } + bnc_nexthop_free(bnc); + bnc->nexthop = nhlist_head; + } + else + { + bnc->flags &= ~BGP_NEXTHOP_VALID; + UNSET_FLAG(bnc->flags, BGP_NEXTHOP_PEER_NOTIFIED); + bnc_nexthop_free(bnc); + bnc->nexthop = NULL; + } + + evaluate_paths(bnc); +} + +/** + * make_prefix - make a prefix structure from the path (essentially + * path's node. + */ +static int +make_prefix (int afi, struct bgp_info *ri, struct prefix *p) +{ + memset (p, 0, sizeof (struct prefix)); + switch (afi) + { + case AFI_IP: + p->family = AF_INET; + p->prefixlen = IPV4_MAX_BITLEN; + p->u.prefix4 = ri->attr->nexthop; + break; +#ifdef HAVE_IPV6 + case AFI_IP6: + if (ri->attr->extra->mp_nexthop_len != 16 + || IN6_IS_ADDR_LINKLOCAL (&ri->attr->extra->mp_nexthop_global)) + return -1; + + p->family = AF_INET6; + p->prefixlen = IPV6_MAX_BITLEN; + p->u.prefix6 = ri->attr->extra->mp_nexthop_global; + break; +#endif + default: + break; + } + return 0; +} + +/** + * sendmsg_nexthop -- Format and send a nexthop register/Unregister + * command to Zebra. + * ARGUMENTS: + * struct bgp_nexthop_cache *bnc -- the nexthop structure. + * int command -- either ZEBRA_NEXTHOP_REGISTER or ZEBRA_NEXTHOP_UNREGISTER + * RETURNS: + * void. + */ +static void +sendmsg_nexthop (struct bgp_nexthop_cache *bnc, int command) +{ + struct stream *s; + struct prefix *p; + int ret; + + /* Check socket. */ + if (!zclient || zclient->sock < 0) + { + zlog_debug("%s: Can't send NH register, Zebra client not established", + __FUNCTION__); + return; + } + + p = &(bnc->node->p); + s = zclient->obuf; + stream_reset (s); + zclient_create_header (s, command, VRF_DEFAULT); + if (CHECK_FLAG(bnc->flags, BGP_NEXTHOP_CONNECTED)) + stream_putc(s, 1); + else + stream_putc(s, 0); + + stream_putw(s, PREFIX_FAMILY(p)); + stream_putc(s, p->prefixlen); + switch (PREFIX_FAMILY(p)) + { + case AF_INET: + stream_put_in_addr (s, &p->u.prefix4); + break; + case AF_INET6: + stream_put(s, &(p->u.prefix6), 16); + break; + default: + break; + } + stream_putw_at (s, 0, stream_get_endp (s)); + + ret = zclient_send_message(zclient); + /* TBD: handle the failure */ + if (ret < 0) + zlog_warn("sendmsg_nexthop: zclient_send_message() failed"); + + if (command == ZEBRA_NEXTHOP_REGISTER) + SET_FLAG(bnc->flags, BGP_NEXTHOP_REGISTERED); + else if (command == ZEBRA_NEXTHOP_UNREGISTER) + UNSET_FLAG(bnc->flags, BGP_NEXTHOP_REGISTERED); + return; +} + +/** + * register_nexthop - register a nexthop with Zebra for notification + * when the route to the nexthop changes. + * ARGUMENTS: + * struct bgp_nexthop_cache *bnc -- the nexthop structure. + * RETURNS: + * void. + */ +static void +register_nexthop (struct bgp_nexthop_cache *bnc) +{ + /* Check if we have already registered */ + if (bnc->flags & BGP_NEXTHOP_REGISTERED) + return; + sendmsg_nexthop(bnc, ZEBRA_NEXTHOP_REGISTER); +} + +/** + * unregister_nexthop -- Unregister the nexthop from Zebra. + * ARGUMENTS: + * struct bgp_nexthop_cache *bnc -- the nexthop structure. + * RETURNS: + * void. + */ +static void +unregister_nexthop (struct bgp_nexthop_cache *bnc) +{ + /* Check if we have already registered */ + if (!CHECK_FLAG(bnc->flags, BGP_NEXTHOP_REGISTERED)) + return; + + sendmsg_nexthop(bnc, ZEBRA_NEXTHOP_UNREGISTER); +} + +/** + * evaluate_paths - Evaluate the paths/nets associated with a nexthop. + * ARGUMENTS: + * struct bgp_nexthop_cache *bnc -- the nexthop structure. + * RETURNS: + * void. + */ +static void +evaluate_paths (struct bgp_nexthop_cache *bnc) +{ + struct bgp_node *rn; + struct bgp_info *path; + struct bgp *bgp = bgp_get_default(); + int afi; + struct peer *peer = (struct peer *)bnc->nht_info; + + LIST_FOREACH(path, &(bnc->paths), nh_thread) + { + if (!(path->type == ZEBRA_ROUTE_BGP && + path->sub_type == BGP_ROUTE_NORMAL)) + continue; + + rn = path->net; + afi = family2afi(rn->p.family); + + /* Path becomes valid/invalid depending on whether the nexthop + * reachable/unreachable. + */ + if ((CHECK_FLAG(path->flags, BGP_INFO_VALID) ? 1 : 0) != + (CHECK_FLAG(bnc->flags, BGP_NEXTHOP_VALID) ? 1 : 0)) + { + if (CHECK_FLAG (path->flags, BGP_INFO_VALID)) + { + bgp_aggregate_decrement (bgp, &rn->p, path, + afi, SAFI_UNICAST); + bgp_info_unset_flag (rn, path, BGP_INFO_VALID); + } + else + { + bgp_info_set_flag (rn, path, BGP_INFO_VALID); + bgp_aggregate_increment (bgp, &rn->p, path, + afi, SAFI_UNICAST); + } + } + + /* Copy the metric to the path. Will be used for bestpath computation */ + if (CHECK_FLAG(bnc->flags, BGP_NEXTHOP_VALID) && bnc->metric) + (bgp_info_extra_get(path))->igpmetric = bnc->metric; + else if (path->extra) + path->extra->igpmetric = 0; + + if (CHECK_FLAG(bnc->flags, BGP_NEXTHOP_METRIC_CHANGED) || + CHECK_FLAG(bnc->flags, BGP_NEXTHOP_CHANGED)) + SET_FLAG(path->flags, BGP_INFO_IGP_CHANGED); + + bgp_process(bgp, rn, afi, SAFI_UNICAST); + } + + if (peer && !CHECK_FLAG(bnc->flags, BGP_NEXTHOP_PEER_NOTIFIED)) + { + if (BGP_DEBUG(nht, NHT)) + zlog_debug("%s: Updating peer (%s) status with NHT", __FUNCTION__, peer->host); + SET_FLAG(bnc->flags, BGP_NEXTHOP_PEER_NOTIFIED); + } + + RESET_FLAG(bnc->change_flags); +} + +/** + * path_nh_map - make or break path-to-nexthop association. + * ARGUMENTS: + * path - pointer to the path structure + * bnc - pointer to the nexthop structure + * make - if set, make the association. if unset, just break the existing + * association. + */ +static void +path_nh_map (struct bgp_info *path, struct bgp_nexthop_cache *bnc, int make) +{ + if (path->nexthop) + { + LIST_REMOVE(path, nh_thread); + path->nexthop->path_count--; + path->nexthop = NULL; + } + if (make) + { + LIST_INSERT_HEAD(&(bnc->paths), path, nh_thread); + path->nexthop = bnc; + path->nexthop->path_count++; + } +} diff --git a/bgpd/bgp_nht.h b/bgpd/bgp_nht.h new file mode 100644 index 000000000..dd6300eb6 --- /dev/null +++ b/bgpd/bgp_nht.h @@ -0,0 +1,66 @@ +/* BGP Nexthop tracking + * Copyright (C) 2013 Cumulus Networks, Inc. + * + * 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 _BGP_NHT_H +#define _BGP_NHT_H + +/** + * bgp_parse_nexthop_update() - parse a nexthop update message from Zebra. + */ +void bgp_parse_nexthop_update (void); + +/** + * bgp_nexthop_check() - check if the bnc object is valid. + * ARGUMENTS: + * p - path for which the nexthop object is being looked up + * connected - True if NH MUST be a connected route + */ +int bgp_nexthop_check (struct bgp_info *, int connected); + +/** + * bgp_ensure_nexthop() - Ensure a bgp_nexthop_cache object exists for + * the given prefix or peer. If an existing one is not found, + * create a new object and register with ZEBRA for nexthop + * notification. + * ARGUMENTS: + * afi: AFI_IP or AF_IP6 + * struct bgp_info *: path for which the nexthop object is + * being looked up + * OR + * struct peer The BGP peer associated with this NHT + * connected - True if NH MUST be a connected route + */ +int bgp_ensure_nexthop (struct bgp_info *, struct peer *, int connected); + +/** + * bgp_unlink_nexthop() - Unlink the nexthop object from the path structure. + * ARGUMENTS: + * struct bgp_info *: path structure. + */ +void bgp_unlink_nexthop (struct bgp_info *); + +/** + * bgp_unlink_nexthop() - Unlink the nexthop object for the given peer. + */ +extern void bgp_unlink_nexthop(struct bgp_info *p); +void bgp_unlink_nexthop_by_peer (struct peer *); + +#endif /* _BGP_NHT_H */ diff --git a/bgpd/bgp_open.c b/bgpd/bgp_open.c index b5b50bb5f..280042306 100644 --- a/bgpd/bgp_open.c +++ b/bgpd/bgp_open.c @@ -27,6 +27,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "log.h" #include "command.h" #include "memory.h" +#include "filter.h" #include "bgpd/bgpd.h" #include "bgpd/bgp_attr.h" @@ -96,6 +97,9 @@ bgp_capability_vty_out (struct vty *vty, struct peer *peer) case SAFI_MPLS_LABELED_VPN: vty_out (vty, "SAFI MPLS-labeled VPN"); break; + case SAFI_ENCAP: + vty_out (vty, "SAFI ENCAP"); + break; default: vty_out (vty, "SAFI Unknown %d ", mpc.safi); break; @@ -126,23 +130,26 @@ bgp_afi_safi_valid_indices (afi_t afi, safi_t *safi) { switch (afi) { - case AFI_IP: -#ifdef HAVE_IPV6 - case AFI_IP6: -#endif - switch (*safi) - { - /* BGP MPLS-labeled VPN SAFI isn't contigious with others, remap */ - case SAFI_MPLS_LABELED_VPN: - *safi = SAFI_MPLS_VPN; - case SAFI_UNICAST: - case SAFI_MULTICAST: - case SAFI_MPLS_VPN: - return 1; - } + case AFI_IP: + case AFI_IP6: + switch (*safi) + { + /* BGP MPLS-labeled VPN SAFI isn't contigious with others, remap */ + case SAFI_MPLS_LABELED_VPN: + *safi = SAFI_MPLS_VPN; + case SAFI_UNICAST: + case SAFI_MULTICAST: + case SAFI_MPLS_VPN: + case SAFI_ENCAP: + return 1; + } + case AFI_ETHER: + default: + break; } + zlog_debug ("unknown afi/safi (%u/%u)", afi, *safi); - + return 0; } @@ -187,8 +194,7 @@ static const struct message orf_type_str[] = { ORF_TYPE_PREFIX, "Prefixlist" }, { ORF_TYPE_PREFIX_OLD, "Prefixlist (old)" }, }; -static const int orf_type_str_max - = sizeof(orf_type_str)/sizeof(orf_type_str[0]); +static const int orf_type_str_max = array_size(orf_type_str); static const struct message orf_mode_str[] = { @@ -196,8 +202,7 @@ static const struct message orf_mode_str[] = { ORF_MODE_SEND, "Send" }, { ORF_MODE_BOTH, "Both" }, }; -static const int orf_mode_str_max - = sizeof(orf_mode_str)/sizeof(orf_mode_str[0]); +static const int orf_mode_str_max = array_size(orf_mode_str); static int bgp_capability_orf_entry (struct peer *peer, struct capability_header *hdr) @@ -232,12 +237,12 @@ 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 (CAPABILITY_CODE_ORF_LEN + (entry.num * 2) > hdr->length) { zlog_info ("%s ORF Capability entry length error," " Cap length %u, num %u", peer->host, hdr->length, entry.num); - bgp_notify_send (peer, BGP_NOTIFY_CEASE, 0); + bgp_notify_send (peer, BGP_NOTIFY_OPEN_ERR, BGP_NOTIFY_OPEN_UNSPECIFIC); return -1; } @@ -335,40 +340,18 @@ bgp_capability_orf_entry (struct peer *peer, struct capability_header *hdr) return 0; } -static int -bgp_capability_orf (struct peer *peer, struct capability_header *hdr) -{ - struct stream *s = BGP_INPUT (peer); - size_t end = stream_get_getp (s) + hdr->length; - - assert (stream_get_getp(s) + sizeof(struct capability_orf_entry) <= end); - - /* We must have at least one ORF entry, as the caller has already done - * minimum length validation for the capability code - for ORF there must - * at least one ORF entry (header and unknown number of pairs of bytes). - */ - do - { - if (bgp_capability_orf_entry (peer, hdr) == -1) - return -1; - } - while (stream_get_getp(s) + sizeof(struct capability_orf_entry) < end); - - return 0; -} - static int 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; @@ -376,7 +359,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); } @@ -449,26 +434,48 @@ static const struct message capcode_str[] = { CAPABILITY_CODE_REFRESH_OLD, "Route Refresh (Old)" }, { CAPABILITY_CODE_ORF_OLD, "ORF (Old)" }, }; -static const int capcode_str_max = sizeof(capcode_str)/sizeof(capcode_str[0]); +static const int capcode_str_max = array_size(capcode_str); /* Minimum sizes for length field of each cap (so not inc. the header) */ static const size_t cap_minsizes[] = { - [CAPABILITY_CODE_MP] = sizeof (struct capability_mp_data), + [CAPABILITY_CODE_MP] = CAPABILITY_CODE_MP_LEN, [CAPABILITY_CODE_REFRESH] = CAPABILITY_CODE_REFRESH_LEN, - [CAPABILITY_CODE_ORF] = sizeof (struct capability_orf_entry), - [CAPABILITY_CODE_RESTART] = sizeof (struct capability_gr), + [CAPABILITY_CODE_ORF] = CAPABILITY_CODE_ORF_LEN, + [CAPABILITY_CODE_RESTART] = CAPABILITY_CODE_RESTART_LEN, [CAPABILITY_CODE_AS4] = CAPABILITY_CODE_AS4_LEN, [CAPABILITY_CODE_DYNAMIC] = CAPABILITY_CODE_DYNAMIC_LEN, [CAPABILITY_CODE_REFRESH_OLD] = CAPABILITY_CODE_REFRESH_LEN, - [CAPABILITY_CODE_ORF_OLD] = sizeof (struct capability_orf_entry), + [CAPABILITY_CODE_ORF_OLD] = CAPABILITY_CODE_ORF_LEN, +}; + +/* value the capability must be a multiple of. + * 0-data capabilities won't be checked against this. + * Other capabilities whose data doesn't fall on convenient boundaries for this + * table should be set to 1. + */ +static const size_t cap_modsizes[] = +{ + [CAPABILITY_CODE_MP] = 4, + [CAPABILITY_CODE_REFRESH] = 1, + [CAPABILITY_CODE_ORF] = 1, + [CAPABILITY_CODE_RESTART] = 1, + [CAPABILITY_CODE_AS4] = 4, + [CAPABILITY_CODE_DYNAMIC] = 1, + [CAPABILITY_CODE_REFRESH_OLD] = 1, + [CAPABILITY_CODE_ORF_OLD] = 1, }; -/* Parse given capability. +/** + * Parse given capability. * XXX: This is reading into a stream, but not using stream API + * + * @param[out] mp_capability Set to 1 on return iff one or more Multiprotocol + * capabilities were encountered. */ static int -bgp_capability_parse (struct peer *peer, size_t length, u_char **error) +bgp_capability_parse (struct peer *peer, size_t length, int *mp_capability, + u_char **error) { int ret; struct stream *s = BGP_INPUT (peer); @@ -486,7 +493,7 @@ bgp_capability_parse (struct peer *peer, size_t length, u_char **error) if (stream_get_getp(s) + 2 > end) { zlog_info ("%s Capability length error (< header)", peer->host); - bgp_notify_send (peer, BGP_NOTIFY_CEASE, 0); + bgp_notify_send (peer, BGP_NOTIFY_OPEN_ERR, BGP_NOTIFY_OPEN_UNSPECIFIC); return -1; } @@ -498,7 +505,7 @@ bgp_capability_parse (struct peer *peer, size_t length, u_char **error) if (start + caphdr.length > end) { zlog_info ("%s Capability length error (< length)", peer->host); - bgp_notify_send (peer, BGP_NOTIFY_CEASE, 0); + bgp_notify_send (peer, BGP_NOTIFY_OPEN_ERR, BGP_NOTIFY_OPEN_UNSPECIFIC); return -1; } @@ -528,7 +535,21 @@ bgp_capability_parse (struct peer *peer, size_t length, u_char **error) LOOKUP (capcode_str, caphdr.code), caphdr.length, (unsigned) cap_minsizes[caphdr.code]); - bgp_notify_send (peer, BGP_NOTIFY_CEASE, 0); + bgp_notify_send (peer, BGP_NOTIFY_OPEN_ERR, + BGP_NOTIFY_OPEN_UNSPECIFIC); + return -1; + } + if (caphdr.length + && caphdr.length % cap_modsizes[caphdr.code] != 0) + { + zlog_info ("%s %s Capability length error: got %u," + " expected a multiple of %u", + peer->host, + LOOKUP (capcode_str, caphdr.code), + caphdr.length, + (unsigned) cap_modsizes[caphdr.code]); + bgp_notify_send (peer, BGP_NOTIFY_OPEN_ERR, + BGP_NOTIFY_OPEN_UNSPECIFIC); return -1; } /* we deliberately ignore unknown codes, see below */ @@ -540,6 +561,8 @@ bgp_capability_parse (struct peer *peer, size_t length, u_char **error) { case CAPABILITY_CODE_MP: { + *mp_capability = 1; + /* Ignore capability when override-capability is set. */ if (! CHECK_FLAG (peer->flags, PEER_FLAG_OVERRIDE_CAPABILITY)) { @@ -568,7 +591,7 @@ bgp_capability_parse (struct peer *peer, size_t length, u_char **error) break; case CAPABILITY_CODE_ORF: case CAPABILITY_CODE_ORF_OLD: - if (bgp_capability_orf (peer, &caphdr)) + if (bgp_capability_orf_entry (peer, &caphdr)) return -1; break; case CAPABILITY_CODE_RESTART: @@ -712,9 +735,13 @@ peek_for_as4_capability (struct peer *peer, u_char length) return as4; } -/* Parse open option */ +/** + * Parse open option. + * + * @param[out] mp_capability @see bgp_capability_parse() for semantics. + */ int -bgp_open_option_parse (struct peer *peer, u_char length, int *capability) +bgp_open_option_parse (struct peer *peer, u_char length, int *mp_capability) { int ret; u_char *error; @@ -738,7 +765,8 @@ bgp_open_option_parse (struct peer *peer, u_char length, int *capability) if (STREAM_READABLE(s) < 2) { zlog_info ("%s Option length error", peer->host); - bgp_notify_send (peer, BGP_NOTIFY_CEASE, 0); + bgp_notify_send (peer, BGP_NOTIFY_OPEN_ERR, + BGP_NOTIFY_OPEN_UNSPECIFIC); return -1; } @@ -750,7 +778,8 @@ bgp_open_option_parse (struct peer *peer, u_char length, int *capability) if (STREAM_READABLE (s) < opt_length) { zlog_info ("%s Option length error", peer->host); - bgp_notify_send (peer, BGP_NOTIFY_CEASE, 0); + bgp_notify_send (peer, BGP_NOTIFY_OPEN_ERR, + BGP_NOTIFY_OPEN_UNSPECIFIC); return -1; } @@ -767,8 +796,7 @@ bgp_open_option_parse (struct peer *peer, u_char length, int *capability) ret = bgp_auth_parse (peer, opt_length); break; case BGP_OPEN_OPT_CAP: - ret = bgp_capability_parse (peer, opt_length, &error); - *capability = 1; + ret = bgp_capability_parse (peer, opt_length, mp_capability, &error); break; default: bgp_notify_send (peer, @@ -811,17 +839,23 @@ bgp_open_option_parse (struct peer *peer, u_char length, int *capability) } } - /* Check there is no common capability send Unsupported Capability + /* Check there are no common AFI/SAFIs and send Unsupported Capability error. */ - if (*capability && ! CHECK_FLAG (peer->flags, PEER_FLAG_OVERRIDE_CAPABILITY)) + if (*mp_capability && + ! CHECK_FLAG (peer->flags, PEER_FLAG_OVERRIDE_CAPABILITY)) { if (! peer->afc_nego[AFI_IP][SAFI_UNICAST] && ! peer->afc_nego[AFI_IP][SAFI_MULTICAST] && ! peer->afc_nego[AFI_IP][SAFI_MPLS_VPN] + && ! peer->afc_nego[AFI_IP][SAFI_ENCAP] && ! peer->afc_nego[AFI_IP6][SAFI_UNICAST] - && ! peer->afc_nego[AFI_IP6][SAFI_MULTICAST]) + && ! peer->afc_nego[AFI_IP6][SAFI_MULTICAST] + && ! peer->afc_nego[AFI_IP6][SAFI_MPLS_VPN] + && ! peer->afc_nego[AFI_IP6][SAFI_ENCAP]) { - plog_err (peer->log, "%s [Error] No common capability", peer->host); + plog_err (peer->log, "%s [Error] Configured AFI/SAFIs do not " + "overlap with received MP capabilities", + peer->host); if (error != error_data) @@ -909,10 +943,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); @@ -961,7 +996,18 @@ bgp_open_capability (struct stream *s, struct peer *peer) stream_putc (s, 0); stream_putc (s, SAFI_MPLS_LABELED_VPN); } -#ifdef HAVE_IPV6 + /* ENCAP */ + if (peer->afc[AFI_IP][SAFI_ENCAP]) + { + peer->afc_adv[AFI_IP][SAFI_ENCAP] = 1; + stream_putc (s, BGP_OPEN_OPT_CAP); + stream_putc (s, CAPABILITY_CODE_MP_LEN + 2); + stream_putc (s, CAPABILITY_CODE_MP); + stream_putc (s, CAPABILITY_CODE_MP_LEN); + stream_putw (s, AFI_IP); + stream_putc (s, 0); + stream_putc (s, SAFI_ENCAP); + } /* IPv6 unicast. */ if (peer->afc[AFI_IP6][SAFI_UNICAST]) { @@ -986,7 +1032,30 @@ bgp_open_capability (struct stream *s, struct peer *peer) stream_putc (s, 0); stream_putc (s, SAFI_MULTICAST); } -#endif /* HAVE_IPV6 */ + /* IPv6 VPN. */ + if (peer->afc[AFI_IP6][SAFI_MPLS_VPN]) + { + peer->afc_adv[AFI_IP6][SAFI_MPLS_VPN] = 1; + stream_putc (s, BGP_OPEN_OPT_CAP); + stream_putc (s, CAPABILITY_CODE_MP_LEN + 2); + stream_putc (s, CAPABILITY_CODE_MP); + stream_putc (s, CAPABILITY_CODE_MP_LEN); + stream_putw (s, AFI_IP6); + stream_putc (s, 0); + stream_putc (s, SAFI_MPLS_LABELED_VPN); + } + /* IPv6 ENCAP. */ + if (peer->afc[AFI_IP6][SAFI_ENCAP]) + { + peer->afc_adv[AFI_IP6][SAFI_ENCAP] = 1; + stream_putc (s, BGP_OPEN_OPT_CAP); + stream_putc (s, CAPABILITY_CODE_MP_LEN + 2); + stream_putc (s, CAPABILITY_CODE_MP); + stream_putc (s, CAPABILITY_CODE_MP_LEN); + stream_putw (s, AFI_IP6); + stream_putc (s, 0); + stream_putc (s, SAFI_ENCAP); + } /* Route refresh. */ SET_FLAG (peer->cap, PEER_CAP_REFRESH_ADV); @@ -1031,16 +1100,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_open.h b/bgpd/bgp_open.h index 2b1382d8b..62333754b 100644 --- a/bgpd/bgp_open.h +++ b/bgpd/bgp_open.h @@ -82,6 +82,7 @@ struct capability_gr #define CAPABILITY_CODE_DYNAMIC_LEN 0 #define CAPABILITY_CODE_RESTART_LEN 2 /* Receiving only case */ #define CAPABILITY_CODE_AS4_LEN 4 +#define CAPABILITY_CODE_ORF_LEN 5 /* Cooperative Route Filtering Capability. */ diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c index 5d8087a8e..b497e4546 100644 --- a/bgpd/bgp_packet.c +++ b/bgpd/bgp_packet.c @@ -28,8 +28,10 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "log.h" #include "memory.h" #include "sockunion.h" /* for inet_ntop () */ +#include "sockopt.h" #include "linklist.h" #include "plist.h" +#include "filter.h" #include "bgpd/bgpd.h" #include "bgpd/bgp_table.h" @@ -45,11 +47,12 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "bgpd/bgp_ecommunity.h" #include "bgpd/bgp_network.h" #include "bgpd/bgp_mplsvpn.h" +#include "bgpd/bgp_encap.h" #include "bgpd/bgp_advertise.h" #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,19 +145,25 @@ 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; - char buf[BUFSIZ]; + unsigned long attrlen_pos = 0; + int space_remaining = 0; + int space_needed = 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,8 +173,12 @@ bgp_update_packet (struct peer *peer, afi_t afi, safi_t safi) if (adv->binfo) binfo = adv->binfo; + space_remaining = STREAM_CONCAT_REMAIN (s, snlri, STREAM_SIZE(s)) - + BGP_MAX_PACKET_SIZE_OVERFLOW; + space_needed = BGP_NLRI_LENGTH + bgp_packet_mpattr_prefix_size (afi, safi, &rn->p); + /* When remaining space can't include NLRI and it's length. */ - if (STREAM_REMAIN (s) <= BGP_NLRI_LENGTH + PSIZE (rn->p.prefixlen)) + if (space_remaining < space_needed) break; /* If packet is empty, set attribute. */ @@ -174,7 +187,7 @@ bgp_update_packet (struct peer *peer, afi_t afi, safi_t safi) 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) @@ -183,26 +196,77 @@ bgp_update_packet (struct peer *peer, afi_t afi, safi_t safi) if (binfo->extra) tag = binfo->extra->tag; } - + + /* 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); + + /* 3: total attributes length - attrlen_pos stores the position */ + attrlen_pos = stream_get_endp (s); stream_putw (s, 0); - total_attr_len = bgp_packet_attribute (NULL, peer, s, + + /* 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, + ((afi == AFI_IP && safi == SAFI_UNICAST) ? + &rn->p : NULL), + afi, safi, from, prd, tag); - stream_putw_at (s, pos, total_attr_len); + space_remaining = STREAM_CONCAT_REMAIN (s, snlri, STREAM_SIZE(s)) - + BGP_MAX_PACKET_SIZE_OVERFLOW; + space_needed = BGP_NLRI_LENGTH + bgp_packet_mpattr_prefix_size (afi, safi, &rn->p);; + + /* If the attributes alone do not leave any room for NLRI then + * return */ + if (space_remaining < space_needed) + { + zlog_err ("%s cannot send UPDATE, the attributes do not leave " + "room for NLRI", peer->host); + /* Flush the FIFO update queue */ + while (adv) + adv = bgp_advertise_clean (peer, adv->adj, afi, safi); + return 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)) - zlog (peer->log, LOG_DEBUG, "%s send UPDATE %s/%d", - peer->host, - inet_ntop (rn->p.family, &(rn->p.u.prefix), buf, BUFSIZ), - rn->p.prefixlen); + { + char buf[INET6_BUFSIZ]; + + zlog (peer->log, LOG_DEBUG, "%s send UPDATE %s/%d", + peer->host, + inet_ntop (rn->p.family, &(rn->p.u.prefix), buf, INET6_BUFSIZ), + rn->p.prefixlen); + } /* Synchnorize attribute. */ if (adj->attr) @@ -213,18 +277,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; @@ -234,7 +308,6 @@ static struct stream * bgp_update_packet_eor (struct peer *peer, afi_t afi, safi_t safi) { struct stream *s; - struct stream *packet; if (DISABLE_BGP_ANNOUNCE) return NULL; @@ -267,13 +340,20 @@ bgp_update_packet_eor (struct peer *peer, afi_t afi, safi_t safi) } bgp_packet_set_size (s); - packet = stream_dup (s); - bgp_packet_add (peer, packet); - stream_free (s); - return packet; + bgp_packet_add (peer, s); + return s; } /* 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) { @@ -282,71 +362,96 @@ 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; - char buf[BUFSIZ]; + size_t mp_start = 0; + size_t attrlen_pos = 0; + size_t mplen_pos = 0; + u_char first_time = 1; + int space_remaining = 0; + int space_needed = 0; 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) - < (BGP_NLRI_LENGTH + BGP_TOTAL_ATTR_LEN + PSIZE (rn->p.prefixlen))) + space_remaining = STREAM_REMAIN (s) - + BGP_MAX_PACKET_SIZE_OVERFLOW; + space_needed = (BGP_NLRI_LENGTH + BGP_TOTAL_ATTR_LEN + + bgp_packet_mpattr_prefix_size (afi, safi, &rn->p)); + + if (space_remaining < space_needed) 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)) - zlog (peer->log, LOG_DEBUG, "%s send UPDATE %s/%d -- unreachable", - peer->host, - inet_ntop (rn->p.family, &(rn->p.u.prefix), buf, BUFSIZ), - rn->p.prefixlen); + { + char buf[INET6_BUFSIZ]; + + zlog (peer->log, LOG_DEBUG, "%s send UPDATE %s/%d -- unreachable", + peer->host, + inet_ntop (rn->p.family, &(rn->p.u.prefix), buf, INET6_BUFSIZ), + rn->p.prefixlen); + } peer->scount[afi][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); @@ -362,29 +467,28 @@ bgp_default_update_send (struct peer *peer, struct attr *attr, afi_t afi, safi_t safi, struct peer *from) { struct stream *s; - struct stream *packet; struct prefix p; unsigned long pos; bgp_size_t total_attr_len; - char attrstr[BUFSIZ]; - char buf[BUFSIZ]; if (DISABLE_BGP_ANNOUNCE) return; if (afi == AFI_IP) str2prefix ("0.0.0.0/0", &p); -#ifdef HAVE_IPV6 else str2prefix ("::/0", &p); -#endif /* HAVE_IPV6 */ /* Logging the attribute. */ if (BGP_DEBUG (update, UPDATE_OUT)) { + char attrstr[BUFSIZ]; + char buf[INET6_BUFSIZ]; + attrstr[0] = '\0'; + bgp_dump_attr (peer, attr, attrstr, BUFSIZ); zlog (peer->log, LOG_DEBUG, "%s send UPDATE %s/%d %s", - peer->host, inet_ntop(p.family, &(p.u.prefix), buf, BUFSIZ), + peer->host, inet_ntop(p.family, &(p.u.prefix), buf, INET6_BUFSIZ), p.prefixlen, attrstr); } @@ -411,16 +515,13 @@ bgp_default_update_send (struct peer *peer, struct attr *attr, /* Set size. */ bgp_packet_set_size (s); - packet = stream_dup (s); - stream_free (s); - /* Dump packet if debug option is set. */ #ifdef DEBUG /* bgp_packet_dump (packet); */ #endif /* DEBUG */ /* Add packet to the peer. */ - bgp_packet_add (peer, packet); + bgp_packet_add (peer, s); BGP_WRITE_ON (peer->t_write, bgp_write, peer->fd); } @@ -429,31 +530,32 @@ void 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; - char buf[BUFSIZ]; + size_t mp_start = 0; + size_t mplen_pos = 0; if (DISABLE_BGP_ANNOUNCE) return; if (afi == AFI_IP) str2prefix ("0.0.0.0/0", &p); -#ifdef HAVE_IPV6 else str2prefix ("::/0", &p); -#endif /* HAVE_IPV6 */ total_attr_len = 0; - pos = 0; if (BGP_DEBUG (update, UPDATE_OUT)) - zlog (peer->log, LOG_DEBUG, "%s send UPDATE %s/%d -- unreachable", - peer->host, inet_ntop(p.family, &(p.u.prefix), buf, BUFSIZ), - p.prefixlen); + { + char buf[INET6_BUFSIZ]; + + zlog (peer->log, LOG_DEBUG, "%s send UPDATE %s/%d -- unreachable", + peer->host, inet_ntop(p.family, &(p.u.prefix), buf, INET6_BUFSIZ), + p.prefixlen); + } s = stream_new (BGP_MAX_PACKET_SIZE); @@ -479,21 +581,24 @@ 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); - packet = stream_dup (s); - stream_free (s); - /* Add packet to the peer. */ - bgp_packet_add (peer, packet); + bgp_packet_add (peer, s); BGP_WRITE_ON (peer->t_write, bgp_write, peer->fd); } @@ -514,7 +619,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); @@ -526,13 +631,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) { @@ -582,7 +691,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; @@ -657,16 +766,11 @@ bgp_write (struct thread *thread) break; case BGP_MSG_NOTIFY: peer->notify_out++; - /* Double start timer. */ - peer->v_start *= 2; - - /* Overflow check. */ - if (peer->v_start >= (60 * 2)) - peer->v_start = (60 * 2); /* Flush any existing events */ - BGP_EVENT_ADD (peer, BGP_Stop); - return 0; + BGP_EVENT_ADD (peer, BGP_Stop_with_error); + goto done; + case BGP_MSG_KEEPALIVE: peer->keepalive_out++; break; @@ -687,9 +791,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; } @@ -707,17 +811,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); @@ -727,14 +840,7 @@ bgp_write_notify (struct peer *peer) /* Type should be notify. */ peer->notify_out++; - /* Double start timer. */ - peer->v_start *= 2; - - /* Overflow check. */ - if (peer->v_start >= (60 * 2)) - peer->v_start = (60 * 2); - - BGP_EVENT_ADD (peer, BGP_Stop); + BGP_EVENT_ADD (peer, BGP_Stop_with_error); return 0; } @@ -883,8 +989,13 @@ bgp_notify_send_with_data (struct peer *peer, u_char code, u_char sub_code, } } bgp_notify_print (peer, &bgp_notify, "sending"); + if (bgp_notify.data) - XFREE (MTYPE_TMP, bgp_notify.data); + { + XFREE (MTYPE_TMP, bgp_notify.data); + bgp_notify.data = NULL; + bgp_notify.length = 0; + } } if (BGP_DEBUG (normal, NORMAL)) @@ -897,23 +1008,26 @@ bgp_notify_send_with_data (struct peer *peer, u_char code, u_char sub_code, if (sub_code == BGP_NOTIFY_CEASE_ADMIN_RESET) { peer->last_reset = PEER_DOWN_USER_RESET; - zlog_info ("Notification sent to neighbor %s: User reset", peer->host); + zlog_info ("Notification sent to neighbor %s:%u: User reset", + peer->host, sockunion_get_port (&peer->su)); } else if (sub_code == BGP_NOTIFY_CEASE_ADMIN_SHUTDOWN) { peer->last_reset = PEER_DOWN_USER_SHUTDOWN; - zlog_info ("Notification sent to neighbor %s: shutdown", peer->host); + zlog_info ("Notification sent to neighbor %s:%u shutdown", + peer->host, sockunion_get_port (&peer->su)); } else { peer->last_reset = PEER_DOWN_NOTIFY_SEND; - zlog_info ("Notification sent to neighbor %s: type %u/%u", - peer->host, code, sub_code); + zlog_info ("Notification sent to neighbor %s:%u: type %u/%u", + peer->host, sockunion_get_port (&peer->su), + code, sub_code); } } else - zlog_info ("Notification sent to neighbor %s: configuration change", - peer->host); + zlog_info ("Notification sent to neighbor %s:%u: configuration change", + peer->host, sockunion_get_port (&peer->su)); /* Call immediately. */ BGP_WRITE_OFF (peer->t_write); @@ -934,7 +1048,6 @@ bgp_route_refresh_send (struct peer *peer, afi_t afi, safi_t safi, u_char orf_type, u_char when_to_refresh, int remove) { struct stream *s; - struct stream *packet; int length; struct bgp_filter *filter; int orf_refresh = 0; @@ -1015,12 +1128,8 @@ bgp_route_refresh_send (struct peer *peer, afi_t afi, safi_t safi, BGP_MSG_ROUTE_REFRESH_NEW : BGP_MSG_ROUTE_REFRESH_OLD, length); } - /* Make real packet. */ - packet = stream_dup (s); - stream_free (s); - /* Add packet to the peer. */ - bgp_packet_add (peer, packet); + bgp_packet_add (peer, s); BGP_WRITE_ON (peer->t_write, bgp_write, peer->fd); } @@ -1031,7 +1140,6 @@ bgp_capability_send (struct peer *peer, afi_t afi, safi_t safi, int capability_code, int action) { struct stream *s; - struct stream *packet; int length; /* Adjust safi code. */ @@ -1062,12 +1170,9 @@ bgp_capability_send (struct peer *peer, afi_t afi, safi_t safi, /* Set packet size. */ length = bgp_packet_set_size (s); - /* Make real packet. */ - packet = stream_dup (s); - stream_free (s); /* Add packet to the peer. */ - bgp_packet_add (peer, packet); + bgp_packet_add (peer, s); if (BGP_DEBUG (normal, NORMAL)) zlog_debug ("%s send message type %d, length (incl. header) %d", @@ -1075,7 +1180,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) @@ -1099,13 +1204,55 @@ bgp_collision_detect (struct peer *new, struct in_addr remote_id) for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer)) { - /* Under OpenConfirm status, local peer structure already hold - remote router ID. */ - - if (peer != new - && (peer->status == OpenConfirm || peer->status == OpenSent) - && sockunion_same (&peer->su, &new->su)) + if (peer == new) + continue; + if (!sockunion_same (&peer->su, &new->su)) + continue; + + /* Unless allowed via configuration, a connection collision with an + existing BGP connection that is in the Established state causes + closing of the newly created connection. */ + if (peer->status == Established) + { + /* GR may do things slightly differently to classic RFC . Punt to + * open_receive, see below + */ + if (CHECK_FLAG (peer->sflags, PEER_STATUS_NSF_MODE)) + continue; + + if (new->fd >= 0) + { + if (BGP_DEBUG (events, EVENTS)) + zlog_debug ("%s:%u Existing Established peer, sending NOTIFY", + new->host, sockunion_get_port (&new->su)); + bgp_notify_send (new, BGP_NOTIFY_CEASE, + BGP_NOTIFY_CEASE_COLLISION_RESOLUTION); + } + return -1; + } + + /* Note: Quagga historically orders explicitly only on the processing + * of the Opens, treating 'new' as the passive, inbound and connection + * and 'peer' as the active outbound connection. + */ + + /* The local_id is always set, so we can match the given remote-ID + * from the OPEN against both OpenConfirm and OpenSent peers. + */ + if (peer->status == OpenConfirm || peer->status == OpenSent) { + struct peer *out = peer; + struct peer *in = new; + int ret_close_out = 1, ret_close_in = -1; + + if (!CHECK_FLAG (new->sflags, PEER_STATUS_ACCEPT_PEER)) + { + out = new; + ret_close_out = -1; + in = peer; + ret_close_in = 1; + } + /* 1. The BGP Identifier of the local system is compared to the BGP Identifier of the remote system (as specified in the OPEN message). */ @@ -1118,9 +1265,15 @@ bgp_collision_detect (struct peer *new, struct in_addr remote_id) already in the OpenConfirm state), and accepts BGP connection initiated by the remote system. */ - if (peer->fd >= 0) - bgp_notify_send (peer, BGP_NOTIFY_CEASE, BGP_NOTIFY_CEASE_COLLISION_RESOLUTION); - return 1; + if (out->fd >= 0) + { + if (BGP_DEBUG (events, EVENTS)) + zlog_debug ("%s Collision resolution, remote ID higher," + " closing outbound", peer->host); + bgp_notify_send (out, BGP_NOTIFY_CEASE, + BGP_NOTIFY_CEASE_COLLISION_RESOLUTION); + } + return ret_close_out; } else { @@ -1130,10 +1283,16 @@ bgp_collision_detect (struct peer *new, struct in_addr remote_id) existing one (the one that is already in the OpenConfirm state). */ - if (new->fd >= 0) - bgp_notify_send (new, BGP_NOTIFY_CEASE, - BGP_NOTIFY_CEASE_COLLISION_RESOLUTION); - return -1; + if (in->fd >= 0) + { + if (BGP_DEBUG (events, EVENTS)) + zlog_debug ("%s Collision resolution, local ID higher," + " closing inbound", peer->host); + + bgp_notify_send (in, BGP_NOTIFY_CEASE, + BGP_NOTIFY_CEASE_COLLISION_RESOLUTION); + } + return ret_close_in; } } } @@ -1152,9 +1311,10 @@ bgp_open_receive (struct peer *peer, bgp_size_t size) as_t as4 = 0; struct peer *realpeer; struct in_addr remote_id; - int capability; + int mp_capability; u_int8_t notify_data_remote_as[2]; u_int8_t notify_data_remote_id[4]; + u_int16_t *holdtime_ptr; realpeer = NULL; @@ -1162,6 +1322,7 @@ bgp_open_receive (struct peer *peer, bgp_size_t size) version = stream_getc (peer->ibuf); memcpy (notify_data_remote_as, stream_pnt (peer->ibuf), 2); remote_as = stream_getw (peer->ibuf); + holdtime_ptr = (u_int16_t *)stream_pnt (peer->ibuf); holdtime = stream_getw (peer->ibuf); memcpy (notify_data_remote_id, stream_pnt (peer->ibuf), 4); remote_id.s_addr = stream_get_ipv4 (peer->ibuf); @@ -1169,12 +1330,14 @@ bgp_open_receive (struct peer *peer, bgp_size_t size) /* Receive OPEN message log */ if (BGP_DEBUG (normal, NORMAL)) zlog_debug ("%s rcv OPEN, version %d, remote-as (in open) %u," - " holdtime %d, id %s", + " holdtime %d, id %s, %sbound connection", peer->host, version, remote_as, holdtime, - inet_ntoa (remote_id)); + inet_ntoa (remote_id), + CHECK_FLAG(peer->sflags, PEER_STATUS_ACCEPT_PEER) + ? "in" : "out"); /* BEGIN to read the capability here, but dont do it yet */ - capability = 0; + mp_capability = 0; optlen = stream_getc (peer->ibuf); if (optlen != 0) @@ -1277,78 +1440,89 @@ bgp_open_receive (struct peer *peer, bgp_size_t size) if (ret < 0) return ret; - /* Hack part. */ + /* Bit hacky */ if (CHECK_FLAG (peer->sflags, PEER_STATUS_ACCEPT_PEER)) - { - if (realpeer->status == Established + { + /* Connection FSM state is intertwined with our peer configuration + * (the RFC encourages this a bit). At _this_ point we have a + * 'realpeer' which represents the configuration and any earlier FSM + * (outbound, unless the remote side has opened two connections to + * us), and a 'peer' which here represents an inbound connection that + * has not yet been reconciled with a 'realpeer'. + * + * As 'peer' has just sent an OPEN that reconciliation must now + * happen, as only the 'realpeer' can ever proceed to Established. + * + * bgp_collision_detect should have resolved any collisions with + * realpeers that are in states OpenSent, OpenConfirm or Established, + * and may have sent a notify on the 'realpeer' connection. + * bgp_accept will have rejected any connections where the 'realpeer' + * is in Idle or >Established (though, that status may have changed + * since). + * + * Need to finish off any reconciliation here, and ensure that + * 'realpeer' is left holding any needed state from the appropriate + * connection (fd, buffers, etc.), and any state from the other + * connection is cleaned up. + */ + + /* Is realpeer in some globally-down state, that precludes any and all + * connections (Idle, Clearing, Deleted, etc.)? + */ + if (realpeer->status == Idle || realpeer->status > Established) + { + if (BGP_DEBUG (events, EVENTS)) + zlog_debug ("%s peer status is %s, closing the new connection", + realpeer->host, + LOOKUP (bgp_status_msg, realpeer->status)); + return -1; + } + + /* GR does things differently, and prefers any new connection attempts + * over an Established one (why not just rely on KEEPALIVE and avoid + * having to special case this?) */ + if (realpeer->status == Established && CHECK_FLAG (realpeer->sflags, PEER_STATUS_NSF_MODE)) { realpeer->last_reset = PEER_DOWN_NSF_CLOSE_SESSION; SET_FLAG (realpeer->sflags, PEER_STATUS_NSF_WAIT); } - else if (ret == 0 && realpeer->status != Active - && realpeer->status != OpenSent - && realpeer->status != OpenConfirm - && realpeer->status != Connect) + else if (ret == 0) { - /* XXX: This is an awful problem.. + /* If we're here, RFC collision-detect did not reconcile the + * connections, and the 'realpeer' is still available. So + * 'realpeer' must be 'Active' or 'Connect'. * * According to the RFC we should just let this connection (of the * accepted 'peer') continue on to Established if the other - * connection (the 'realpeer' one) is in state Connect, and deal - * with the more larval FSM as/when it gets far enough to receive - * an Open. We don't do that though, we instead close the (more - * developed) accepted connection. - * - * This means there's a race, which if hit, can loop: - * - * FSM for A FSM for B - * realpeer accept-peer realpeer accept-peer - * - * Connect Connect - * Active - * OpenSent OpenSent - * - * Idle Active - * OpenSent OpenSent - * - * Idle - * - * Connect Connect + * onnection (the 'realpeer') is in a more larval state, and + * reconcile them when OPEN is sent on the 'realpeer'. * - * - * If both sides are Quagga, they're almost certain to wait for - * the same amount of time of course (which doesn't preclude other - * implementations also waiting for same time). The race is - * exacerbated by high-latency (in bgpd and/or the network). + * However, the accepted 'peer' must be reconciled with 'peer' at + * this point, due to the implementation, if 'peer' is to be able + * to proceed. So it should be allowed to go to Established, as + * long as the 'realpeer' was in Active or Connect state - which + * /should/ be the case if we're here. * - * The reason we do this is because our FSM is tied to our peer - * structure, which carries our configuration information, etc. - * I.e. we can't let the accepted-peer FSM continue on as it is, - * cause it's not associated with any actual peer configuration - - * it's just a dummy. - * - * It's possible we could hack-fix this by just bgp_stop'ing the - * realpeer and continueing on with the 'transfer FSM' below. - * Ideally, we need to seperate FSMs from struct peer. - * - * Setting one side to passive avoids the race, as a workaround. + * So we should only need to sanity check that that is the case + * here, and allow the code to get on with transferring the 'peer' + * connection state over. */ - if (BGP_DEBUG (events, EVENTS)) - zlog_debug ("%s peer status is %s close connection", - realpeer->host, LOOKUP (bgp_status_msg, - realpeer->status)); - bgp_notify_send (peer, BGP_NOTIFY_CEASE, - BGP_NOTIFY_CEASE_CONNECT_REJECT); - - return -1; + if (realpeer->status != Active && realpeer->status != Connect) + { + if (BGP_DEBUG (events, EVENTS)) + zlog_warn ("%s real peer status should be Active or Connect," + " but is %s", + realpeer->host, + LOOKUP (bgp_status_msg, realpeer->status)); + bgp_notify_send (realpeer, BGP_NOTIFY_CEASE, + BGP_NOTIFY_CEASE_COLLISION_RESOLUTION); + } } if (BGP_DEBUG (events, EVENTS)) - zlog_debug ("%s [Event] Transfer accept BGP peer to real (state %s)", - peer->host, + zlog_debug ("%s:%u [Event] Transfer accept BGP peer to real (state %s)", + peer->host, sockunion_get_port (&peer->su), LOOKUP (bgp_status_msg, realpeer->status)); bgp_stop (realpeer); @@ -1362,14 +1536,22 @@ bgp_open_receive (struct peer *peer, bgp_size_t size) realpeer->ibuf = peer->ibuf; realpeer->packet_size = peer->packet_size; peer->ibuf = NULL; - + + /* Transfer output buffer, there may be an OPEN queued to send */ + stream_fifo_free (realpeer->obuf); + realpeer->obuf = peer->obuf; + peer->obuf = NULL; + + bool open_deferred + = CHECK_FLAG (peer->sflags, PEER_STATUS_OPEN_DEFERRED); + /* Transfer status. */ realpeer->status = peer->status; bgp_stop (peer); - /* peer pointer change. Open packet send to neighbor. */ + /* peer pointer change */ peer = realpeer; - bgp_open_send (peer); + if (peer->fd < 0) { zlog_err ("bgp_open_receive peer's fd is negative value %d", @@ -1377,6 +1559,18 @@ bgp_open_receive (struct peer *peer, bgp_size_t size) return -1; } BGP_READ_ON (peer->t_read, bgp_read, peer->fd); + if (stream_fifo_head (peer->obuf)) + BGP_WRITE_ON (peer->t_write, bgp_write, peer->fd); + + /* hack: we may defer OPEN on accept peers, when there seems to be a + * realpeer in progress, when an accept peer connection is opened. This + * is to avoid interoperability issues, with test/conformance tools + * particularly. See bgp_fsm.c::bgp_connect_success + * + * If OPEN was deferred there, then we must send it now. + */ + if (open_deferred) + bgp_open_send (peer); } /* remote router-id check. */ @@ -1400,14 +1594,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; } @@ -1432,9 +1628,10 @@ bgp_open_receive (struct peer *peer, bgp_size_t size) if (holdtime < 3 && holdtime != 0) { - bgp_notify_send (peer, - BGP_NOTIFY_OPEN_ERR, - BGP_NOTIFY_OPEN_UNACEP_HOLDTIME); + bgp_notify_send_with_data (peer, + BGP_NOTIFY_OPEN_ERR, + BGP_NOTIFY_OPEN_UNACEP_HOLDTIME, + (u_int8_t *)holdtime_ptr, 2); return -1; } @@ -1459,11 +1656,11 @@ bgp_open_receive (struct peer *peer, bgp_size_t size) /* Open option part parse. */ if (optlen != 0) { - if ((ret = bgp_open_option_parse (peer, optlen, &capability)) < 0) + if ((ret = bgp_open_option_parse (peer, optlen, &mp_capability)) < 0) { bgp_notify_send (peer, BGP_NOTIFY_OPEN_ERR, - BGP_NOTIFY_OPEN_UNACEP_HOLDTIME); + BGP_NOTIFY_OPEN_UNSPECIFIC); return ret; } } @@ -1474,8 +1671,13 @@ bgp_open_receive (struct peer *peer, bgp_size_t size) peer->host); } - /* Override capability. */ - if (! capability || CHECK_FLAG (peer->flags, PEER_FLAG_OVERRIDE_CAPABILITY)) + /* + * Assume that the peer supports the locally configured set of + * AFI/SAFIs if the peer did not send us any Mulitiprotocol + * capabilities, or if 'override-capability' is configured. + */ + if (! mp_capability || + CHECK_FLAG (peer->flags, PEER_FLAG_OVERRIDE_CAPABILITY)) { peer->afc_nego[AFI_IP][SAFI_UNICAST] = peer->afc[AFI_IP][SAFI_UNICAST]; peer->afc_nego[AFI_IP][SAFI_MULTICAST] = peer->afc[AFI_IP][SAFI_MULTICAST]; @@ -1485,6 +1687,7 @@ bgp_open_receive (struct peer *peer, bgp_size_t size) /* Get sockname. */ bgp_getsockname (peer); + peer->rtt = sockopt_tcp_rtt (peer->fd); BGP_EVENT_ADD (peer, Receive_OPEN_message); @@ -1495,22 +1698,46 @@ bgp_open_receive (struct peer *peer, bgp_size_t size) return 0; } +/* Frontend for NLRI parsing, to fan-out to AFI/SAFI specific parsers */ +int +bgp_nlri_parse (struct peer *peer, struct attr *attr, struct bgp_nlri *packet) +{ + switch (packet->safi) + { + case SAFI_UNICAST: + case SAFI_MULTICAST: + return bgp_nlri_parse_ip (peer, attr, packet); + case SAFI_MPLS_VPN: + case SAFI_MPLS_LABELED_VPN: + return bgp_nlri_parse_vpn (peer, attr, packet); + case SAFI_ENCAP: + return bgp_nlri_parse_encap (peer, attr, packet); + } + return -1; +} + /* Parse BGP Update packet and make attribute object. */ static int bgp_update_receive (struct peer *peer, bgp_size_t size) { - int ret; + int ret, nlri_ret; u_char *end; struct stream *s; struct attr attr; + struct attr_extra extra; bgp_size_t attribute_len; bgp_size_t update_len; bgp_size_t withdraw_len; - struct bgp_nlri update; - struct bgp_nlri withdraw; - struct bgp_nlri mp_update; - struct bgp_nlri mp_withdraw; - char attrstr[BUFSIZ] = ""; + int i; + + enum NLRI_TYPES { + NLRI_UPDATE, + NLRI_WITHDRAW, + NLRI_MP_UPDATE, + NLRI_MP_WITHDRAW, + NLRI_TYPE_MAX, + }; + struct bgp_nlri nlris[NLRI_TYPE_MAX]; /* Status must be Established. */ if (peer->status != Established) @@ -1523,10 +1750,10 @@ bgp_update_receive (struct peer *peer, bgp_size_t size) /* Set initial values. */ memset (&attr, 0, sizeof (struct attr)); - memset (&update, 0, sizeof (struct bgp_nlri)); - memset (&withdraw, 0, sizeof (struct bgp_nlri)); - memset (&mp_update, 0, sizeof (struct bgp_nlri)); - memset (&mp_withdraw, 0, sizeof (struct bgp_nlri)); + memset (&extra, 0, sizeof (struct attr_extra)); + memset (&nlris, 0, sizeof nlris); + + attr.extra = &extra; s = peer->ibuf; end = stream_pnt (s) + size; @@ -1562,17 +1789,14 @@ bgp_update_receive (struct peer *peer, bgp_size_t size) /* Unfeasible Route packet format check. */ if (withdraw_len > 0) { - ret = bgp_nlri_sanity_check (peer, AFI_IP, stream_pnt (s), withdraw_len); - if (ret < 0) - return -1; - + nlris[NLRI_WITHDRAW].afi = AFI_IP; + nlris[NLRI_WITHDRAW].safi = SAFI_UNICAST; + nlris[NLRI_WITHDRAW].nlri = stream_pnt (s); + nlris[NLRI_WITHDRAW].length = withdraw_len; + if (BGP_DEBUG (packet, PACKET_RECV)) zlog_debug ("%s [Update:RECV] Unfeasible NLRI received", peer->host); - withdraw.afi = AFI_IP; - withdraw.safi = SAFI_UNICAST; - withdraw.nlri = stream_pnt (s); - withdraw.length = withdraw_len; stream_forward_getp (s, withdraw_len); } @@ -1618,15 +1842,22 @@ bgp_update_receive (struct peer *peer, bgp_size_t size) if (attribute_len) { attr_parse_ret = bgp_attr_parse (peer, &attr, attribute_len, - &mp_update, &mp_withdraw); + &nlris[NLRI_MP_UPDATE], &nlris[NLRI_MP_WITHDRAW]); if (attr_parse_ret == BGP_ATTR_PARSE_ERROR) - return -1; + { + bgp_attr_unintern_sub (&attr); + bgp_attr_flush (&attr); + return -1; + } } /* Logging the attribute. */ if (attr_parse_ret == BGP_ATTR_PARSE_WITHDRAW || BGP_DEBUG (update, UPDATE_IN)) { + char attrstr[BUFSIZ]; + attrstr[0] = '\0'; + ret= bgp_dump_attr (peer, &attr, attrstr, BUFSIZ); int lvl = (attr_parse_ret == BGP_ATTR_PARSE_WITHDRAW) ? LOG_ERR : LOG_DEBUG; @@ -1646,190 +1877,132 @@ bgp_update_receive (struct peer *peer, bgp_size_t size) if (update_len) { - /* Check NLRI packet format and prefix length. */ - ret = bgp_nlri_sanity_check (peer, AFI_IP, stream_pnt (s), update_len); - if (ret < 0) - { - bgp_attr_unintern_sub (&attr); - if (attr.extra) - bgp_attr_extra_free (&attr); - return -1; - } - /* Set NLRI portion to structure. */ - update.afi = AFI_IP; - update.safi = SAFI_UNICAST; - update.nlri = stream_pnt (s); - update.length = update_len; + nlris[NLRI_UPDATE].afi = AFI_IP; + nlris[NLRI_UPDATE].safi = SAFI_UNICAST; + nlris[NLRI_UPDATE].nlri = stream_pnt (s); + nlris[NLRI_UPDATE].length = update_len; + stream_forward_getp (s, update_len); } - - /* NLRI is processed only when the peer is configured specific - Address Family and Subsequent Address Family. */ - if (peer->afc[AFI_IP][SAFI_UNICAST]) + + /* Parse any given NLRIs */ + for (i = NLRI_UPDATE; i < NLRI_TYPE_MAX; i++) { - if (withdraw.length) - 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); - if (attr.extra) - bgp_attr_extra_free (&attr); - return -1; - } - - bgp_nlri_parse (peer, NLRI_ATTR_ARG, &update); - } - - if (mp_update.length - && mp_update.afi == AFI_IP - && mp_update.safi == SAFI_UNICAST) - bgp_nlri_parse (peer, NLRI_ATTR_ARG, &mp_update); - - if (mp_withdraw.length - && mp_withdraw.afi == AFI_IP - && mp_withdraw.safi == SAFI_UNICAST) - bgp_nlri_parse (peer, NULL, &mp_withdraw); - - if (! attribute_len && ! withdraw_len) - { - /* End-of-RIB received */ - SET_FLAG (peer->af_sflags[AFI_IP][SAFI_UNICAST], - PEER_STATUS_EOR_RECEIVED); - - /* NSF delete stale route */ - if (peer->nsf[AFI_IP][SAFI_UNICAST]) - bgp_clear_stale_route (peer, AFI_IP, SAFI_UNICAST); - - if (BGP_DEBUG (normal, NORMAL)) - zlog (peer->log, LOG_DEBUG, "rcvd End-of-RIB for IPv4 Unicast from %s", - peer->host); - } + if (!nlris[i].nlri) continue; + + /* We use afi and safi as indices into tables and what not. It would + * be impossible, at this time, to support unknown afi/safis. And + * anyway, the peer needs to be configured to enable the afi/safi + * explicitly which requires UI support. + * + * Ignore unknown afi/safi NLRIs. + * + * Note: this means nlri[x].afi/safi still can not be trusted for + * indexing later in this function! + * + * Note2: This will also remap the wire code-point for VPN safi to the + * internal safi_t point, as needs be. + */ + if (!bgp_afi_safi_valid_indices (nlris[i].afi, &nlris[i].safi)) + { + plog_info (peer->log, + "%s [Info] UPDATE with unsupported AFI/SAFI %u/%u", + peer->host, nlris[i].afi, nlris[i].safi); + continue; + } + + /* NLRI is processed only when the peer is configured specific + Address Family and Subsequent Address Family. */ + if (!peer->afc[nlris[i].afi][nlris[i].safi]) + { + plog_info (peer->log, + "%s [Info] UPDATE for non-enabled AFI/SAFI %u/%u", + peer->host, nlris[i].afi, nlris[i].safi); + continue; + } + + /* EoR handled later */ + if (nlris[i].length == 0) + continue; + + switch (i) + { + case NLRI_UPDATE: + case NLRI_MP_UPDATE: + nlri_ret = bgp_nlri_parse (peer, NLRI_ATTR_ARG, &nlris[i]); + break; + case NLRI_WITHDRAW: + case NLRI_MP_WITHDRAW: + nlri_ret = bgp_nlri_parse (peer, NULL, &nlris[i]); + } + + if (nlri_ret < 0) + { + plog_err (peer->log, + "%s [Error] Error parsing NLRI", peer->host); + if (peer->status == Established) + bgp_notify_send (peer, BGP_NOTIFY_UPDATE_ERR, + i <= NLRI_WITHDRAW + ? BGP_NOTIFY_UPDATE_INVAL_NETWORK + : BGP_NOTIFY_UPDATE_OPT_ATTR_ERR); + bgp_attr_unintern_sub (&attr); + return -1; + } } - if (peer->afc[AFI_IP][SAFI_MULTICAST]) + + /* EoR checks. + * + * Non-MP IPv4/Unicast EoR is a completely empty UPDATE + * and MP EoR should have only an empty MP_UNREACH + */ + if (!update_len && !withdraw_len + && nlris[NLRI_MP_UPDATE].length == 0) { - if (mp_update.length - && mp_update.afi == AFI_IP - && mp_update.safi == SAFI_MULTICAST) - bgp_nlri_parse (peer, NLRI_ATTR_ARG, &mp_update); - - if (mp_withdraw.length - && mp_withdraw.afi == AFI_IP - && mp_withdraw.safi == SAFI_MULTICAST) - bgp_nlri_parse (peer, NULL, &mp_withdraw); - - if (! withdraw_len - && mp_withdraw.afi == AFI_IP - && mp_withdraw.safi == SAFI_MULTICAST - && mp_withdraw.length == 0) - { + afi_t afi = 0; + safi_t safi; + + /* Non-MP IPv4/Unicast is a completely empty UPDATE - already + * checked update and withdraw NLRI lengths are 0. + */ + if (!attribute_len) + { + afi = AFI_IP; + safi = SAFI_UNICAST; + } + /* otherwise MP AFI/SAFI is an empty update, other than an empty + * MP_UNREACH_NLRI attr (with an AFI/SAFI we recognise). + */ + else if (attr.flag == BGP_ATTR_MP_UNREACH_NLRI + && nlris[NLRI_MP_WITHDRAW].length == 0 + && bgp_afi_safi_valid_indices (nlris[NLRI_MP_WITHDRAW].afi, + &nlris[NLRI_MP_WITHDRAW].safi)) + { + afi = nlris[NLRI_MP_WITHDRAW].afi; + safi = nlris[NLRI_MP_WITHDRAW].safi; + } + + if (afi && peer->afc[afi][safi]) + { /* End-of-RIB received */ - SET_FLAG (peer->af_sflags[AFI_IP][SAFI_MULTICAST], + SET_FLAG (peer->af_sflags[afi][safi], PEER_STATUS_EOR_RECEIVED); /* NSF delete stale route */ - if (peer->nsf[AFI_IP][SAFI_MULTICAST]) - bgp_clear_stale_route (peer, AFI_IP, SAFI_MULTICAST); + if (peer->nsf[afi][safi]) + bgp_clear_stale_route (peer, afi, safi); if (BGP_DEBUG (normal, NORMAL)) - zlog (peer->log, LOG_DEBUG, "rcvd End-of-RIB for IPv4 Multicast from %s", - peer->host); - } - } - if (peer->afc[AFI_IP6][SAFI_UNICAST]) - { - if (mp_update.length - && mp_update.afi == AFI_IP6 - && mp_update.safi == SAFI_UNICAST) - bgp_nlri_parse (peer, NLRI_ATTR_ARG, &mp_update); - - if (mp_withdraw.length - && mp_withdraw.afi == AFI_IP6 - && mp_withdraw.safi == SAFI_UNICAST) - bgp_nlri_parse (peer, NULL, &mp_withdraw); - - if (! withdraw_len - && mp_withdraw.afi == AFI_IP6 - && mp_withdraw.safi == SAFI_UNICAST - && mp_withdraw.length == 0) - { - /* End-of-RIB received */ - SET_FLAG (peer->af_sflags[AFI_IP6][SAFI_UNICAST], PEER_STATUS_EOR_RECEIVED); - - /* NSF delete stale route */ - if (peer->nsf[AFI_IP6][SAFI_UNICAST]) - bgp_clear_stale_route (peer, AFI_IP6, SAFI_UNICAST); - - if (BGP_DEBUG (normal, NORMAL)) - zlog (peer->log, LOG_DEBUG, "rcvd End-of-RIB for IPv6 Unicast from %s", - peer->host); - } - } - if (peer->afc[AFI_IP6][SAFI_MULTICAST]) - { - if (mp_update.length - && mp_update.afi == AFI_IP6 - && mp_update.safi == SAFI_MULTICAST) - bgp_nlri_parse (peer, NLRI_ATTR_ARG, &mp_update); - - if (mp_withdraw.length - && mp_withdraw.afi == AFI_IP6 - && mp_withdraw.safi == SAFI_MULTICAST) - bgp_nlri_parse (peer, NULL, &mp_withdraw); - - if (! withdraw_len - && mp_withdraw.afi == AFI_IP6 - && mp_withdraw.safi == SAFI_MULTICAST - && mp_withdraw.length == 0) - { - /* End-of-RIB received */ - - /* NSF delete stale route */ - if (peer->nsf[AFI_IP6][SAFI_MULTICAST]) - bgp_clear_stale_route (peer, AFI_IP6, SAFI_MULTICAST); - - if (BGP_DEBUG (update, UPDATE_IN)) - zlog (peer->log, LOG_DEBUG, "rcvd End-of-RIB for IPv6 Multicast from %s", - peer->host); - } - } - if (peer->afc[AFI_IP][SAFI_MPLS_VPN]) - { - if (mp_update.length - && mp_update.afi == AFI_IP - && mp_update.safi == SAFI_MPLS_LABELED_VPN) - bgp_nlri_parse_vpnv4 (peer, NLRI_ATTR_ARG, &mp_update); - - if (mp_withdraw.length - && mp_withdraw.afi == AFI_IP - && mp_withdraw.safi == SAFI_MPLS_LABELED_VPN) - bgp_nlri_parse_vpnv4 (peer, NULL, &mp_withdraw); - - if (! withdraw_len - && mp_withdraw.afi == AFI_IP - && mp_withdraw.safi == SAFI_MPLS_LABELED_VPN - && mp_withdraw.length == 0) - { - /* End-of-RIB received */ - - if (BGP_DEBUG (update, UPDATE_IN)) - zlog (peer->log, LOG_DEBUG, "rcvd End-of-RIB for VPNv4 Unicast from %s", - peer->host); - } + zlog (peer->log, LOG_DEBUG, "rcvd End-of-RIB for %s from %s", + peer->host, afi_safi_print (afi, safi)); + } } - + /* Everything is done. We unintern temporary structures which interned in bgp_attr_parse(). */ bgp_attr_unintern_sub (&attr); - if (attr.extra) - bgp_attr_extra_free (&attr); - + bgp_attr_flush (&attr); + /* If peering is stopped due to some reason, do not generate BGP event. */ if (peer->status != Established) @@ -1839,8 +2012,9 @@ bgp_update_receive (struct peer *peer, bgp_size_t size) peer->update_in++; peer->update_time = bgp_clock (); - /* Generate BGP event. */ - BGP_EVENT_ADD (peer, Receive_UPDATE_message); + /* Rearm holdtime timer */ + BGP_TIMER_OFF (peer->t_holdtime); + bgp_timer_set (peer); return 0; } @@ -1899,7 +2073,11 @@ bgp_notify_receive (struct peer *peer, bgp_size_t size) bgp_notify_print(peer, &bgp_notify, "received"); if (bgp_notify.data) - XFREE (MTYPE_TMP, bgp_notify.data); + { + XFREE (MTYPE_TMP, bgp_notify.data); + bgp_notify.data = NULL; + bgp_notify.length = 0; + } } /* peer count update */ @@ -1935,7 +2113,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. */ @@ -1963,7 +2140,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)) @@ -2015,14 +2193,13 @@ 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; int psize; char name[BUFSIZ]; - char buf[BUFSIZ]; int ret; if (BGP_DEBUG (normal, NORMAL)) @@ -2054,10 +2231,10 @@ bgp_route_refresh_receive (struct peer *peer, bgp_size_t size) { if (BGP_DEBUG (normal, NORMAL)) zlog_debug ("%s rcvd Remove-All pfxlist ORF request", peer->host); - prefix_bgp_orf_remove_all (name); + prefix_bgp_orf_remove_all (afi, 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)); @@ -2092,31 +2269,35 @@ bgp_route_refresh_receive (struct peer *peer, bgp_size_t size) p_pnt += psize; if (BGP_DEBUG (normal, NORMAL)) - zlog_debug ("%s rcvd %s %s seq %u %s/%d ge %d le %d%s", - peer->host, - (common & ORF_COMMON_PART_REMOVE ? "Remove" : "Add"), - (common & ORF_COMMON_PART_DENY ? "deny" : "permit"), - orfp.seq, - inet_ntop (orfp.p.family, &orfp.p.u.prefix, buf, BUFSIZ), - orfp.p.prefixlen, orfp.ge, orfp.le, - ok ? "" : " MALFORMED"); - + { + char buf[INET6_BUFSIZ]; + + zlog_debug ("%s rcvd %s %s seq %u %s/%d ge %d le %d%s", + peer->host, + (common & ORF_COMMON_PART_REMOVE ? "Remove" : "Add"), + (common & ORF_COMMON_PART_DENY ? "deny" : "permit"), + orfp.seq, + inet_ntop (orfp.p.family, &orfp.p.u.prefix, buf, INET6_BUFSIZ), + orfp.p.prefixlen, orfp.ge, orfp.le, + ok ? "" : " MALFORMED"); + } + if (ok) 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." " Remove All pfxlist", peer->host); - prefix_bgp_orf_remove_all (name); + prefix_bgp_orf_remove_all (afi, name); break; } } peer->orf_plist[afi][safi] = - prefix_list_lookup (AFI_ORF_PREFIX, name); + prefix_bgp_orf_lookup (afi, name); } stream_forward_getp (s, orf_len); } @@ -2142,11 +2323,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) @@ -2280,7 +2459,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) @@ -2364,6 +2543,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) @@ -2492,14 +2680,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_packet.h b/bgpd/bgp_packet.h index 8f0ebe318..6b0b7f4d4 100644 --- a/bgpd/bgp_packet.h +++ b/bgpd/bgp_packet.h @@ -54,4 +54,6 @@ extern void bgp_default_withdraw_send (struct peer *, afi_t, safi_t); extern int bgp_capability_receive (struct peer *, bgp_size_t); +extern int bgp_nlri_parse (struct peer *, struct attr *, struct bgp_nlri *); + #endif /* _QUAGGA_BGP_PACKET_H */ diff --git a/bgpd/bgp_regex.c b/bgpd/bgp_regex.c index 9b65f7cb1..13fa82951 100644 --- a/bgpd/bgp_regex.c +++ b/bgpd/bgp_regex.c @@ -23,6 +23,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "log.h" #include "command.h" #include "memory.h" +#include "filter.h" #include "bgpd.h" #include "bgp_aspath.h" diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index ba530321a..13596fbed 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -1,5 +1,6 @@ /* BGP routing information Copyright (C) 1996, 97, 98, 99 Kunihiro Ishiguro + Copyright (C) 2016 Job Snijders This file is part of GNU Zebra. @@ -44,6 +45,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "bgpd/bgp_regex.h" #include "bgpd/bgp_community.h" #include "bgpd/bgp_ecommunity.h" +#include "bgpd/bgp_lcommunity.h" #include "bgpd/bgp_clist.h" #include "bgpd/bgp_packet.h" #include "bgpd/bgp_filter.h" @@ -54,11 +56,13 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "bgpd/bgp_advertise.h" #include "bgpd/bgp_zebra.h" #include "bgpd/bgp_vty.h" +#include "bgpd/bgp_mpath.h" +#include "bgpd/bgp_nht.h" /* 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) @@ -70,7 +74,7 @@ bgp_afi_node_get (struct bgp_table *table, afi_t afi, safi_t safi, struct prefix if (!table) return NULL; - if (safi == SAFI_MPLS_VPN) + if ((safi == SAFI_MPLS_VPN) || (safi == SAFI_ENCAP)) { prn = bgp_node_get (table, (struct prefix *) prd); @@ -83,12 +87,12 @@ bgp_afi_node_get (struct bgp_table *table, afi_t afi, safi_t safi, struct prefix rn = bgp_node_get (table, p); - if (safi == SAFI_MPLS_VPN) + if ((safi == SAFI_MPLS_VPN) || (safi == SAFI_ENCAP)) rn->prn = prn; return rn; } - + /* Allocate bgp_info_extra */ static struct bgp_info_extra * bgp_info_extra_new (void) @@ -125,21 +129,16 @@ bgp_info_extra_get (struct bgp_info *ri) return ri->extra; } -/* Allocate new bgp info structure. */ -static struct bgp_info * -bgp_info_new (void) -{ - return XCALLOC (MTYPE_BGP_ROUTE, sizeof (struct bgp_info)); -} - /* Free bgp route information. */ static void bgp_info_free (struct bgp_info *binfo) { if (binfo->attr) bgp_attr_unintern (&binfo->attr); - + + bgp_unlink_nexthop (binfo); bgp_info_extra_free (&binfo->extra); + bgp_info_mpath_free (&binfo->mpath); peer_unlock (binfo->peer); /* bgp_info peer reference */ @@ -210,6 +209,7 @@ bgp_info_reap (struct bgp_node *rn, struct bgp_info *ri) else rn->info = ri->next; + bgp_info_mpath_dequeue (ri); bgp_info_unlock (ri); bgp_unlock_node (rn); } @@ -237,23 +237,27 @@ bgp_info_restore (struct bgp_node *rn, struct bgp_info *ri) static void bgp_pcount_adjust (struct bgp_node *rn, struct bgp_info *ri) { - assert (rn && rn->table); + struct bgp_table *table; + + assert (rn && bgp_node_table (rn)); assert (ri && ri->peer && ri->peer->bgp); + table = bgp_node_table (rn); + /* Ignore 'pcount' for RS-client tables */ - if (rn->table->type != BGP_TABLE_MAIN + if (table->type != BGP_TABLE_MAIN || ri->peer == ri->peer->bgp->peer_self) return; - if (BGP_INFO_HOLDDOWN (ri) + if (!BGP_INFO_COUNTABLE (ri) && CHECK_FLAG (ri->flags, BGP_INFO_COUNTED)) { UNSET_FLAG (ri->flags, BGP_INFO_COUNTED); /* slight hack, but more robust against errors. */ - if (ri->peer->pcount[rn->table->afi][rn->table->safi]) - ri->peer->pcount[rn->table->afi][rn->table->safi]--; + if (ri->peer->pcount[table->afi][table->safi]) + ri->peer->pcount[table->afi][table->safi]--; else { zlog_warn ("%s: Asked to decrement 0 prefix count for peer %s", @@ -262,11 +266,11 @@ bgp_pcount_adjust (struct bgp_node *rn, struct bgp_info *ri) zlog_warn ("%s: Please report to Quagga bugzilla", __func__); } } - else if (!BGP_INFO_HOLDDOWN (ri) + else if (BGP_INFO_COUNTABLE (ri) && !CHECK_FLAG (ri->flags, BGP_INFO_COUNTED)) { SET_FLAG (ri->flags, BGP_INFO_COUNTED); - ri->peer->pcount[rn->table->afi][rn->table->safi]++; + ri->peer->pcount[table->afi][table->safi]++; } } @@ -279,8 +283,8 @@ bgp_info_set_flag (struct bgp_node *rn, struct bgp_info *ri, u_int32_t flag) { SET_FLAG (ri->flags, flag); - /* early bath if we know it's not a flag that changes useability state */ - if (!CHECK_FLAG (flag, BGP_INFO_VALID|BGP_INFO_UNUSEABLE)) + /* early bath if we know it's not a flag that changes countability state */ + if (!CHECK_FLAG (flag, BGP_INFO_VALID|BGP_INFO_HISTORY|BGP_INFO_REMOVED)) return; bgp_pcount_adjust (rn, ri); @@ -291,8 +295,8 @@ bgp_info_unset_flag (struct bgp_node *rn, struct bgp_info *ri, u_int32_t flag) { UNSET_FLAG (ri->flags, flag); - /* early bath if we know it's not a flag that changes useability state */ - if (!CHECK_FLAG (flag, BGP_INFO_VALID|BGP_INFO_UNUSEABLE)) + /* early bath if we know it's not a flag that changes countability state */ + if (!CHECK_FLAG (flag, BGP_INFO_VALID|BGP_INFO_HISTORY|BGP_INFO_REMOVED)) return; bgp_pcount_adjust (rn, ri); @@ -314,212 +318,254 @@ bgp_med_value (struct attr *attr, struct bgp *bgp) } } -/* Compare two bgp route entity. br is preferable then return 1. */ +/* Compare two bgp route entity. Return -1 if new is preferred, 1 if exist + * is preferred, or 0 if they are the same (usually will only occur if + * multipath is enabled */ static int -bgp_info_cmp (struct bgp *bgp, struct bgp_info *new, struct bgp_info *exist) +bgp_info_cmp (struct bgp *bgp, struct bgp_info *new, struct bgp_info *exist, + afi_t afi, safi_t safi) { + struct attr *newattr, *existattr; + struct attr_extra *newattre, *existattre; + bgp_peer_sort_t new_sort; + bgp_peer_sort_t exist_sort; u_int32_t new_pref; u_int32_t exist_pref; u_int32_t new_med; u_int32_t exist_med; - u_int32_t new_weight = 0; - u_int32_t exist_weight = 0; + u_int32_t new_weight; + u_int32_t exist_weight; + uint32_t newm, existm; struct in_addr new_id; struct in_addr exist_id; int new_cluster; int exist_cluster; - int internal_as_route = 0; - int confed_as_route = 0; + int internal_as_route; + int confed_as_route; int ret; /* 0. Null check. */ if (new == NULL) - return 0; - if (exist == NULL) return 1; + if (exist == NULL) + return -1; + + newattr = new->attr; + existattr = exist->attr; + newattre = newattr->extra; + existattre = existattr->extra; /* 1. Weight check. */ - if (new->attr->extra) - new_weight = new->attr->extra->weight; - if (exist->attr->extra) - exist_weight = exist->attr->extra->weight; + new_weight = exist_weight = 0; + + if (newattre) + new_weight = newattre->weight; + if (existattre) + exist_weight = existattre->weight; + if (new_weight > exist_weight) - return 1; + return -1; if (new_weight < exist_weight) - return 0; + return 1; /* 2. Local preference check. */ - if (new->attr->flag & ATTR_FLAG_BIT (BGP_ATTR_LOCAL_PREF)) - new_pref = new->attr->local_pref; - else - new_pref = bgp->default_local_pref; + new_pref = exist_pref = bgp->default_local_pref; + + if (newattr->flag & ATTR_FLAG_BIT (BGP_ATTR_LOCAL_PREF)) + new_pref = newattr->local_pref; + if (existattr->flag & ATTR_FLAG_BIT (BGP_ATTR_LOCAL_PREF)) + exist_pref = existattr->local_pref; - if (exist->attr->flag & ATTR_FLAG_BIT (BGP_ATTR_LOCAL_PREF)) - exist_pref = exist->attr->local_pref; - else - exist_pref = bgp->default_local_pref; - if (new_pref > exist_pref) - return 1; + return -1; if (new_pref < exist_pref) - return 0; - - /* 3. Local route check. */ - if (new->sub_type == BGP_ROUTE_STATIC) return 1; - if (exist->sub_type == BGP_ROUTE_STATIC) - return 0; - - if (new->sub_type == BGP_ROUTE_REDISTRIBUTE) - return 1; - if (exist->sub_type == BGP_ROUTE_REDISTRIBUTE) - return 0; - if (new->sub_type == BGP_ROUTE_AGGREGATE) - return 1; - if (exist->sub_type == BGP_ROUTE_AGGREGATE) - return 0; + /* 3. Local route check. We prefer: + * - BGP_ROUTE_STATIC + * - BGP_ROUTE_AGGREGATE + * - BGP_ROUTE_REDISTRIBUTE + */ + if (! (new->sub_type == BGP_ROUTE_NORMAL)) + return -1; + if (! (exist->sub_type == BGP_ROUTE_NORMAL)) + return 1; /* 4. AS path length check. */ if (! bgp_flag_check (bgp, BGP_FLAG_ASPATH_IGNORE)) { - int exist_hops = aspath_count_hops (exist->attr->aspath); - int exist_confeds = aspath_count_confeds (exist->attr->aspath); + int exist_hops = aspath_count_hops (existattr->aspath); + int exist_confeds = aspath_count_confeds (existattr->aspath); if (bgp_flag_check (bgp, BGP_FLAG_ASPATH_CONFED)) { int aspath_hops; - aspath_hops = aspath_count_hops (new->attr->aspath); - aspath_hops += aspath_count_confeds (new->attr->aspath); + aspath_hops = aspath_count_hops (newattr->aspath); + aspath_hops += aspath_count_confeds (newattr->aspath); if ( aspath_hops < (exist_hops + exist_confeds)) - return 1; + return -1; if ( aspath_hops > (exist_hops + exist_confeds)) - return 0; + return 1; } else { - int newhops = aspath_count_hops (new->attr->aspath); + int newhops = aspath_count_hops (newattr->aspath); if (newhops < exist_hops) - return 1; + return -1; if (newhops > exist_hops) - return 0; + return 1; } } /* 5. Origin check. */ - if (new->attr->origin < exist->attr->origin) + if (newattr->origin < existattr->origin) + return -1; + if (newattr->origin > existattr->origin) return 1; - if (new->attr->origin > exist->attr->origin) - return 0; /* 6. MED check. */ - internal_as_route = (aspath_count_hops (new->attr->aspath) == 0 - && aspath_count_hops (exist->attr->aspath) == 0); - confed_as_route = (aspath_count_confeds (new->attr->aspath) > 0 - && aspath_count_confeds (exist->attr->aspath) > 0 - && aspath_count_hops (new->attr->aspath) == 0 - && aspath_count_hops (exist->attr->aspath) == 0); + internal_as_route = (aspath_count_hops (newattr->aspath) == 0 + && aspath_count_hops (existattr->aspath) == 0); + confed_as_route = (aspath_count_confeds (newattr->aspath) > 0 + && aspath_count_confeds (existattr->aspath) > 0 + && aspath_count_hops (newattr->aspath) == 0 + && aspath_count_hops (existattr->aspath) == 0); if (bgp_flag_check (bgp, BGP_FLAG_ALWAYS_COMPARE_MED) || (bgp_flag_check (bgp, BGP_FLAG_MED_CONFED) && confed_as_route) - || aspath_cmp_left (new->attr->aspath, exist->attr->aspath) - || aspath_cmp_left_confed (new->attr->aspath, exist->attr->aspath) + || aspath_cmp_left (newattr->aspath, existattr->aspath) + || aspath_cmp_left_confed (newattr->aspath, existattr->aspath) || internal_as_route) { new_med = bgp_med_value (new->attr, bgp); exist_med = bgp_med_value (exist->attr, bgp); if (new_med < exist_med) - return 1; + return -1; if (new_med > exist_med) - return 0; + return 1; } /* 7. Peer type check. */ - if (peer_sort (new->peer) == BGP_PEER_EBGP - && peer_sort (exist->peer) == BGP_PEER_IBGP) - return 1; - if (peer_sort (new->peer) == BGP_PEER_EBGP - && peer_sort (exist->peer) == BGP_PEER_CONFED) + new_sort = new->peer->sort; + exist_sort = exist->peer->sort; + + if (new_sort == BGP_PEER_EBGP + && (exist_sort == BGP_PEER_IBGP || exist_sort == BGP_PEER_CONFED)) + return -1; + if (exist_sort == BGP_PEER_EBGP + && (new_sort == BGP_PEER_IBGP || new_sort == BGP_PEER_CONFED)) return 1; - if (peer_sort (new->peer) == BGP_PEER_IBGP - && peer_sort (exist->peer) == BGP_PEER_EBGP) - return 0; - if (peer_sort (new->peer) == BGP_PEER_CONFED - && peer_sort (exist->peer) == BGP_PEER_EBGP) - return 0; /* 8. IGP metric check. */ - if (new->extra || exist->extra) + newm = existm = 0; + + if (new->extra) + newm = new->extra->igpmetric; + if (exist->extra) + existm = exist->extra->igpmetric; + + if (newm < existm) + return -1; + if (newm > existm) + return 1; + + /* 9. Maximum path check. */ + if (bgp_mpath_is_configured (bgp, afi, safi)) { - uint32_t newm = (new->extra ? new->extra->igpmetric : 0); - uint32_t existm = (exist->extra ? exist->extra->igpmetric : 0); - - if (newm < existm) - return 1; - if (newm > existm) + 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. + */ + return 0; + } + else if (new->peer->sort == BGP_PEER_IBGP) + { + if (aspath_cmp (new->attr->aspath, exist->attr->aspath)) + return 0; + } + else if (new->peer->as == exist->peer->as) return 0; } - /* 9. Maximum path check. */ - /* 10. If both paths are external, prefer the path that was received first (the oldest one). This step minimizes route-flap, since a newer path won't displace an older one, even if it was the preferred route based on the additional decision criteria below. */ if (! bgp_flag_check (bgp, BGP_FLAG_COMPARE_ROUTER_ID) - && peer_sort (new->peer) == BGP_PEER_EBGP - && peer_sort (exist->peer) == BGP_PEER_EBGP) + && new_sort == BGP_PEER_EBGP + && exist_sort == BGP_PEER_EBGP) { if (CHECK_FLAG (new->flags, BGP_INFO_SELECTED)) - return 1; + return -1; if (CHECK_FLAG (exist->flags, BGP_INFO_SELECTED)) - return 0; + return 1; } - /* 11. Rourter-ID comparision. */ - if (new->attr->flag & ATTR_FLAG_BIT(BGP_ATTR_ORIGINATOR_ID)) - new_id.s_addr = new->attr->extra->originator_id.s_addr; + /* 11. Router-ID comparision. */ + /* If one of the paths is "stale", the corresponding peer router-id will + * be 0 and would always win over the other path. If originator id is + * used for the comparision, it will decide which path is better. + */ + if (newattr->flag & ATTR_FLAG_BIT(BGP_ATTR_ORIGINATOR_ID)) + new_id.s_addr = newattre->originator_id.s_addr; else new_id.s_addr = new->peer->remote_id.s_addr; - if (exist->attr->flag & ATTR_FLAG_BIT(BGP_ATTR_ORIGINATOR_ID)) - exist_id.s_addr = exist->attr->extra->originator_id.s_addr; + if (existattr->flag & ATTR_FLAG_BIT(BGP_ATTR_ORIGINATOR_ID)) + exist_id.s_addr = existattre->originator_id.s_addr; else exist_id.s_addr = exist->peer->remote_id.s_addr; if (ntohl (new_id.s_addr) < ntohl (exist_id.s_addr)) - return 1; + return -1; if (ntohl (new_id.s_addr) > ntohl (exist_id.s_addr)) - return 0; + return 1; /* 12. Cluster length comparision. */ - if (new->attr->flag & ATTR_FLAG_BIT(BGP_ATTR_CLUSTER_LIST)) - new_cluster = new->attr->extra->cluster->length; - else - new_cluster = 0; - if (exist->attr->flag & ATTR_FLAG_BIT(BGP_ATTR_CLUSTER_LIST)) - exist_cluster = exist->attr->extra->cluster->length; - else - exist_cluster = 0; + new_cluster = exist_cluster = 0; + + if (newattr->flag & ATTR_FLAG_BIT(BGP_ATTR_CLUSTER_LIST)) + new_cluster = newattre->cluster->length; + if (existattr->flag & ATTR_FLAG_BIT(BGP_ATTR_CLUSTER_LIST)) + exist_cluster = existattre->cluster->length; if (new_cluster < exist_cluster) - return 1; + return -1; if (new_cluster > exist_cluster) - return 0; + return 1; /* 13. Neighbor address comparision. */ + /* Do this only if neither path is "stale" as stale paths do not have + * valid peer information (as the connection may or may not be up). + */ + if (CHECK_FLAG (exist->flags, BGP_INFO_STALE)) + return -1; + if (CHECK_FLAG (new->flags, BGP_INFO_STALE)) + return 1; + /* locally configured routes to advertise do not have su_remote */ + if (new->peer->su_remote == NULL) + return 1; + if (exist->peer->su_remote == NULL) + return -1; + ret = sockunion_cmp (new->peer->su_remote, exist->peer->su_remote); if (ret == 1) - return 0; - if (ret == -1) return 1; + if (ret == -1) + return -1; - return 1; + return -1; } static enum filter_type @@ -611,13 +657,13 @@ bgp_community_filter (struct peer *peer, struct attr *attr) return 1; /* NO_EXPORT check. */ - if (peer_sort (peer) == BGP_PEER_EBGP && + if (peer->sort == BGP_PEER_EBGP && community_include (attr->community, COMMUNITY_NO_EXPORT)) return 1; /* NO_EXPORT_SUBCONFED check. */ - if (peer_sort (peer) == BGP_PEER_EBGP - || peer_sort (peer) == BGP_PEER_CONFED) + if (peer->sort == BGP_PEER_EBGP + || peer->sort == BGP_PEER_CONFED) if (community_include (attr->community, COMMUNITY_NO_EXPORT_SUBCONFED)) return 1; } @@ -642,7 +688,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) @@ -672,15 +718,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) @@ -752,7 +795,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) @@ -764,10 +807,12 @@ bgp_announce_check (struct bgp_info *ri, struct peer *peer, struct prefix *p, struct bgp *bgp; int transparent; int reflect; + struct attr *riattr; from = ri->peer; filter = &peer->filter[afi][safi]; bgp = peer->bgp; + riattr = bgp_info_mpath_count (ri) ? bgp_info_mpath_attr (ri) : ri->attr; if (DISABLE_BGP_ANNOUNCE) return 0; @@ -780,16 +825,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, &ri->attr->nexthop)) - return 0; -#ifdef HAVE_IPV6 - if (p->family == AF_INET6 - && IPV6_ADDR_SAME(&peer->remote_id, &ri->attr->nexthop)) - return 0; -#endif - /* Aggregate-address suppress check. */ if (ri->extra && ri->extra->suppress) if (! UNSUPPRESS_MAP_NAME (filter)) @@ -800,10 +835,8 @@ bgp_announce_check (struct bgp_info *ri, struct peer *peer, struct prefix *p, { if (p->family == AF_INET && p->u.prefix4.s_addr == INADDR_ANY) return 0; -#ifdef HAVE_IPV6 else if (p->family == AF_INET6 && p->prefixlen == 0) return 0; -#endif /* HAVE_IPV6 */ } /* Transparency check. */ @@ -814,14 +847,14 @@ bgp_announce_check (struct bgp_info *ri, struct peer *peer, struct prefix *p, transparent = 0; /* If community is not disabled check the no-export and local. */ - if (! transparent && bgp_community_filter (peer, ri->attr)) + if (! transparent && bgp_community_filter (peer, riattr)) return 0; /* If the attribute has originator-id and it is same as remote peer's id. */ - if (ri->attr->flag & ATTR_FLAG_BIT (BGP_ATTR_ORIGINATOR_ID)) + if (riattr->flag & ATTR_FLAG_BIT (BGP_ATTR_ORIGINATOR_ID)) { - if (IPV4_ADDR_SAME (&peer->remote_id, &ri->attr->extra->originator_id)) + if (IPV4_ADDR_SAME (&peer->remote_id, &riattr->extra->originator_id)) { if (BGP_DEBUG (filter, FILTER)) zlog (peer->log, LOG_DEBUG, @@ -844,7 +877,7 @@ bgp_announce_check (struct bgp_info *ri, struct peer *peer, struct prefix *p, } /* Output filter check. */ - if (bgp_output_filter (peer, p, ri->attr, afi, safi) == FILTER_DENY) + if (bgp_output_filter (peer, p, riattr, afi, safi) == FILTER_DENY) { if (BGP_DEBUG (filter, FILTER)) zlog (peer->log, LOG_DEBUG, @@ -857,7 +890,7 @@ bgp_announce_check (struct bgp_info *ri, struct peer *peer, struct prefix *p, #ifdef BGP_SEND_ASPATH_CHECK /* AS path loop check. */ - if (aspath_loop_check (ri->attr->aspath, peer->as)) + if (aspath_loop_check (riattr->aspath, peer->as)) { if (BGP_DEBUG (filter, FILTER)) zlog (peer->log, LOG_DEBUG, @@ -865,12 +898,11 @@ bgp_announce_check (struct bgp_info *ri, struct peer *peer, struct prefix *p, peer->host, peer->as); return 0; } -#endif /* BGP_SEND_ASPATH_CHECK */ /* If we're a CONFED we need to loop check the CONFED ID too */ if (CHECK_FLAG(bgp->config, BGP_CONFIG_CONFEDERATION)) { - if (aspath_loop_check(ri->attr->aspath, bgp->confed_id)) + if (aspath_loop_check(riattr->aspath, bgp->confed_id)) { if (BGP_DEBUG (filter, FILTER)) zlog (peer->log, LOG_DEBUG, @@ -880,9 +912,10 @@ bgp_announce_check (struct bgp_info *ri, struct peer *peer, struct prefix *p, return 0; } } +#endif /* BGP_SEND_ASPATH_CHECK */ /* Route-Reflect check. */ - if (peer_sort (from) == BGP_PEER_IBGP && peer_sort (peer) == BGP_PEER_IBGP) + if (from->sort == BGP_PEER_IBGP && peer->sort == BGP_PEER_IBGP) reflect = 1; else reflect = 0; @@ -911,19 +944,30 @@ bgp_announce_check (struct bgp_info *ri, struct peer *peer, struct prefix *p, } /* For modify attribute, copy it to temporary structure. */ - bgp_attr_dup (attr, ri->attr); + bgp_attr_dup (attr, riattr); /* If local-preference is not set. */ - if ((peer_sort (peer) == BGP_PEER_IBGP - || peer_sort (peer) == BGP_PEER_CONFED) + if ((peer->sort == BGP_PEER_IBGP + || peer->sort == BGP_PEER_CONFED) && (! (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_LOCAL_PREF)))) { attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_LOCAL_PREF); 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 (peer) == BGP_PEER_EBGP + if (peer->sort == BGP_PEER_EBGP && attr->flag & ATTR_FLAG_BIT (BGP_ATTR_MULTI_EXIT_DISC)) { if (ri->peer != bgp->peer_self && ! transparent @@ -931,50 +975,53 @@ bgp_announce_check (struct bgp_info *ri, struct peer *peer, struct prefix *p, attr->flag &= ~(ATTR_FLAG_BIT (BGP_ATTR_MULTI_EXIT_DISC)); } + +#define NEXTHOP_IS_V4 (\ + (safi != SAFI_ENCAP && p->family == AF_INET) || \ + (safi == SAFI_ENCAP && attr->extra->mp_nexthop_len == 4)) + +#define NEXTHOP_IS_V6 (\ + (safi != SAFI_ENCAP && p->family == AF_INET6) || \ + (safi == SAFI_ENCAP && attr->extra->mp_nexthop_len == 16)) + /* 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 - || (p->family == AF_INET6 && + && ((NEXTHOP_IS_V4 && attr->nexthop.s_addr) + || (NEXTHOP_IS_V6 && ! IN6_IS_ADDR_UNSPECIFIED(&attr->extra->mp_nexthop_global)) -#endif /* HAVE_IPV6 */ ))) { /* NEXT-HOP Unchanged. */ } else if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_NEXTHOP_SELF) - || (p->family == AF_INET && attr->nexthop.s_addr == 0) -#ifdef HAVE_IPV6 - || (p->family == AF_INET6 && + || (NEXTHOP_IS_V4 && attr->nexthop.s_addr == 0) + || (NEXTHOP_IS_V6 && IN6_IS_ADDR_UNSPECIFIED(&attr->extra->mp_nexthop_global)) -#endif /* HAVE_IPV6 */ - || (peer_sort (peer) == BGP_PEER_EBGP - && bgp_multiaccess_check_v4 (attr->nexthop, peer->host) == 0)) + || (peer->sort == BGP_PEER_EBGP + && (bgp_multiaccess_check_v4 (attr->nexthop, peer) == 0))) { /* Set IPv4 nexthop. */ - if (p->family == AF_INET) + if (NEXTHOP_IS_V4) { - if (safi == SAFI_MPLS_VPN) + if ((safi == SAFI_MPLS_VPN) || (safi == SAFI_ENCAP)) memcpy (&attr->extra->mp_nexthop_global_in, &peer->nexthop.v4, IPV4_MAX_BYTELEN); else memcpy (&attr->nexthop, &peer->nexthop.v4, IPV4_MAX_BYTELEN); } -#ifdef HAVE_IPV6 /* Set IPv6 nexthop. */ - if (p->family == AF_INET6) + if (NEXTHOP_IS_V6) { /* IPv6 global nexthop must be included. */ memcpy (&attr->extra->mp_nexthop_global, &peer->nexthop.v6_global, IPV6_MAX_BYTELEN); attr->extra->mp_nexthop_len = 16; } -#endif /* HAVE_IPV6 */ } -#ifdef HAVE_IPV6 - if (p->family == AF_INET6) + if (p->family == AF_INET6 && safi != SAFI_ENCAP) { /* Left nexthop_local unchanged if so configured. */ if ( CHECK_FLAG (peer->af_flags[afi][safi], @@ -1012,10 +1059,9 @@ bgp_announce_check (struct bgp_info *ri, struct peer *peer, struct prefix *p, } } -#endif /* HAVE_IPV6 */ /* If this is EBGP peer and remove-private-AS is set. */ - if (peer_sort (peer) == BGP_PEER_EBGP + if (peer->sort == BGP_PEER_EBGP && peer_af_flag_check (peer, afi, safi, PEER_FLAG_REMOVE_PRIVATE_AS) && aspath_private_as_check (attr->aspath)) attr->aspath = aspath_empty_get (); @@ -1025,15 +1071,18 @@ bgp_announce_check (struct bgp_info *ri, struct peer *peer, struct prefix *p, || (ri->extra && ri->extra->suppress) ) { struct bgp_info info; - struct attr dummy_attr = { 0 }; - + struct attr dummy_attr; + struct attr_extra dummy_extra; + + dummy_attr.extra = &dummy_extra; + info.peer = peer; info.attr = attr; /* The route reflector is not allowed to modify the attributes - of the reflected IBGP routes. */ - if (peer_sort (from) == BGP_PEER_IBGP - && peer_sort (peer) == BGP_PEER_IBGP) + of the reflected IBGP routes, unless configured to allow it */ + if ((from->sort == BGP_PEER_IBGP && peer->sort == BGP_PEER_IBGP) && + !bgp_flag_check(bgp, BGP_FLAG_RR_ALLOW_OUTBOUND_POLICY)) { bgp_attr_dup (&dummy_attr, attr); info.attr = &dummy_attr; @@ -1047,10 +1096,7 @@ bgp_announce_check (struct bgp_info *ri, struct peer *peer, struct prefix *p, ret = route_map_apply (ROUTE_MAP_OUT (filter), p, RMAP_BGP, &info); peer->rmap_type = 0; - - if (dummy_attr.extra) - bgp_attr_extra_free (&dummy_attr); - + if (ret == RMAP_DENYMATCH) { bgp_attr_flush (attr); @@ -1069,9 +1115,11 @@ bgp_announce_check_rsclient (struct bgp_info *ri, struct peer *rsclient, struct bgp_filter *filter; struct bgp_info info; struct peer *from; + struct attr *riattr; from = ri->peer; filter = &rsclient->filter[afi][safi]; + riattr = bgp_info_mpath_count (ri) ? bgp_info_mpath_attr (ri) : ri->attr; if (DISABLE_BGP_ANNOUNCE) return 0; @@ -1091,18 +1139,16 @@ bgp_announce_check_rsclient (struct bgp_info *ri, struct peer *rsclient, { if (p->family == AF_INET && p->u.prefix4.s_addr == INADDR_ANY) return 0; -#ifdef HAVE_IPV6 else if (p->family == AF_INET6 && p->prefixlen == 0) return 0; -#endif /* HAVE_IPV6 */ } /* If the attribute has originator-id and it is same as remote peer's id. */ - if (ri->attr->flag & ATTR_FLAG_BIT (BGP_ATTR_ORIGINATOR_ID)) + if (riattr->flag & ATTR_FLAG_BIT (BGP_ATTR_ORIGINATOR_ID)) { if (IPV4_ADDR_SAME (&rsclient->remote_id, - &ri->attr->extra->originator_id)) + &riattr->extra->originator_id)) { if (BGP_DEBUG (filter, FILTER)) zlog (rsclient->log, LOG_DEBUG, @@ -1125,7 +1171,7 @@ bgp_announce_check_rsclient (struct bgp_info *ri, struct peer *rsclient, } /* Output filter check. */ - if (bgp_output_filter (rsclient, p, ri->attr, afi, safi) == FILTER_DENY) + if (bgp_output_filter (rsclient, p, riattr, afi, safi) == FILTER_DENY) { if (BGP_DEBUG (filter, FILTER)) zlog (rsclient->log, LOG_DEBUG, @@ -1138,7 +1184,7 @@ bgp_announce_check_rsclient (struct bgp_info *ri, struct peer *rsclient, #ifdef BGP_SEND_ASPATH_CHECK /* AS path loop check. */ - if (aspath_loop_check (ri->attr->aspath, rsclient->as)) + if (aspath_loop_check (riattr->aspath, rsclient->as)) { if (BGP_DEBUG (filter, FILTER)) zlog (rsclient->log, LOG_DEBUG, @@ -1149,26 +1195,23 @@ bgp_announce_check_rsclient (struct bgp_info *ri, struct peer *rsclient, #endif /* BGP_SEND_ASPATH_CHECK */ /* For modify attribute, copy it to temporary structure. */ - bgp_attr_dup (attr, ri->attr); + bgp_attr_dup (attr, riattr); /* next-hop-set */ if ((p->family == AF_INET && attr->nexthop.s_addr == 0) -#ifdef HAVE_IPV6 || (p->family == AF_INET6 && IN6_IS_ADDR_UNSPECIFIED(&attr->extra->mp_nexthop_global)) -#endif /* HAVE_IPV6 */ ) { /* Set IPv4 nexthop. */ if (p->family == AF_INET) { - if (safi == SAFI_MPLS_VPN) + if ((safi == SAFI_MPLS_VPN) || (safi == SAFI_ENCAP)) memcpy (&attr->extra->mp_nexthop_global_in, &rsclient->nexthop.v4, IPV4_MAX_BYTELEN); else memcpy (&attr->nexthop, &rsclient->nexthop.v4, IPV4_MAX_BYTELEN); } -#ifdef HAVE_IPV6 /* Set IPv6 nexthop. */ if (p->family == AF_INET6) { @@ -1177,16 +1220,12 @@ bgp_announce_check_rsclient (struct bgp_info *ri, struct peer *rsclient, IPV6_MAX_BYTELEN); attr->extra->mp_nexthop_len = 16; } -#endif /* HAVE_IPV6 */ } -#ifdef HAVE_IPV6 if (p->family == AF_INET6) { struct attr_extra *attre = attr->extra; - - assert (attr->extra); - + /* Left nexthop_local unchanged if so configured. */ if ( CHECK_FLAG (rsclient->af_flags[afi][safi], PEER_FLAG_NEXTHOP_LOCAL_UNCHANGED) ) @@ -1224,11 +1263,9 @@ bgp_announce_check_rsclient (struct bgp_info *ri, struct peer *rsclient, } } -#endif /* HAVE_IPV6 */ - /* If this is EBGP peer and remove-private-AS is set. */ - if (peer_sort (rsclient) == BGP_PEER_EBGP + if (rsclient->sort == BGP_PEER_EBGP && peer_af_flag_check (rsclient, afi, safi, PEER_FLAG_REMOVE_PRIVATE_AS) && aspath_private_as_check (attr->aspath)) attr->aspath = aspath_empty_get (); @@ -1265,7 +1302,9 @@ struct bgp_info_pair }; static void -bgp_best_selection (struct bgp *bgp, struct bgp_node *rn, struct bgp_info_pair *result) +bgp_best_selection (struct bgp *bgp, struct bgp_node *rn, + struct bgp_info_pair *result, + afi_t afi, safi_t safi) { struct bgp_info *new_select; struct bgp_info *old_select; @@ -1273,7 +1312,23 @@ bgp_best_selection (struct bgp *bgp, struct bgp_node *rn, struct bgp_info_pair * struct bgp_info *ri1; struct bgp_info *ri2; struct bgp_info *nextri = NULL; + int cmpret, do_mpath; + struct list mp_list; + + result->old = result->new = NULL; + + if (rn->info == NULL) + { + char buf[PREFIX_STRLEN]; + zlog_warn ("%s: Called for route_node %s with no routing entries!", + __func__, + prefix2str (&(bgp_node_to_rnode (rn)->p), buf, sizeof(buf))); + return; + } + bgp_mp_list_init (&mp_list); + do_mpath = bgp_mpath_is_configured (bgp, afi, safi); + /* bgp deterministic-med */ new_select = NULL; if (bgp_flag_check (bgp, BGP_FLAG_DETERMINISTIC_MED)) @@ -1283,8 +1338,14 @@ bgp_best_selection (struct bgp *bgp, struct bgp_node *rn, struct bgp_info_pair * continue; if (BGP_INFO_HOLDDOWN (ri1)) continue; + if (ri1->peer && ri1->peer != bgp->peer_self) + if (ri1->peer->status != Established) + continue; new_select = ri1; + if (do_mpath) + bgp_mp_list_add (&mp_list, ri1); + old_select = CHECK_FLAG (ri1->flags, BGP_INFO_SELECTED) ? ri1 : NULL; if (ri1->next) for (ri2 = ri1->next; ri2; ri2 = ri2->next) { @@ -1292,22 +1353,42 @@ bgp_best_selection (struct bgp *bgp, struct bgp_node *rn, struct bgp_info_pair * continue; if (BGP_INFO_HOLDDOWN (ri2)) continue; + if (ri2->peer && + ri2->peer != bgp->peer_self && + !CHECK_FLAG (ri2->peer->sflags, PEER_STATUS_NSF_WAIT)) + if (ri2->peer->status != Established) + continue; if (aspath_cmp_left (ri1->attr->aspath, ri2->attr->aspath) || aspath_cmp_left_confed (ri1->attr->aspath, ri2->attr->aspath)) { - if (bgp_info_cmp (bgp, ri2, new_select)) + if (CHECK_FLAG (ri2->flags, BGP_INFO_SELECTED)) + old_select = ri2; + if ((cmpret = bgp_info_cmp (bgp, ri2, new_select, afi, safi)) + == -1) { bgp_info_unset_flag (rn, new_select, BGP_INFO_DMED_SELECTED); new_select = ri2; } + if (do_mpath) + { + if (cmpret != 0) + bgp_mp_list_clear (&mp_list); + + if (cmpret == 0 || cmpret == -1) + bgp_mp_list_add (&mp_list, ri2); + } + bgp_info_set_flag (rn, ri2, BGP_INFO_DMED_CHECK); } } bgp_info_set_flag (rn, new_select, BGP_INFO_DMED_CHECK); bgp_info_set_flag (rn, new_select, BGP_INFO_DMED_SELECTED); + + bgp_info_mpath_update (rn, new_select, old_select, &mp_list, afi, safi); + bgp_mp_list_clear (&mp_list); } /* Check old selected route and new selected route. */ @@ -1330,6 +1411,12 @@ bgp_best_selection (struct bgp *bgp, struct bgp_node *rn, struct bgp_info_pair * continue; } + if (ri->peer && + ri->peer != bgp->peer_self && + !CHECK_FLAG (ri->peer->sflags, PEER_STATUS_NSF_WAIT)) + if (ri->peer->status != Established) + continue; + if (bgp_flag_check (bgp, BGP_FLAG_DETERMINISTIC_MED) && (! CHECK_FLAG (ri->flags, BGP_INFO_DMED_SELECTED))) { @@ -1339,14 +1426,37 @@ bgp_best_selection (struct bgp *bgp, struct bgp_node *rn, struct bgp_info_pair * bgp_info_unset_flag (rn, ri, BGP_INFO_DMED_CHECK); bgp_info_unset_flag (rn, ri, BGP_INFO_DMED_SELECTED); - if (bgp_info_cmp (bgp, ri, new_select)) - new_select = ri; + if ((cmpret = bgp_info_cmp (bgp, ri, new_select, afi, safi)) == -1) + { + if (do_mpath && bgp_flag_check (bgp, BGP_FLAG_DETERMINISTIC_MED)) + bgp_mp_dmed_deselect (new_select); + + new_select = ri; + } + else if (cmpret == 1 && do_mpath + && bgp_flag_check (bgp, BGP_FLAG_DETERMINISTIC_MED)) + bgp_mp_dmed_deselect (ri); + + if (do_mpath) + { + if (cmpret != 0) + bgp_mp_list_clear (&mp_list); + + if (cmpret == 0 || cmpret == -1) + bgp_mp_list_add (&mp_list, ri); + } } - - result->old = old_select; - result->new = new_select; - return; + if (!bgp_flag_check (bgp, BGP_FLAG_DETERMINISTIC_MED)) + bgp_info_mpath_update (rn, new_select, old_select, &mp_list, afi, safi); + + bgp_info_mpath_aggregate_update (new_select, old_select); + bgp_mp_list_clear (&mp_list); + + result->old = old_select; + result->new = new_select; + + return; } static int @@ -1354,7 +1464,11 @@ bgp_process_announce_selected (struct peer *peer, struct bgp_info *selected, struct bgp_node *rn, afi_t afi, safi_t safi) { struct prefix *p; - struct attr attr = { 0 }; + struct attr attr; + struct attr_extra extra; + + memset (&attr, 0, sizeof(struct attr)); + memset (&extra, 0, sizeof(struct attr_extra)); p = &rn->p; @@ -1371,7 +1485,10 @@ bgp_process_announce_selected (struct peer *peer, struct bgp_info *selected, PEER_STATUS_ORF_WAIT_REFRESH)) return 0; - switch (rn->table->type) + /* It's initialized in bgp_announce_[check|check_rsclient]() */ + attr.extra = &extra; + + switch (bgp_node_table (rn)->type) { case BGP_TABLE_MAIN: /* Announcement to peer->conf. If the route is filtered, @@ -1391,9 +1508,8 @@ bgp_process_announce_selected (struct peer *peer, struct bgp_info *selected, bgp_adj_out_unset (rn, peer, p, afi, safi); break; } - - bgp_attr_extra_free (&attr); - + + bgp_attr_flush (&attr); return 0; } @@ -1417,10 +1533,10 @@ bgp_process_rsclient (struct work_queue *wq, void *data) struct bgp_info *old_select; struct bgp_info_pair old_and_new; struct listnode *node, *nnode; - struct peer *rsclient = rn->table->owner; + struct peer *rsclient = bgp_node_table (rn)->owner; /* Best path selection. */ - bgp_best_selection (bgp, rn, &old_and_new); + bgp_best_selection (bgp, rn, &old_and_new, afi, safi); new_select = old_and_new.new; old_select = old_and_new.old; @@ -1440,7 +1556,8 @@ bgp_process_rsclient (struct work_queue *wq, void *data) { bgp_info_set_flag (rn, new_select, BGP_INFO_SELECTED); bgp_info_unset_flag (rn, new_select, BGP_INFO_ATTR_CHANGED); - } + UNSET_FLAG (new_select->flags, BGP_INFO_MULTIPATH_CHG); + } bgp_process_announce_selected (rsclient, new_select, rn, afi, safi); @@ -1454,6 +1571,7 @@ bgp_process_rsclient (struct work_queue *wq, void *data) { bgp_info_set_flag (rn, new_select, BGP_INFO_SELECTED); bgp_info_unset_flag (rn, new_select, BGP_INFO_ATTR_CHANGED); + UNSET_FLAG (new_select->flags, BGP_INFO_MULTIPATH_CHG); } bgp_process_announce_selected (rsclient, new_select, rn, afi, safi); } @@ -1481,29 +1599,36 @@ bgp_process_main (struct work_queue *wq, void *data) struct peer *peer; /* Best path selection. */ - bgp_best_selection (bgp, rn, &old_and_new); + bgp_best_selection (bgp, rn, &old_and_new, afi, safi); old_select = old_and_new.old; new_select = old_and_new.new; /* Nothing to do. */ - if (old_select && old_select == new_select) + if (old_select && old_select == new_select + && !CHECK_FLAG(rn->flags, BGP_NODE_USER_CLEAR)) { if (! CHECK_FLAG (old_select->flags, BGP_INFO_ATTR_CHANGED)) { - if (CHECK_FLAG (old_select->flags, BGP_INFO_IGP_CHANGED)) + if (CHECK_FLAG (old_select->flags, BGP_INFO_IGP_CHANGED) || + CHECK_FLAG (old_select->flags, BGP_INFO_MULTIPATH_CHG)) bgp_zebra_announce (p, old_select, bgp, safi); + UNSET_FLAG (old_select->flags, BGP_INFO_MULTIPATH_CHG); UNSET_FLAG (rn->flags, BGP_NODE_PROCESS_SCHEDULED); return WQ_SUCCESS; } } + /* If the user did "clear ip bgp prefix x.x.x.x" this flag will be set */ + UNSET_FLAG(rn->flags, BGP_NODE_USER_CLEAR); + if (old_select) bgp_info_unset_flag (rn, old_select, BGP_INFO_SELECTED); if (new_select) { bgp_info_set_flag (rn, new_select, BGP_INFO_SELECTED); bgp_info_unset_flag (rn, new_select, BGP_INFO_ATTR_CHANGED); + UNSET_FLAG (new_select->flags, BGP_INFO_MULTIPATH_CHG); } @@ -1531,7 +1656,7 @@ bgp_process_main (struct work_queue *wq, void *data) } } - /* Reap old select bgp_info, it it has been removed */ + /* Reap old select bgp_info, if it has been removed */ if (old_select && CHECK_FLAG (old_select->flags, BGP_INFO_REMOVED)) bgp_info_reap (rn, old_select); @@ -1543,7 +1668,7 @@ static void bgp_processq_del (struct work_queue *wq, void *data) { struct bgp_process_queue *pq = data; - struct bgp_table *table = pq->rn->table; + struct bgp_table *table = bgp_node_table (pq->rn); bgp_unlock (pq->bgp); bgp_unlock_node (pq->rn); @@ -1570,9 +1695,10 @@ bgp_process_queue_init (void) bm->process_main_queue->spec.max_retries = 0; bm->process_main_queue->spec.hold = 50; - memcpy (bm->process_rsclient_queue, bm->process_main_queue, - sizeof (struct work_queue *)); bm->process_rsclient_queue->spec.workfunc = &bgp_process_rsclient; + bm->process_rsclient_queue->spec.del_item_data = &bgp_processq_del; + bm->process_rsclient_queue->spec.max_retries = 0; + bm->process_rsclient_queue->spec.hold = 50; } void @@ -1584,6 +1710,19 @@ bgp_process (struct bgp *bgp, struct bgp_node *rn, afi_t afi, safi_t safi) if (CHECK_FLAG (rn->flags, BGP_NODE_PROCESS_SCHEDULED)) return; + if (rn->info == NULL) + { + /* XXX: Perhaps remove before next release, after we've flushed out + * any obvious cases + */ + assert (rn->info != NULL); + char buf[PREFIX_STRLEN]; + zlog_warn ("%s: Called for route_node %s with no routing entries!", + __func__, + prefix2str (&(bgp_node_to_rnode (rn)->p), buf, sizeof(buf))); + return; + } + if ( (bm->process_main_queue == NULL) || (bm->process_rsclient_queue == NULL) ) bgp_process_queue_init (); @@ -1594,14 +1733,14 @@ bgp_process (struct bgp *bgp, struct bgp_node *rn, afi_t afi, safi_t safi) return; /* all unlocked in bgp_processq_del */ - bgp_table_lock (rn->table); + bgp_table_lock (bgp_node_table (rn)); pqnode->rn = bgp_lock_node (rn); pqnode->bgp = bgp; bgp_lock (bgp); pqnode->afi = afi; pqnode->safi = safi; - switch (rn->table->type) + switch (bgp_node_table (rn)->type) { case BGP_TABLE_MAIN: work_queue_add (bm->process_main_queue, pqnode); @@ -1611,6 +1750,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; } @@ -1724,7 +1864,7 @@ bgp_rib_remove (struct bgp_node *rn, struct bgp_info *ri, struct peer *peer, static void bgp_rib_withdraw (struct bgp_node *rn, struct bgp_info *ri, struct peer *peer, - afi_t afi, safi_t safi) + afi_t afi, safi_t safi, struct prefix_rd *prd) { int status = BGP_DAMP_NONE; @@ -1732,7 +1872,7 @@ bgp_rib_withdraw (struct bgp_node *rn, struct bgp_info *ri, struct peer *peer, * the bgp_info in the RIB for historical reference. */ if (CHECK_FLAG (peer->bgp->af_flags[afi][safi], BGP_CONFIG_DAMPENING) - && peer_sort (peer) == BGP_PEER_EBGP) + && peer->sort == BGP_PEER_EBGP) if ( (status = bgp_damp_withdraw (ri, rn, afi, safi, 0)) == BGP_DAMP_SUPPRESSED) { @@ -1743,6 +1883,23 @@ bgp_rib_withdraw (struct bgp_node *rn, struct bgp_info *ri, struct peer *peer, bgp_rib_remove (rn, ri, peer, afi, safi); } +static struct bgp_info * +info_make (int type, int sub_type, struct peer *peer, struct attr *attr, + struct bgp_node *rn) +{ + struct bgp_info *new; + + /* Make new BGP info. */ + new = XCALLOC (MTYPE_BGP_ROUTE, sizeof (struct bgp_info)); + new->type = type; + new->sub_type = sub_type; + new->peer = peer; + new->attr = attr; + new->uptime = bgp_clock (); + new->net = rn; + return new; +} + static void bgp_update_rsclient (struct peer *rsclient, afi_t afi, safi_t safi, struct attr *attr, struct peer *peer, struct prefix *p, int type, @@ -1750,7 +1907,8 @@ bgp_update_rsclient (struct peer *rsclient, afi_t afi, safi_t safi, { struct bgp_node *rn; struct bgp *bgp; - struct attr new_attr = { 0 }; + struct attr new_attr; + struct attr_extra new_extra; struct attr *attr_new; struct attr *attr_new2; struct bgp_info *ri; @@ -1771,7 +1929,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; @@ -1785,6 +1943,7 @@ bgp_update_rsclient (struct peer *rsclient, afi_t afi, safi_t safi, goto filtered; } + new_attr.extra = &new_extra; bgp_attr_dup (&new_attr, attr); /* Apply export policy. */ @@ -1822,10 +1981,7 @@ bgp_update_rsclient (struct peer *rsclient, afi_t afi, safi_t safi, goto filtered; } } - - /* new_attr isn't passed to any functions after here */ - bgp_attr_extra_free (&new_attr); - + /* If the update is implicit withdraw. */ if (ri) { @@ -1836,7 +1992,6 @@ bgp_update_rsclient (struct peer *rsclient, afi_t afi, safi_t safi, && attrhash_cmp (ri->attr, attr_new)) { - bgp_info_unset_flag (rn, ri, BGP_INFO_ATTR_CHANGED); if (BGP_DEBUG (update, UPDATE_IN)) zlog (peer->log, LOG_DEBUG, @@ -1847,7 +2002,7 @@ bgp_update_rsclient (struct peer *rsclient, afi_t afi, safi_t safi, bgp_unlock_node (rn); bgp_attr_unintern (&attr_new); - + bgp_attr_flush(&new_attr); return; } @@ -1891,13 +2046,7 @@ bgp_update_rsclient (struct peer *rsclient, afi_t afi, safi_t safi, p->prefixlen, rsclient->host); } - /* Make new BGP info. */ - new = bgp_info_new (); - new->type = type; - new->sub_type = sub_type; - new->peer = peer; - new->attr = attr_new; - new->uptime = bgp_clock (); + new = info_make(type, sub_type, peer, attr_new, rn); /* Update MPLS tag. */ if (safi == SAFI_MPLS_VPN) @@ -1913,9 +2062,7 @@ bgp_update_rsclient (struct peer *rsclient, afi_t afi, safi_t safi, /* Process change. */ bgp_process (bgp, rn, afi, safi); - - bgp_attr_extra_free (&new_attr); - + return; filtered: @@ -1932,10 +2079,7 @@ bgp_update_rsclient (struct peer *rsclient, afi_t afi, safi_t safi, bgp_rib_remove (rn, ri, peer, afi, safi); bgp_unlock_node (rn); - - if (new_attr.extra) - bgp_attr_extra_free (&new_attr); - + return; } @@ -1960,7 +2104,7 @@ bgp_withdraw_rsclient (struct peer *rsclient, afi_t afi, safi_t safi, /* Withdraw specified route from routing table. */ if (ri && ! CHECK_FLAG (ri->flags, BGP_INFO_HISTORY)) - bgp_rib_withdraw (rn, ri, peer, afi, safi); + bgp_rib_withdraw (rn, ri, peer, afi, safi, prd); else if (BGP_DEBUG (update, UPDATE_IN)) zlog (peer->log, LOG_DEBUG, "%s Can't find the route %s/%d", peer->host, @@ -1980,20 +2124,25 @@ bgp_update_main (struct peer *peer, struct prefix *p, struct attr *attr, int aspath_loop_count = 0; struct bgp_node *rn; struct bgp *bgp; - struct attr new_attr = { 0 }; + struct attr new_attr; + struct attr_extra new_extra; struct attr *attr_new; struct bgp_info *ri; struct bgp_info *new; const char *reason; char buf[SU_ADDRSTRLEN]; + int connected = 0; + + memset (&new_attr, 0, sizeof(struct attr)); + memset (&new_extra, 0, sizeof(struct attr_extra)); bgp = peer->bgp; rn = bgp_afi_node_get (bgp->rib[afi][safi], afi, safi, p, prd); /* When peer's soft reconfiguration enabled. Record input packet in Adj-RIBs-In. */ - if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_SOFT_RECONFIG) - && peer != bgp->peer_self && ! soft_reconfig) + if (! soft_reconfig && CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_SOFT_RECONFIG) + && peer != bgp->peer_self) bgp_adj_in_set (rn, peer, attr); /* Check previously received route. */ @@ -2046,35 +2195,31 @@ bgp_update_main (struct peer *peer, struct prefix *p, struct attr *attr, goto filtered; } - /* Apply incoming route-map. */ + new_attr.extra = &new_extra; bgp_attr_dup (&new_attr, attr); + /* 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; } /* IPv4 unicast next hop check. */ if (afi == AFI_IP && safi == SAFI_UNICAST) { - /* If the peer is EBGP and nexthop is not on connected route, - discard it. */ - if (peer_sort (peer) == BGP_PEER_EBGP && peer->ttl == 1 - && ! bgp_nexthop_onlink (afi, &new_attr) - && ! CHECK_FLAG (peer->flags, PEER_FLAG_DISABLE_CONNECTED_CHECK)) - { - reason = "non-connected next-hop;"; - goto filtered; - } - /* Next hop must not be 0.0.0.0 nor Class D/E address. Next hop must not be my own address. */ - if (bgp_nexthop_self (afi, &new_attr) - || new_attr.nexthop.s_addr == 0 - || IPV4_CLASS_DE (ntohl (new_attr.nexthop.s_addr))) + if (new_attr.nexthop.s_addr == 0 + || IPV4_CLASS_DE (ntohl (new_attr.nexthop.s_addr)) + || bgp_nexthop_self (&new_attr)) { reason = "martian next-hop;"; + bgp_attr_flush (&new_attr); goto filtered; } } @@ -2090,10 +2235,8 @@ bgp_update_main (struct peer *peer, struct prefix *p, struct attr *attr, if (!CHECK_FLAG (ri->flags, BGP_INFO_REMOVED) && attrhash_cmp (ri->attr, attr_new)) { - bgp_info_unset_flag (rn, ri, BGP_INFO_ATTR_CHANGED); - if (CHECK_FLAG (bgp->af_flags[afi][safi], BGP_CONFIG_DAMPENING) - && peer_sort (peer) == BGP_PEER_EBGP + && peer->sort == BGP_PEER_EBGP && CHECK_FLAG (ri->flags, BGP_INFO_HISTORY)) { if (BGP_DEBUG (update, UPDATE_IN)) @@ -2127,8 +2270,8 @@ bgp_update_main (struct peer *peer, struct prefix *p, struct attr *attr, bgp_unlock_node (rn); bgp_attr_unintern (&attr_new); - bgp_attr_extra_free (&new_attr); - + bgp_attr_flush (&new_attr); + return 0; } @@ -2164,7 +2307,7 @@ bgp_update_main (struct peer *peer, struct prefix *p, struct attr *attr, /* Update bgp route dampening information. */ if (CHECK_FLAG (bgp->af_flags[afi][safi], BGP_CONFIG_DAMPENING) - && peer_sort (peer) == BGP_PEER_EBGP) + && peer->sort == BGP_PEER_EBGP) { /* This is implicit withdraw so we should update dampening information. */ @@ -2180,43 +2323,54 @@ bgp_update_main (struct peer *peer, struct prefix *p, struct attr *attr, if (safi == SAFI_MPLS_VPN) memcpy ((bgp_info_extra_get (ri))->tag, tag, 3); + bgp_attr_flush (&new_attr); + /* Update bgp route dampening information. */ if (CHECK_FLAG (bgp->af_flags[afi][safi], BGP_CONFIG_DAMPENING) - && peer_sort (peer) == BGP_PEER_EBGP) + && peer->sort == BGP_PEER_EBGP) { /* Now we do normal update dampening. */ ret = bgp_damp_update (ri, rn, afi, safi); if (ret == BGP_DAMP_SUPPRESSED) { bgp_unlock_node (rn); - bgp_attr_extra_free (&new_attr); return 0; } } /* Nexthop reachability check. */ - if ((afi == AFI_IP || afi == AFI_IP6) - && safi == SAFI_UNICAST - && (peer_sort (peer) == BGP_PEER_IBGP - || peer_sort (peer) == BGP_PEER_CONFED - || (peer_sort (peer) == BGP_PEER_EBGP && peer->ttl != 1) - || CHECK_FLAG (peer->flags, PEER_FLAG_DISABLE_CONNECTED_CHECK))) + if ((afi == AFI_IP || afi == AFI_IP6) && safi == SAFI_UNICAST) { - if (bgp_nexthop_lookup (afi, peer, ri, NULL, NULL)) + if (peer->sort == BGP_PEER_EBGP && peer_ttl (peer) == 1 && + ! CHECK_FLAG (peer->flags, PEER_FLAG_DISABLE_CONNECTED_CHECK)) + connected = 1; + else + connected = 0; + + if (bgp_ensure_nexthop (ri, NULL, connected)) bgp_info_set_flag (rn, ri, BGP_INFO_VALID); else - bgp_info_unset_flag (rn, ri, BGP_INFO_VALID); + { + if (BGP_DEBUG(nht, NHT)) + { + char buf1[INET6_ADDRSTRLEN]; + inet_ntop(AF_INET, (const void *)&attr_new->nexthop, buf1, INET6_ADDRSTRLEN); + zlog_debug("%s(%s): NH unresolved", __FUNCTION__, buf1); + } + bgp_info_unset_flag (rn, ri, BGP_INFO_VALID); + } } else - bgp_info_set_flag (rn, ri, BGP_INFO_VALID); + bgp_info_set_flag (rn, ri, BGP_INFO_VALID); + + bgp_attr_flush (&new_attr); /* Process change. */ bgp_aggregate_increment (bgp, p, ri, afi, safi); bgp_process (bgp, rn, afi, safi); bgp_unlock_node (rn); - bgp_attr_extra_free (&new_attr); - + return 0; } @@ -2230,29 +2384,33 @@ bgp_update_main (struct peer *peer, struct prefix *p, struct attr *attr, } /* Make new BGP info. */ - new = bgp_info_new (); - new->type = type; - new->sub_type = sub_type; - new->peer = peer; - new->attr = attr_new; - new->uptime = bgp_clock (); + new = info_make(type, sub_type, peer, attr_new, rn); /* Update MPLS tag. */ if (safi == SAFI_MPLS_VPN) memcpy ((bgp_info_extra_get (new))->tag, tag, 3); /* Nexthop reachability check. */ - if ((afi == AFI_IP || afi == AFI_IP6) - && safi == SAFI_UNICAST - && (peer_sort (peer) == BGP_PEER_IBGP - || peer_sort (peer) == BGP_PEER_CONFED - || (peer_sort (peer) == BGP_PEER_EBGP && peer->ttl != 1) - || CHECK_FLAG (peer->flags, PEER_FLAG_DISABLE_CONNECTED_CHECK))) - { - if (bgp_nexthop_lookup (afi, peer, new, NULL, NULL)) + if ((afi == AFI_IP || afi == AFI_IP6) && safi == SAFI_UNICAST) + { + if (peer->sort == BGP_PEER_EBGP && peer_ttl (peer) == 1 && + ! CHECK_FLAG (peer->flags, PEER_FLAG_DISABLE_CONNECTED_CHECK)) + connected = 1; + else + connected = 0; + + if (bgp_ensure_nexthop (new, NULL, connected)) bgp_info_set_flag (rn, new, BGP_INFO_VALID); else - bgp_info_unset_flag (rn, new, BGP_INFO_VALID); + { + if (BGP_DEBUG(nht, NHT)) + { + char buf1[INET6_ADDRSTRLEN]; + inet_ntop(AF_INET, (const void *)&attr_new->nexthop, buf1, INET6_ADDRSTRLEN); + zlog_debug("%s(%s): NH unresolved", __FUNCTION__, buf1); + } + bgp_info_unset_flag (rn, new, BGP_INFO_VALID); + } } else bgp_info_set_flag (rn, new, BGP_INFO_VALID); @@ -2265,9 +2423,9 @@ bgp_update_main (struct peer *peer, struct prefix *p, struct attr *attr, /* route_node_get lock */ bgp_unlock_node (rn); - - bgp_attr_extra_free (&new_attr); - + + bgp_attr_flush (&new_attr); + /* If maximum prefix count is configured and current prefix count exeed it. */ if (bgp_maximum_prefix_overflow (peer, afi, safi, 0)) @@ -2292,9 +2450,8 @@ bgp_update_main (struct peer *peer, struct prefix *p, struct attr *attr, bgp_rib_remove (rn, ri, peer, afi, safi); bgp_unlock_node (rn); - - bgp_attr_extra_free (&new_attr); - + bgp_attr_flush (&new_attr); + return 0; } @@ -2338,6 +2495,27 @@ bgp_withdraw (struct peer *peer, struct prefix *p, struct attr *attr, bgp = peer->bgp; + /* Lookup node. */ + rn = bgp_afi_node_get (bgp->rib[afi][safi], afi, safi, p, prd); + + /* Cisco IOS 12.4(24)T4 on session establishment sends withdraws for all + * routes that are filtered. This tanks out Quagga RS pretty badly due to + * the iteration over all RS clients. + * Since we need to remove the entry from adj_in anyway, do that first and + * if there was no entry, we don't need to do anything more. */ + if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_SOFT_RECONFIG) + && peer != bgp->peer_self) + if (!bgp_adj_in_unset (rn, peer)) + { + if (BGP_DEBUG (update, UPDATE_IN)) + zlog (peer->log, LOG_DEBUG, "%s withdrawing route %s/%d " + "not in adj-in", peer->host, + inet_ntop(p->family, &p->u.prefix, buf, SU_ADDRSTRLEN), + p->prefixlen); + bgp_unlock_node (rn); + return 0; + } + /* Process the withdraw for each RS-client. */ for (ALL_LIST_ELEMENTS (bgp->rsclient, node, nnode, rsclient)) { @@ -2352,15 +2530,6 @@ bgp_withdraw (struct peer *peer, struct prefix *p, struct attr *attr, inet_ntop(p->family, &p->u.prefix, buf, SU_ADDRSTRLEN), p->prefixlen); - /* Lookup node. */ - rn = bgp_afi_node_get (bgp->rib[afi][safi], afi, safi, p, prd); - - /* If peer is soft reconfiguration enabled. Record input packet for - further calculation. */ - if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_SOFT_RECONFIG) - && peer != bgp->peer_self) - bgp_adj_in_unset (rn, peer); - /* Lookup withdrawn route. */ for (ri = rn->info; ri; ri = ri->next) if (ri->peer == peer && ri->type == type && ri->sub_type == sub_type) @@ -2368,7 +2537,7 @@ bgp_withdraw (struct peer *peer, struct prefix *p, struct attr *attr, /* Withdraw specified route from routing table. */ if (ri && ! CHECK_FLAG (ri->flags, BGP_INFO_HISTORY)) - bgp_rib_withdraw (rn, ri, peer, afi, safi); + bgp_rib_withdraw (rn, ri, peer, afi, safi, prd); else if (BGP_DEBUG (update, UPDATE_IN)) zlog (peer->log, LOG_DEBUG, "%s Can't find the route %s/%d", peer->host, @@ -2380,16 +2549,17 @@ 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) { struct bgp *bgp; - struct attr attr = { 0 }; - struct aspath *aspath = { 0 }; + 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)) @@ -2405,15 +2575,10 @@ bgp_default_originate (struct peer *peer, afi_t afi, safi_t safi, int withdraw) if (afi == AFI_IP) str2prefix ("0.0.0.0/0", &p); -#ifdef HAVE_IPV6 else if (afi == AFI_IP6) { - struct attr_extra *ae; - attr.extra = NULL; - - ae = bgp_attr_extra_get (&attr); - attr.extra = ae; - + struct attr_extra *ae = attr.extra; + str2prefix ("::/0", &p); /* IPv6 global nexthop must be included. */ @@ -2431,25 +2596,40 @@ bgp_default_originate (struct peer *peer, afi_t afi, safi_t safi, int withdraw) ae->mp_nexthop_len = 32; } } -#endif /* HAVE_IPV6 */ 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) @@ -2460,29 +2640,38 @@ 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) { struct bgp_node *rn; struct bgp_info *ri; - struct attr attr = { 0 }; - + struct attr attr; + struct attr_extra extra; + + memset(&extra, 0, sizeof(extra)); + if (! table) table = (rsclient) ? peer->rib[afi][safi] : peer->bgp->rib[afi][safi]; - if (safi != SAFI_MPLS_VPN + if ((safi != SAFI_MPLS_VPN) && (safi != SAFI_ENCAP) && CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_DEFAULT_ORIGINATE)) bgp_default_originate (peer, afi, safi, 0); + /* It's initialized in bgp_announce_[check|check_rsclient]() */ + attr.extra = &extra; + for (rn = bgp_table_top (table); rn; rn = bgp_route_next(rn)) for (ri = rn->info; ri; ri = ri->next) if (CHECK_FLAG (ri->flags, BGP_INFO_SELECTED) && ri->peer != peer) @@ -2493,9 +2682,9 @@ bgp_announce_table (struct peer *peer, afi_t afi, safi_t safi, bgp_adj_out_set (rn, peer, &rn->p, &attr, afi, safi, ri); else bgp_adj_out_unset (rn, peer, &rn->p, afi, safi); - - bgp_attr_extra_free (&attr); } + + bgp_attr_flush_encap(&attr); } void @@ -2514,7 +2703,7 @@ bgp_announce_route (struct peer *peer, afi_t afi, safi_t safi) if (CHECK_FLAG (peer->af_sflags[afi][safi], PEER_STATUS_ORF_WAIT_REFRESH)) return; - if (safi != SAFI_MPLS_VPN) + if ((safi != SAFI_MPLS_VPN) && (safi != SAFI_ENCAP)) bgp_announce_table (peer, afi, safi, NULL, 0); else for (rn = bgp_table_top (peer->bgp->rib[afi][safi]); rn; @@ -2536,10 +2725,10 @@ 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) + safi_t safi, struct bgp_table *table, struct prefix_rd *prd) { struct bgp_node *rn; struct bgp_adj_in *ain; @@ -2550,8 +2739,11 @@ bgp_soft_reconfig_table_rsclient (struct peer *rsclient, afi_t afi, for (rn = bgp_table_top (table); rn; rn = bgp_route_next (rn)) 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, NULL, NULL); + &rn->p, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, prd, tag); } } @@ -2561,19 +2753,26 @@ bgp_soft_reconfig_rsclient (struct peer *rsclient, afi_t afi, safi_t safi) struct bgp_table *table; struct bgp_node *rn; - if (safi != SAFI_MPLS_VPN) - bgp_soft_reconfig_table_rsclient (rsclient, afi, safi, NULL); + if ((safi != SAFI_MPLS_VPN) && (safi != SAFI_ENCAP)) + bgp_soft_reconfig_table_rsclient (rsclient, afi, safi, NULL, NULL); else for (rn = bgp_table_top (rsclient->bgp->rib[afi][safi]); rn; rn = bgp_route_next (rn)) if ((table = rn->info) != NULL) - bgp_soft_reconfig_table_rsclient (rsclient, afi, safi, table); + { + struct prefix_rd prd; + prd.family = AF_UNSPEC; + prd.prefixlen = 64; + memcpy(&prd.val, rn->p.u.val, 8); + + 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 bgp_table *table, struct prefix_rd *prd) { int ret; struct bgp_node *rn; @@ -2587,9 +2786,13 @@ 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, - NULL, NULL, 1); + prd, tag, 1); + if (ret < 0) { bgp_unlock_node (rn); @@ -2609,15 +2812,22 @@ bgp_soft_reconfig_in (struct peer *peer, afi_t afi, safi_t safi) if (peer->status != Established) return; - if (safi != SAFI_MPLS_VPN) - bgp_soft_reconfig_table (peer, afi, safi, NULL); + if ((safi != SAFI_MPLS_VPN) && (safi != SAFI_ENCAP)) + bgp_soft_reconfig_table (peer, afi, safi, NULL, NULL); else for (rn = bgp_table_top (peer->bgp->rib[afi][safi]); rn; rn = bgp_route_next (rn)) if ((table = rn->info) != NULL) - bgp_soft_reconfig_table (peer, afi, safi, table); + { + struct prefix_rd prd; + prd.family = AF_UNSPEC; + prd.prefixlen = 64; + memcpy(&prd.val, rn->p.u.val, 8); + + bgp_soft_reconfig_table (peer, afi, safi, table, &prd); + } } - + struct bgp_clear_node_queue { @@ -2632,8 +2842,8 @@ bgp_clear_route_node (struct work_queue *wq, void *data) struct bgp_node *rn = cnq->rn; struct peer *peer = wq->spec.data; struct bgp_info *ri; - afi_t afi = rn->table->afi; - safi_t safi = rn->table->safi; + afi_t afi = bgp_node_table (rn)->afi; + safi_t safi = bgp_node_table (rn)->safi; assert (rn && peer); @@ -2658,7 +2868,7 @@ bgp_clear_node_queue_del (struct work_queue *wq, void *data) { struct bgp_clear_node_queue *cnq = data; struct bgp_node *rn = cnq->rn; - struct bgp_table *table = rn->table; + struct bgp_table *table = bgp_node_table (rn); bgp_unlock_node (rn); bgp_table_unlock (table); @@ -2719,9 +2929,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 @@ -2754,22 +2961,6 @@ 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 (ri = rn->info; ri; ri = ri->next) - if (ri->peer == peer || purpose == BGP_CLEAR_ROUTE_MY_RSCLIENT) - { - struct bgp_clear_node_queue *cnq; - - /* both unlocked in bgp_clear_node_queue_del */ - bgp_table_lock (rn->table); - bgp_lock_node (rn); - cnq = XCALLOC (MTYPE_BGP_CLEAR_NODE_QUEUE, - sizeof (struct bgp_clear_node_queue)); - cnq->rn = rn; - cnq->purpose = purpose; - 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) { @@ -2784,6 +2975,22 @@ bgp_clear_route_table (struct peer *peer, afi_t afi, safi_t safi, bgp_unlock_node (rn); break; } + + for (ri = rn->info; ri; ri = ri->next) + if (ri->peer == peer || purpose == BGP_CLEAR_ROUTE_MY_RSCLIENT) + { + struct bgp_clear_node_queue *cnq; + + /* both unlocked in bgp_clear_node_queue_del */ + bgp_table_lock (bgp_node_table (rn)); + bgp_lock_node (rn); + cnq = XCALLOC (MTYPE_BGP_CLEAR_NODE_QUEUE, + sizeof (struct bgp_clear_node_queue)); + cnq->rn = rn; + cnq->purpose = purpose; + work_queue_add (peer->clear_node_queue, cnq); + break; + } } return; } @@ -2812,13 +3019,17 @@ bgp_clear_route (struct peer *peer, afi_t afi, safi_t safi, * on the process_main queue. Fast-flapping could cause that queue * to grow and grow. */ + + /* lock peer in assumption that clear-node-queue will get nodes; if so, + * the unlock will happen upon work-queue completion; other wise, the + * unlock happens at the end of this function. + */ if (!peer->clear_node_queue->thread) peer_lock (peer); /* bgp_clear_node_complete */ - switch (purpose) { case BGP_CLEAR_ROUTE_NORMAL: - if (safi != SAFI_MPLS_VPN) + if ((safi != SAFI_MPLS_VPN) && (safi != SAFI_ENCAP)) bgp_clear_route_table (peer, afi, safi, NULL, NULL, purpose); else for (rn = bgp_table_top (peer->bgp->rib[afi][safi]); rn; @@ -2833,6 +3044,11 @@ bgp_clear_route (struct peer *peer, afi_t afi, safi_t safi, break; case BGP_CLEAR_ROUTE_MY_RSCLIENT: + /* + * gpz 091009: TBD why don't we have special handling for + * SAFI_MPLS_VPN here in the original quagga code? + * (and, by extension, for SAFI_ENCAP) + */ bgp_clear_route_table (peer, afi, safi, NULL, peer, purpose); break; @@ -2840,28 +3056,11 @@ bgp_clear_route (struct peer *peer, afi_t afi, safi_t safi, assert (0); break; } - - /* If no routes were cleared, nothing was added to workqueue, the - * completion function won't be run by workqueue code - call it here. - * XXX: Actually, this assumption doesn't hold, see - * bgp_clear_route_table(), we queue all non-empty nodes. - * - * Additionally, there is a presumption in FSM that clearing is only - * really needed if peer state is Established - peers in - * pre-Established states shouldn't have any route-update state - * associated with them (in or out). - * - * We still can get here in pre-Established though, through - * peer_delete -> bgp_fsm_change_status, so this is a useful sanity - * check to ensure the assumption above holds. - * - * At some future point, this check could be move to the top of the - * function, and do a quick early-return when state is - * pre-Established, avoiding above list and table scans. Once we're - * sure it is safe.. - */ + + /* unlock if no nodes got added to the clear-node-queue. */ if (!peer->clear_node_queue->thread) - bgp_clear_node_complete (peer->clear_node_queue); + peer_unlock (peer); + } void @@ -2875,6 +3074,57 @@ bgp_clear_route_all (struct peer *peer) bgp_clear_route (peer, afi, safi, BGP_CLEAR_ROUTE_NORMAL); } +/* + * Finish freeing things when exiting + */ +static void +bgp_drain_workqueue_immediate (struct work_queue *wq) +{ + if (!wq) + return; + + if (!wq->thread) + { + /* + * no thread implies no queued items + */ + assert(!wq->items->count); + return; + } + + while (wq->items->count) + { + if (wq->thread) + thread_cancel(wq->thread); + work_queue_run(wq->thread); + } +} + +/* + * Special function to process clear node queue when bgpd is exiting + * and the thread scheduler is no longer running. + */ +void +bgp_peer_clear_node_queue_drain_immediate(struct peer *peer) +{ + if (!peer) + return; + + bgp_drain_workqueue_immediate(peer->clear_node_queue); +} + +/* + * The work queues are not specific to a BGP instance, but the + * items in them refer to BGP instances, so this should be called + * before each BGP instance is deleted. + */ +void +bgp_process_queues_drain_immediate(void) +{ + bgp_drain_workqueue_immediate(bm->process_main_queue); + bgp_drain_workqueue_immediate(bm->process_rsclient_queue); +} + void bgp_clear_adj_in (struct peer *peer, afi_t afi, safi_t safi) { @@ -2914,36 +3164,68 @@ bgp_clear_stale_route (struct peer *peer, afi_t afi, safi_t safi) } } } - + +static void +bgp_cleanup_table(struct bgp_table *table, safi_t safi) +{ + struct bgp_node *rn; + struct bgp_info *ri; + struct bgp_info *next; + + for (rn = bgp_table_top (table); rn; rn = bgp_route_next (rn)) + for (ri = rn->info; ri; ri = next) + { + next = ri->next; + if (CHECK_FLAG (ri->flags, BGP_INFO_SELECTED) + && ri->type == ZEBRA_ROUTE_BGP + && ri->sub_type == BGP_ROUTE_NORMAL) + bgp_zebra_withdraw (&rn->p, ri, safi); + } +} + /* Delete all kernel routes. */ void bgp_cleanup_routes (void) { struct bgp *bgp; struct listnode *node, *nnode; - struct bgp_node *rn; - struct bgp_table *table; - struct bgp_info *ri; + afi_t afi; for (ALL_LIST_ELEMENTS (bm->bgp, node, nnode, bgp)) { - table = bgp->rib[AFI_IP][SAFI_UNICAST]; + for (afi = AFI_IP; afi < AFI_MAX; ++afi) + { + struct bgp_node *rn; - for (rn = bgp_table_top (table); rn; rn = bgp_route_next (rn)) - for (ri = rn->info; ri; ri = ri->next) - if (CHECK_FLAG (ri->flags, BGP_INFO_SELECTED) - && ri->type == ZEBRA_ROUTE_BGP - && ri->sub_type == BGP_ROUTE_NORMAL) - bgp_zebra_withdraw (&rn->p, ri,SAFI_UNICAST); + bgp_cleanup_table(bgp->rib[afi][SAFI_UNICAST], SAFI_UNICAST); - table = bgp->rib[AFI_IP6][SAFI_UNICAST]; + /* + * VPN and ENCAP tables are two-level (RD is top level) + */ + for (rn = bgp_table_top(bgp->rib[afi][SAFI_MPLS_VPN]); rn; + rn = bgp_route_next (rn)) + { + if (rn->info) + { + bgp_cleanup_table((struct bgp_table *)(rn->info), SAFI_MPLS_VPN); + bgp_table_finish ((struct bgp_table **)&(rn->info)); + rn->info = NULL; + bgp_unlock_node(rn); + } + } - for (rn = bgp_table_top (table); rn; rn = bgp_route_next (rn)) - for (ri = rn->info; ri; ri = ri->next) - if (CHECK_FLAG (ri->flags, BGP_INFO_SELECTED) - && ri->type == ZEBRA_ROUTE_BGP - && ri->sub_type == BGP_ROUTE_NORMAL) - bgp_zebra_withdraw (&rn->p, ri,SAFI_UNICAST); + for (rn = bgp_table_top(bgp->rib[afi][SAFI_ENCAP]); rn; + rn = bgp_route_next (rn)) + { + if (rn->info) + { + bgp_cleanup_table((struct bgp_table *)(rn->info), SAFI_ENCAP); + bgp_table_finish ((struct bgp_table **)&(rn->info)); + rn->info = NULL; + bgp_unlock_node(rn); + } + } + } } } @@ -2955,11 +3237,12 @@ bgp_reset (void) access_list_reset (); prefix_list_reset (); } - + /* Parse NLRI stream. Withdraw NLRI is recognized by NULL attr value. */ int -bgp_nlri_parse (struct peer *peer, struct attr *attr, struct bgp_nlri *packet) +bgp_nlri_parse_ip (struct peer *peer, struct attr *attr, + struct bgp_nlri *packet) { u_char *pnt; u_char *lim; @@ -2974,6 +3257,9 @@ bgp_nlri_parse (struct peer *peer, struct attr *attr, struct bgp_nlri *packet) pnt = packet->nlri; lim = pnt + packet->length; + /* RFC4771 6.3 The NLRI field in the UPDATE message is checked for + syntactic validity. If the field is syntactically incorrect, + then the Error Subcode is set to Invalid Network Field. */ for (; pnt < lim; pnt += psize) { /* Clear prefix structure. */ @@ -2981,20 +3267,41 @@ bgp_nlri_parse (struct peer *peer, struct attr *attr, struct bgp_nlri *packet) /* Fetch prefix length. */ p.prefixlen = *pnt++; + /* afi/safi validity already verified by caller, bgp_update_receive */ p.family = afi2family (packet->afi); - /* Already checked in nlri_sanity_check(). We do double check - here. */ - if ((packet->afi == AFI_IP && p.prefixlen > 32) - || (packet->afi == AFI_IP6 && p.prefixlen > 128)) - return -1; - + /* Prefix length check. */ + if (p.prefixlen > prefix_blen (&p) * 8) + { + plog_err (peer->log, + "%s [Error] Update packet error" + " (wrong prefix length %u for afi %u)", + peer->host, p.prefixlen, packet->afi); + return -1; + } + /* Packet size overflow check. */ psize = PSIZE (p.prefixlen); /* When packet overflow occur return immediately. */ if (pnt + psize > lim) - return -1; + { + plog_err (peer->log, + "%s [Error] Update packet error" + " (prefix length %u overflows packet)", + peer->host, p.prefixlen); + return -1; + } + + /* Defensive coding, double-check the psize fits in a struct prefix */ + if (psize > (ssize_t) sizeof(p.u)) + { + plog_err (peer->log, + "%s [Error] Update packet error" + " (prefix length %u too large for prefix storage %zu!?!!", + peer->host, p.prefixlen, sizeof(p.u)); + return -1; + } /* Fetch prefix from NLRI packet. */ memcpy (&p.u.prefix, pnt, psize); @@ -3005,21 +3312,19 @@ bgp_nlri_parse (struct peer *peer, struct attr *attr, struct bgp_nlri *packet) if (IN_CLASSD (ntohl (p.u.prefix4.s_addr))) { /* - * From draft-ietf-idr-bgp4-22, Section 6.3: - * If a BGP router receives an UPDATE message with a - * semantically incorrect NLRI field, in which a prefix is - * semantically incorrect (eg. an unexpected multicast IP - * address), it should ignore the prefix. + * From RFC4271 Section 6.3: + * + * If a prefix in the NLRI field is semantically incorrect + * (e.g., an unexpected multicast IP address), an error SHOULD + * be logged locally, and the prefix SHOULD be ignored. */ zlog (peer->log, LOG_ERR, - "IPv4 unicast NLRI is multicast address %s", - inet_ntoa (p.u.prefix4)); - - return -1; + "%s: IPv4 unicast NLRI is multicast address %s, ignoring", + peer->host, inet_ntoa (p.u.prefix4)); + continue; } } -#ifdef HAVE_IPV6 /* Check address. */ if (packet->afi == AFI_IP6 && packet->safi == SAFI_UNICAST) { @@ -3027,14 +3332,23 @@ bgp_nlri_parse (struct peer *peer, struct attr *attr, struct bgp_nlri *packet) { char buf[BUFSIZ]; - zlog (peer->log, LOG_WARNING, - "IPv6 link-local NLRI received %s ignore this NLRI", + zlog (peer->log, LOG_ERR, + "%s: IPv6 unicast NLRI is link-local address %s, ignoring", + peer->host, inet_ntop (AF_INET6, &p.u.prefix6, buf, BUFSIZ)); + continue; + } + if (IN6_IS_ADDR_MULTICAST (&p.u.prefix6)) + { + char buf[BUFSIZ]; + zlog (peer->log, LOG_ERR, + "%s: IPv6 unicast NLRI is multicast address %s, ignoring", + peer->host, + inet_ntop (AF_INET6, &p.u.prefix6, buf, BUFSIZ)); continue; } - } -#endif /* HAVE_IPV6 */ + } /* Normal process. */ if (attr) @@ -3052,73 +3366,17 @@ bgp_nlri_parse (struct peer *peer, struct attr *attr, struct bgp_nlri *packet) /* Packet length consistency check. */ if (pnt != lim) - return -1; - - return 0; -} - -/* NLRI encode syntax check routine. */ -int -bgp_nlri_sanity_check (struct peer *peer, int afi, u_char *pnt, - bgp_size_t length) -{ - u_char *end; - u_char prefixlen; - int psize; - - end = pnt + length; - - /* RFC1771 6.3 The NLRI field in the UPDATE message is checked for - syntactic validity. If the field is syntactically incorrect, - then the Error Subcode is set to Invalid Network Field. */ - - while (pnt < end) - { - prefixlen = *pnt++; - - /* Prefix length check. */ - if ((afi == AFI_IP && prefixlen > 32) - || (afi == AFI_IP6 && prefixlen > 128)) - { - plog_err (peer->log, - "%s [Error] Update packet error (wrong prefix length %d)", - peer->host, prefixlen); - bgp_notify_send (peer, BGP_NOTIFY_UPDATE_ERR, - BGP_NOTIFY_UPDATE_INVAL_NETWORK); - return -1; - } - - /* Packet size overflow check. */ - psize = PSIZE (prefixlen); - - if (pnt + psize > end) - { - plog_err (peer->log, - "%s [Error] Update packet error" - " (prefix data overflow prefix size is %d)", - peer->host, psize); - bgp_notify_send (peer, BGP_NOTIFY_UPDATE_ERR, - BGP_NOTIFY_UPDATE_INVAL_NETWORK); - return -1; - } - - pnt += psize; - } - - /* Packet length consistency check. */ - if (pnt != end) { plog_err (peer->log, - "%s [Error] Update packet error" - " (prefix length mismatch with total length)", - peer->host); - bgp_notify_send (peer, BGP_NOTIFY_UPDATE_ERR, - BGP_NOTIFY_UPDATE_INVAL_NETWORK); + "%s [Error] Update packet error" + " (prefix length mismatch with total length)", + peer->host); return -1; } + return 0; } - + static struct bgp_static * bgp_static_new (void) { @@ -3170,8 +3428,9 @@ bgp_static_update_rsclient (struct peer *rsclient, struct prefix *p, struct bgp_info *new; struct bgp_info info; struct attr *attr_new; - struct attr attr = {0 }; - struct attr new_attr = { .extra = 0 }; + struct attr attr; + struct attr new_attr; + struct attr_extra new_extra; struct bgp *bgp; int ret; char buf[SU_ADDRSTRLEN]; @@ -3223,7 +3482,8 @@ bgp_static_update_rsclient (struct peer *rsclient, struct prefix *p, } else attr_new = bgp_attr_intern (&attr); - + + new_attr.extra = &new_extra; bgp_attr_dup(&new_attr, attr_new); SET_FLAG (bgp->peer_self->rmap_type, PEER_RMAP_TYPE_NETWORK); @@ -3253,7 +3513,6 @@ bgp_static_update_rsclient (struct peer *rsclient, struct prefix *p, bgp_attr_unintern (&attr_new); attr_new = bgp_attr_intern (&new_attr); - bgp_attr_extra_free (&new_attr); for (ri = rn->info; ri; ri = ri->next) if (ri->peer == bgp->peer_self && ri->type == ZEBRA_ROUTE_BGP @@ -3283,6 +3542,23 @@ bgp_static_update_rsclient (struct peer *rsclient, struct prefix *p, ri->attr = attr_new; ri->uptime = bgp_clock (); + /* Nexthop reachability check. */ + if (bgp_flag_check (bgp, BGP_FLAG_IMPORT_CHECK)) + { + if (bgp_ensure_nexthop (ri, NULL, 0)) + bgp_info_set_flag (rn, ri, BGP_INFO_VALID); + else + { + if (BGP_DEBUG(nht, NHT)) + { + char buf1[INET6_ADDRSTRLEN]; + inet_ntop(AF_INET, (const void *)&attr_new->nexthop, + buf1, INET6_ADDRSTRLEN); + zlog_debug("%s(%s): NH unresolved", __FUNCTION__, buf1); + } + bgp_info_unset_flag (rn, ri, BGP_INFO_VALID); + } + } /* Process change. */ bgp_process (bgp, rn, afi, safi); bgp_unlock_node (rn); @@ -3291,15 +3567,29 @@ bgp_static_update_rsclient (struct peer *rsclient, struct prefix *p, return; } } - + /* Make new BGP info. */ - new = bgp_info_new (); - new->type = ZEBRA_ROUTE_BGP; - new->sub_type = BGP_ROUTE_STATIC; - new->peer = bgp->peer_self; - SET_FLAG (new->flags, BGP_INFO_VALID); - new->attr = attr_new; - new->uptime = bgp_clock (); + new = info_make(ZEBRA_ROUTE_BGP, BGP_ROUTE_STATIC, bgp->peer_self, + attr_new, rn); + /* Nexthop reachability check. */ + if (bgp_flag_check (bgp, BGP_FLAG_IMPORT_CHECK)) + { + if (bgp_ensure_nexthop (new, NULL, 0)) + bgp_info_set_flag (rn, new, BGP_INFO_VALID); + else + { + if (BGP_DEBUG(nht, NHT)) + { + char buf1[INET6_ADDRSTRLEN]; + inet_ntop(AF_INET, (const void *)&attr_new->nexthop, + buf1, INET6_ADDRSTRLEN); + zlog_debug("%s(%s): NH unresolved", __FUNCTION__, buf1); + } + bgp_info_unset_flag (rn, new, BGP_INFO_VALID); + } + } + else + bgp_info_set_flag (rn, new, BGP_INFO_VALID); /* Register new BGP information. */ bgp_info_add (rn, new); @@ -3317,13 +3607,13 @@ bgp_static_update_rsclient (struct peer *rsclient, struct prefix *p, static void bgp_static_update_main (struct bgp *bgp, struct prefix *p, - struct bgp_static *bgp_static, afi_t afi, safi_t safi) + struct bgp_static *bgp_static, afi_t afi, safi_t safi) { struct bgp_node *rn; struct bgp_info *ri; struct bgp_info *new; struct bgp_info info; - struct attr attr = { 0 }; + struct attr attr; struct attr *attr_new; int ret; @@ -3401,6 +3691,23 @@ bgp_static_update_main (struct bgp *bgp, struct prefix *p, ri->attr = attr_new; ri->uptime = bgp_clock (); + /* Nexthop reachability check. */ + if (bgp_flag_check (bgp, BGP_FLAG_IMPORT_CHECK)) + { + if (bgp_ensure_nexthop (ri, NULL, 0)) + bgp_info_set_flag (rn, ri, BGP_INFO_VALID); + else + { + if (BGP_DEBUG(nht, NHT)) + { + char buf1[INET6_ADDRSTRLEN]; + inet_ntop(AF_INET, (const void *)&attr_new->nexthop, + buf1, INET6_ADDRSTRLEN); + zlog_debug("%s(%s): NH unresolved", __FUNCTION__, buf1); + } + bgp_info_unset_flag (rn, ri, BGP_INFO_VALID); + } + } /* Process change. */ bgp_aggregate_increment (bgp, p, ri, afi, safi); bgp_process (bgp, rn, afi, safi); @@ -3412,13 +3719,27 @@ bgp_static_update_main (struct bgp *bgp, struct prefix *p, } /* Make new BGP info. */ - new = bgp_info_new (); - new->type = ZEBRA_ROUTE_BGP; - new->sub_type = BGP_ROUTE_STATIC; - new->peer = bgp->peer_self; - SET_FLAG (new->flags, BGP_INFO_VALID); - new->attr = attr_new; - new->uptime = bgp_clock (); + new = info_make(ZEBRA_ROUTE_BGP, BGP_ROUTE_STATIC, bgp->peer_self, attr_new, + rn); + /* Nexthop reachability check. */ + if (bgp_flag_check (bgp, BGP_FLAG_IMPORT_CHECK)) + { + if (bgp_ensure_nexthop (new, NULL, 0)) + bgp_info_set_flag (rn, new, BGP_INFO_VALID); + else + { + if (BGP_DEBUG(nht, NHT)) + { + char buf1[INET6_ADDRSTRLEN]; + inet_ntop(AF_INET, (const void *)&attr_new->nexthop, buf1, + INET6_ADDRSTRLEN); + zlog_debug("%s(%s): NH unresolved", __FUNCTION__, buf1); + } + bgp_info_unset_flag (rn, new, BGP_INFO_VALID); + } + } + else + bgp_info_set_flag (rn, new, BGP_INFO_VALID); /* Aggregate address increment. */ bgp_aggregate_increment (bgp, p, new, afi, safi); @@ -3453,39 +3774,6 @@ bgp_static_update (struct bgp *bgp, struct prefix *p, } } -static void -bgp_static_update_vpnv4 (struct bgp *bgp, struct prefix *p, afi_t afi, - safi_t safi, struct prefix_rd *prd, u_char *tag) -{ - struct bgp_node *rn; - struct bgp_info *new; - - rn = bgp_afi_node_get (bgp->rib[afi][safi], afi, safi, p, prd); - - /* Make new BGP info. */ - new = bgp_info_new (); - new->type = ZEBRA_ROUTE_BGP; - new->sub_type = BGP_ROUTE_STATIC; - new->peer = bgp->peer_self; - new->attr = bgp_attr_default_intern (BGP_ORIGIN_IGP); - SET_FLAG (new->flags, BGP_INFO_VALID); - new->uptime = bgp_clock (); - new->extra = bgp_info_extra_new(); - memcpy (new->extra->tag, tag, 3); - - /* Aggregate address increment. */ - bgp_aggregate_increment (bgp, p, new, afi, safi); - - /* Register new BGP information. */ - bgp_info_add (rn, new); - - /* route_node_get lock */ - bgp_unlock_node (rn); - - /* Process change. */ - bgp_process (bgp, rn, afi, safi); -} - void bgp_static_withdraw (struct bgp *bgp, struct prefix *p, afi_t afi, safi_t safi) @@ -3493,7 +3781,8 @@ bgp_static_withdraw (struct bgp *bgp, struct prefix *p, afi_t afi, struct bgp_node *rn; struct bgp_info *ri; - rn = bgp_afi_node_get (bgp->rib[afi][safi], afi, safi, p, NULL); + /* Make new BGP info. */ + rn = bgp_node_get (bgp->rib[afi][safi], p); /* Check selected route and self inserted route. */ for (ri = rn->info; ri; ri = ri->next) @@ -3506,6 +3795,7 @@ bgp_static_withdraw (struct bgp *bgp, struct prefix *p, afi_t afi, if (ri) { bgp_aggregate_decrement (bgp, p, ri, afi, safi); + bgp_unlink_nexthop(ri); bgp_info_delete (rn, ri); bgp_process (bgp, rn, afi, safi); } @@ -3534,9 +3824,12 @@ bgp_check_local_routes_rsclient (struct peer *rsclient, afi_t afi, safi_t safi) } } +/* + * Used for SAFI_MPLS_VPN and SAFI_ENCAP + */ static void -bgp_static_withdraw_vpnv4 (struct bgp *bgp, struct prefix *p, afi_t afi, - safi_t safi, struct prefix_rd *prd, u_char *tag) +bgp_static_withdraw_safi (struct bgp *bgp, struct prefix *p, afi_t afi, + safi_t safi, struct prefix_rd *prd, u_char *tag) { struct bgp_node *rn; struct bgp_info *ri; @@ -3562,6 +3855,127 @@ bgp_static_withdraw_vpnv4 (struct bgp *bgp, struct prefix *p, afi_t afi, bgp_unlock_node (rn); } +static void +bgp_static_update_safi (struct bgp *bgp, struct prefix *p, + struct bgp_static *bgp_static, afi_t afi, safi_t safi) +{ + struct bgp_node *rn; + struct bgp_info *new; + struct attr *attr_new; + struct attr attr = { 0 }; + struct bgp_info *ri; + + assert (bgp_static); + + rn = bgp_afi_node_get (bgp->rib[afi][safi], afi, safi, p, &bgp_static->prd); + + bgp_attr_default_set (&attr, BGP_ORIGIN_IGP); + + attr.nexthop = bgp_static->igpnexthop; + attr.med = bgp_static->igpmetric; + attr.flag |= ATTR_FLAG_BIT (BGP_ATTR_MULTI_EXIT_DISC); + + /* Apply route-map. */ + if (bgp_static->rmap.name) + { + struct attr attr_tmp = attr; + struct bgp_info info; + int ret; + + info.peer = bgp->peer_self; + info.attr = &attr_tmp; + + SET_FLAG (bgp->peer_self->rmap_type, PEER_RMAP_TYPE_NETWORK); + + ret = route_map_apply (bgp_static->rmap.map, p, RMAP_BGP, &info); + + bgp->peer_self->rmap_type = 0; + + if (ret == RMAP_DENYMATCH) + { + /* Free uninterned attribute. */ + bgp_attr_flush (&attr_tmp); + + /* Unintern original. */ + aspath_unintern (&attr.aspath); + bgp_attr_extra_free (&attr); + bgp_static_withdraw_safi (bgp, p, afi, safi, &bgp_static->prd, + bgp_static->tag); + return; + } + + attr_new = bgp_attr_intern (&attr_tmp); + } + else + { + attr_new = bgp_attr_intern (&attr); + } + + for (ri = rn->info; ri; ri = ri->next) + if (ri->peer == bgp->peer_self && ri->type == ZEBRA_ROUTE_BGP + && ri->sub_type == BGP_ROUTE_STATIC) + break; + + if (ri) + { + if (attrhash_cmp (ri->attr, attr_new) && + !CHECK_FLAG(ri->flags, BGP_INFO_REMOVED)) + { + bgp_unlock_node (rn); + bgp_attr_unintern (&attr_new); + aspath_unintern (&attr.aspath); + bgp_attr_extra_free (&attr); + return; + } + else + { + /* The attribute is changed. */ + bgp_info_set_flag (rn, ri, BGP_INFO_ATTR_CHANGED); + + /* Rewrite BGP route information. */ + if (CHECK_FLAG(ri->flags, BGP_INFO_REMOVED)) + bgp_info_restore(rn, ri); + else + bgp_aggregate_decrement (bgp, p, ri, afi, safi); + bgp_attr_unintern (&ri->attr); + ri->attr = attr_new; + ri->uptime = bgp_clock (); + + /* Process change. */ + bgp_aggregate_increment (bgp, p, ri, afi, safi); + bgp_process (bgp, rn, afi, safi); + bgp_unlock_node (rn); + aspath_unintern (&attr.aspath); + bgp_attr_extra_free (&attr); + return; + } + } + + + /* Make new BGP info. */ + new = info_make (ZEBRA_ROUTE_BGP, BGP_ROUTE_STATIC, bgp->peer_self, + attr_new, rn); + SET_FLAG (new->flags, BGP_INFO_VALID); + new->extra = bgp_info_extra_new(); + memcpy (new->extra->tag, bgp_static->tag, 3); + + /* Aggregate address increment. */ + bgp_aggregate_increment (bgp, p, new, afi, safi); + + /* Register new BGP information. */ + bgp_info_add (rn, new); + + /* route_node_get lock */ + bgp_unlock_node (rn); + + /* Process change. */ + bgp_process (bgp, rn, afi, safi); + + /* Unintern original. */ + aspath_unintern (&attr.aspath); + bgp_attr_extra_free (&attr); +} + /* Configure static BGP network. When user don't run zebra, static route should be installed as valid. */ static int @@ -3581,14 +3995,12 @@ bgp_static_set (struct vty *vty, struct bgp *bgp, const char *ip_str, vty_out (vty, "%% Malformed prefix%s", VTY_NEWLINE); return CMD_WARNING; } -#ifdef HAVE_IPV6 if (afi == AFI_IP6 && IN6_IS_ADDR_LINKLOCAL (&p.u.prefix6)) { vty_out (vty, "%% Malformed prefix (link-local address)%s", VTY_NEWLINE); return CMD_WARNING; } -#endif /* HAVE_IPV6 */ apply_mask (&p); @@ -3642,17 +4054,12 @@ bgp_static_set (struct vty *vty, struct bgp *bgp, const char *ip_str, rn->info = bgp_static; } - /* If BGP scan is not enabled, we should install this route here. */ - if (! bgp_flag_check (bgp, BGP_FLAG_IMPORT_CHECK)) - { - bgp_static->valid = 1; - - if (need_update) - bgp_static_withdraw (bgp, &p, afi, safi); + bgp_static->valid = 1; + if (need_update) + bgp_static_withdraw (bgp, &p, afi, safi); - if (! bgp_static->backdoor) - bgp_static_update (bgp, &p, bgp_static, afi, safi); - } + if (! bgp_static->backdoor) + bgp_static_update (bgp, &p, bgp_static, afi, safi); return CMD_SUCCESS; } @@ -3674,14 +4081,12 @@ bgp_static_unset (struct vty *vty, struct bgp *bgp, const char *ip_str, vty_out (vty, "%% Malformed prefix%s", VTY_NEWLINE); return CMD_WARNING; } -#ifdef HAVE_IPV6 if (afi == AFI_IP6 && IN6_IS_ADDR_LINKLOCAL (&p.u.prefix6)) { vty_out (vty, "%% Malformed prefix (link-local address)%s", VTY_NEWLINE); return CMD_WARNING; } -#endif /* HAVE_IPV6 */ apply_mask (&p); @@ -3725,15 +4130,15 @@ bgp_static_delete (struct bgp *bgp) for (rn = bgp_table_top (bgp->route[afi][safi]); rn; rn = bgp_route_next (rn)) if (rn->info != NULL) { - if (safi == SAFI_MPLS_VPN) + if ((safi == SAFI_MPLS_VPN) || (safi == SAFI_ENCAP)) { table = rn->info; for (rm = bgp_table_top (table); rm; rm = bgp_route_next (rm)) { bgp_static = rn->info; - bgp_static_withdraw_vpnv4 (bgp, &rm->p, - AFI_IP, SAFI_MPLS_VPN, + bgp_static_withdraw_safi (bgp, &rm->p, + AFI_IP, safi, (struct prefix_rd *)&rn->p, bgp_static->tag); bgp_static_free (bgp_static); @@ -3752,9 +4157,15 @@ bgp_static_delete (struct bgp *bgp) } } +/* + * gpz 110624 + * Currently this is used to set static routes for VPN and ENCAP. + * I think it can probably be factored with bgp_static_set. + */ int -bgp_static_set_vpnv4 (struct vty *vty, const char *ip_str, const char *rd_str, - const char *tag_str) +bgp_static_set_safi (safi_t safi, struct vty *vty, const char *ip_str, + const char *rd_str, const char *tag_str, + const char *rmap_str) { int ret; struct prefix p; @@ -3790,10 +4201,10 @@ bgp_static_set_vpnv4 (struct vty *vty, const char *ip_str, const char *rd_str, return CMD_WARNING; } - prn = bgp_node_get (bgp->route[AFI_IP][SAFI_MPLS_VPN], + prn = bgp_node_get (bgp->route[AFI_IP][safi], (struct prefix *)&prd); if (prn->info == NULL) - prn->info = bgp_table_init (AFI_IP, SAFI_MPLS_VPN); + prn->info = bgp_table_init (AFI_IP, safi); else bgp_unlock_node (prn); table = prn->info; @@ -3809,11 +4220,24 @@ bgp_static_set_vpnv4 (struct vty *vty, const char *ip_str, const char *rd_str, { /* New configuration. */ bgp_static = bgp_static_new (); - bgp_static->valid = 1; - memcpy (bgp_static->tag, tag, 3); + bgp_static->backdoor = 0; + bgp_static->valid = 0; + bgp_static->igpmetric = 0; + bgp_static->igpnexthop.s_addr = 0; + memcpy(bgp_static->tag, tag, 3); + bgp_static->prd = prd; + + if (rmap_str) + { + if (bgp_static->rmap.name) + free (bgp_static->rmap.name); + bgp_static->rmap.name = strdup (rmap_str); + bgp_static->rmap.map = route_map_lookup_by_name (rmap_str); + } rn->info = bgp_static; - bgp_static_update_vpnv4 (bgp, &p, AFI_IP, SAFI_MPLS_VPN, &prd, tag); + bgp_static->valid = 1; + bgp_static_update_safi (bgp, &p, bgp_static, AFI_IP, safi); } return CMD_SUCCESS; @@ -3821,8 +4245,8 @@ bgp_static_set_vpnv4 (struct vty *vty, const char *ip_str, const char *rd_str, /* Configure static BGP network. */ int -bgp_static_unset_vpnv4 (struct vty *vty, const char *ip_str, - const char *rd_str, const char *tag_str) +bgp_static_unset_safi(safi_t safi, struct vty *vty, const char *ip_str, + const char *rd_str, const char *tag_str) { int ret; struct bgp *bgp; @@ -3859,10 +4283,10 @@ bgp_static_unset_vpnv4 (struct vty *vty, const char *ip_str, return CMD_WARNING; } - prn = bgp_node_get (bgp->route[AFI_IP][SAFI_MPLS_VPN], + prn = bgp_node_get (bgp->route[AFI_IP][safi], (struct prefix *)&prd); if (prn->info == NULL) - prn->info = bgp_table_init (AFI_IP, SAFI_MPLS_VPN); + prn->info = bgp_table_init (AFI_IP, safi); else bgp_unlock_node (prn); table = prn->info; @@ -3871,7 +4295,7 @@ bgp_static_unset_vpnv4 (struct vty *vty, const char *ip_str, if (rn) { - bgp_static_withdraw_vpnv4 (bgp, &p, AFI_IP, SAFI_MPLS_VPN, &prd, tag); + bgp_static_withdraw_safi (bgp, &p, AFI_IP, safi, &prd, tag); bgp_static = rn->info; bgp_static_free (bgp_static); @@ -3884,7 +4308,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", @@ -4160,7 +4584,6 @@ ALIAS (no_bgp_network_mask_natural, "Network number\n" "Specify a BGP backdoor route\n") -#ifdef HAVE_IPV6 DEFUN (ipv6_bgp_network, ipv6_bgp_network_cmd, "network X:X::X:X/M", @@ -4218,7 +4641,6 @@ ALIAS (no_ipv6_bgp_network, BGP_STR "Specify a network to announce via BGP\n" "IPv6 prefix /, e.g., 3ffe::/16\n") -#endif /* HAVE_IPV6 */ /* stubs for removed AS-Pathlimit commands, kept for config compatibility */ ALIAS_DEPRECATED (bgp_network, @@ -4264,7 +4686,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" @@ -4325,7 +4747,6 @@ ALIAS_DEPRECATED (no_bgp_network_mask_natural, "Specify a BGP backdoor route\n" "AS-Path hopcount limit attribute\n" "AS-Pathlimit TTL, in number of AS-Path hops\n") -#ifdef HAVE_IPV6 ALIAS_DEPRECATED (ipv6_bgp_network, ipv6_bgp_network_ttl_cmd, "network X:X::X:X/M pathlimit <0-255>", @@ -4341,8 +4762,7 @@ ALIAS_DEPRECATED (no_ipv6_bgp_network, "IPv6 prefix /\n" "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 @@ -4383,6 +4803,7 @@ bgp_aggregate_free (struct bgp_aggregate *aggregate) XFREE (MTYPE_BGP_AGGREGATE, aggregate); } +/* Update an aggregate as routes are added/removed from the BGP table */ static void bgp_aggregate_route (struct bgp *bgp, struct prefix *p, struct bgp_info *rinew, afi_t afi, safi_t safi, struct bgp_info *del, @@ -4396,19 +4817,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; - } + u_char atomic_aggregate = 0; /* ORIGIN attribute: If at least one route among routes that are aggregated has ORIGIN with the value INCOMPLETE, then the @@ -4437,11 +4850,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) @@ -4457,6 +4866,9 @@ bgp_aggregate_route (struct bgp *bgp, struct prefix *p, struct bgp_info *rinew, } #endif /* AGGREGATE_NEXTHOP_CHECK */ + if (ri->attr->flag & ATTR_FLAG_BIT(BGP_ATTR_ATOMIC_AGGREGATE)) + atomic_aggregate = 1; + if (ri->sub_type != BGP_ROUTE_AGGREGATE) { if (aggregate->summary_only) @@ -4468,11 +4880,11 @@ bgp_aggregate_route (struct bgp *bgp, struct prefix *p, struct bgp_info *rinew, aggregate->count++; + if (origin < ri->attr->origin) + origin = ri->attr->origin; + if (aggregate->as_set) { - if (origin < ri->attr->origin) - origin = ri->attr->origin; - if (aspath) { asmerge = aspath_aggregate (aspath, ri->attr->aspath); @@ -4509,11 +4921,11 @@ bgp_aggregate_route (struct bgp *bgp, struct prefix *p, struct bgp_info *rinew, if (aggregate->summary_only) (bgp_info_extra_get (rinew))->suppress++; + if (origin < rinew->attr->origin) + origin = rinew->attr->origin; + if (aggregate->as_set) { - if (origin < rinew->attr->origin) - origin = rinew->attr->origin; - if (aspath) { asmerge = aspath_aggregate (aspath, rinew->attr->aspath); @@ -4541,13 +4953,11 @@ bgp_aggregate_route (struct bgp *bgp, struct prefix *p, struct bgp_info *rinew, if (aggregate->count > 0) { rn = bgp_node_get (table, p); - new = bgp_info_new (); - new->type = ZEBRA_ROUTE_BGP; - new->sub_type = BGP_ROUTE_AGGREGATE; - new->peer = bgp->peer_self; + new = info_make(ZEBRA_ROUTE_BGP, BGP_ROUTE_AGGREGATE, bgp->peer_self, + bgp_attr_aggregate_intern(bgp, origin, aspath, community, + aggregate->as_set, + atomic_aggregate), rn); SET_FLAG (new->flags, BGP_INFO_VALID); - new->attr = bgp_attr_aggregate_intern (bgp, origin, aspath, community, aggregate->as_set); - new->uptime = bgp_clock (); bgp_info_add (rn, new); bgp_unlock_node (rn); @@ -4572,9 +4982,16 @@ bgp_aggregate_increment (struct bgp *bgp, struct prefix *p, struct bgp_node *child; struct bgp_node *rn; struct bgp_aggregate *aggregate; + struct bgp_table *table; /* MPLS-VPN aggregation is not yet supported. */ - if (safi == SAFI_MPLS_VPN) + if ((safi == SAFI_MPLS_VPN) || (safi == SAFI_ENCAP)) + return; + + table = bgp->aggregate[afi][safi]; + + /* No aggregates configured. */ + if (bgp_table_top_nolock (table) == NULL) return; if (p->prefixlen == 0) @@ -4583,10 +5000,10 @@ bgp_aggregate_increment (struct bgp *bgp, struct prefix *p, if (BGP_INFO_HOLDDOWN (ri)) return; - child = bgp_node_get (bgp->aggregate[afi][safi], p); + child = bgp_node_get (table, p); /* Aggregate address configuration check. */ - for (rn = child; rn; rn = rn->parent) + for (rn = child; rn; rn = bgp_node_parent_nolock (rn)) if ((aggregate = rn->info) != NULL && rn->p.prefixlen < p->prefixlen) { bgp_aggregate_delete (bgp, &rn->p, afi, safi, aggregate); @@ -4602,18 +5019,25 @@ bgp_aggregate_decrement (struct bgp *bgp, struct prefix *p, struct bgp_node *child; struct bgp_node *rn; struct bgp_aggregate *aggregate; + struct bgp_table *table; /* MPLS-VPN aggregation is not yet supported. */ - if (safi == SAFI_MPLS_VPN) + if ((safi == SAFI_MPLS_VPN) || (safi == SAFI_ENCAP)) + return; + + table = bgp->aggregate[afi][safi]; + + /* No aggregates configured. */ + if (bgp_table_top_nolock (table) == NULL) return; if (p->prefixlen == 0) return; - child = bgp_node_get (bgp->aggregate[afi][safi], p); + child = bgp_node_get (table, p); /* Aggregate address configuration check. */ - for (rn = child; rn; rn = rn->parent) + for (rn = child; rn; rn = bgp_node_parent_nolock (rn)) if ((aggregate = rn->info) != NULL && rn->p.prefixlen < p->prefixlen) { bgp_aggregate_delete (bgp, &rn->p, afi, safi, aggregate); @@ -4622,6 +5046,7 @@ bgp_aggregate_decrement (struct bgp *bgp, struct prefix *p, bgp_unlock_node (child); } +/* Called via bgp_aggregate_set when the user configures aggregate-address */ static void bgp_aggregate_add (struct bgp *bgp, struct prefix *p, afi_t afi, safi_t safi, struct bgp_aggregate *aggregate) @@ -4637,6 +5062,7 @@ bgp_aggregate_add (struct bgp *bgp, struct prefix *p, afi_t afi, safi_t safi, struct aspath *asmerge = NULL; struct community *community = NULL; struct community *commerge = NULL; + u_char atomic_aggregate = 0; table = bgp->rib[afi][safi]; @@ -4658,6 +5084,9 @@ bgp_aggregate_add (struct bgp *bgp, struct prefix *p, afi_t afi, safi_t safi, if (BGP_INFO_HOLDDOWN (ri)) continue; + if (ri->attr->flag & ATTR_FLAG_BIT(BGP_ATTR_ATOMIC_AGGREGATE)) + atomic_aggregate = 1; + if (ri->sub_type != BGP_ROUTE_AGGREGATE) { /* summary-only aggregate route suppress aggregated @@ -4668,13 +5097,21 @@ bgp_aggregate_add (struct bgp *bgp, struct prefix *p, afi_t afi, safi_t safi, bgp_info_set_flag (rn, ri, BGP_INFO_ATTR_CHANGED); match++; } + + /* 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 INCOMPLETE. + * Otherwise, if at least one route among routes that are + * aggregated has ORIGIN with the value EGP, then the aggregated + * route MUST have the ORIGIN attribute with the value EGP. + */ + if (origin < ri->attr->origin) + origin = ri->attr->origin; + /* as-set aggregate route generate origin, as path, community aggregation. */ if (aggregate->as_set) { - if (origin < ri->attr->origin) - origin = ri->attr->origin; - if (aspath) { asmerge = aspath_aggregate (aspath, ri->attr->aspath); @@ -4711,14 +5148,11 @@ bgp_aggregate_add (struct bgp *bgp, struct prefix *p, afi_t afi, safi_t safi, if (aggregate->count) { rn = bgp_node_get (table, p); - - new = bgp_info_new (); - new->type = ZEBRA_ROUTE_BGP; - new->sub_type = BGP_ROUTE_AGGREGATE; - new->peer = bgp->peer_self; + new = info_make(ZEBRA_ROUTE_BGP, BGP_ROUTE_AGGREGATE, bgp->peer_self, + bgp_attr_aggregate_intern(bgp, origin, aspath, community, + aggregate->as_set, + atomic_aggregate), rn); SET_FLAG (new->flags, BGP_INFO_VALID); - new->attr = bgp_attr_aggregate_intern (bgp, origin, aspath, community, aggregate->as_set); - new->uptime = bgp_clock (); bgp_info_add (rn, new); bgp_unlock_node (rn); @@ -4726,6 +5160,13 @@ bgp_aggregate_add (struct bgp *bgp, struct prefix *p, afi_t afi, safi_t safi, /* Process change. */ bgp_process (bgp, rn, afi, safi); } + else + { + if (aspath) + aspath_free (aspath); + if (community) + community_free (community); + } } void @@ -5161,7 +5602,6 @@ ALIAS (no_aggregate_address_mask, "Filter more specific routes from updates\n" "Generate AS set path information\n") -#ifdef HAVE_IPV6 DEFUN (ipv6_aggregate_address, ipv6_aggregate_address_cmd, "aggregate-address X:X::X:X/M", @@ -5238,13 +5678,12 @@ ALIAS (no_ipv6_aggregate_address_summary_only, "Configure BGP aggregate entries\n" "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, const struct in6_addr *nexthop6, - u_int32_t metric, u_char type) + u_int32_t metric, u_char type, route_tag_t tag) { struct bgp *bgp; struct listnode *node, *nnode; @@ -5252,8 +5691,7 @@ bgp_redistribute_add (struct prefix *p, const struct in_addr *nexthop, struct bgp_info *bi; struct bgp_info info; struct bgp_node *bn; - struct attr attr = { 0 }; - struct attr attr_new = { 0 }; + struct attr attr; struct attr *new_attr; afi_t afi; int ret; @@ -5263,17 +5701,16 @@ bgp_redistribute_add (struct prefix *p, const struct in_addr *nexthop, if (nexthop) attr.nexthop = *nexthop; -#ifdef HAVE_IPV6 if (nexthop6) { struct attr_extra *extra = bgp_attr_extra_get(&attr); extra->mp_nexthop_global = *nexthop6; extra->mp_nexthop_len = 16; } -#endif attr.med = metric; attr.flag |= ATTR_FLAG_BIT (BGP_ATTR_MULTI_EXIT_DISC); + attr.extra->tag = tag; for (ALL_LIST_ELEMENTS (bm->bgp, node, nnode, bgp)) { @@ -5281,14 +5718,18 @@ bgp_redistribute_add (struct prefix *p, const struct in_addr *nexthop, if (bgp->redist[afi][type]) { + struct attr attr_new; + struct attr_extra extra_new; + /* Copy attribute for modification. */ + attr_new.extra = &extra_new; bgp_attr_dup (&attr_new, &attr); if (bgp->redist_metric_flag[afi][type]) attr_new.med = bgp->redist_metric[afi][type]; /* Apply route-map. */ - if (bgp->rmap[afi][type].map) + if (bgp->rmap[afi][type].name) { info.peer = bgp->peer_self; info.attr = &attr_new; @@ -5304,8 +5745,7 @@ bgp_redistribute_add (struct prefix *p, const struct in_addr *nexthop, { /* Free uninterned attribute. */ bgp_attr_flush (&attr_new); - bgp_attr_extra_free (&attr_new); - + /* Unintern original. */ aspath_unintern (&attr.aspath); bgp_attr_extra_free (&attr); @@ -5318,8 +5758,7 @@ bgp_redistribute_add (struct prefix *p, const struct in_addr *nexthop, afi, SAFI_UNICAST, p, NULL); new_attr = bgp_attr_intern (&attr_new); - bgp_attr_extra_free (&attr_new); - + for (bi = bn->info; bi; bi = bi->next) if (bi->peer == bgp->peer_self && bi->sub_type == BGP_ROUTE_REDISTRIBUTE) @@ -5357,16 +5796,12 @@ bgp_redistribute_add (struct prefix *p, const struct in_addr *nexthop, aspath_unintern (&attr.aspath); bgp_attr_extra_free (&attr); return; - } + } } - new = bgp_info_new (); - new->type = type; - new->sub_type = BGP_ROUTE_REDISTRIBUTE; - new->peer = bgp->peer_self; + new = info_make(type, BGP_ROUTE_REDISTRIBUTE, bgp->peer_self, + new_attr, bn); SET_FLAG (new->flags, BGP_INFO_VALID); - new->attr = new_attr; - new->uptime = bgp_clock (); bgp_aggregate_increment (bgp, p, new, afi, SAFI_UNICAST); bgp_info_add (bn, new); @@ -5438,7 +5873,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) @@ -5489,7 +5924,8 @@ route_vty_short_status_out (struct vty *vty, struct bgp_info *binfo) vty_out (vty, "S"); else if (binfo->extra && binfo->extra->suppress) vty_out (vty, "s"); - else if (! CHECK_FLAG (binfo->flags, BGP_INFO_HISTORY)) + else if (CHECK_FLAG (binfo->flags, BGP_INFO_VALID) && + ! CHECK_FLAG (binfo->flags, BGP_INFO_HISTORY)) vty_out (vty, "*"); else vty_out (vty, " "); @@ -5501,6 +5937,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, " "); @@ -5513,8 +5951,12 @@ route_vty_short_status_out (struct vty *vty, struct bgp_info *binfo) /* called from terminal list command */ void -route_vty_out (struct vty *vty, struct prefix *p, - struct bgp_info *binfo, int display, safi_t safi) +route_vty_out( + struct vty *vty, + struct prefix *p, + struct bgp_info *binfo, + int display, + safi_t safi) { struct attr *attr; @@ -5522,7 +5964,7 @@ route_vty_out (struct vty *vty, struct prefix *p, route_vty_short_status_out (vty, binfo); /* print prefix and mask */ - if (! display) + if (!display) route_vty_out_route (p, vty); else vty_out (vty, "%*s", 17, " "); @@ -5531,40 +5973,76 @@ route_vty_out (struct vty *vty, struct prefix *p, attr = binfo->attr; if (attr) { - if (p->family == AF_INET) - { - if (safi == SAFI_MPLS_VPN) - vty_out (vty, "%-16s", - inet_ntoa (attr->extra->mp_nexthop_global_in)); - else - vty_out (vty, "%-16s", inet_ntoa (attr->nexthop)); - } -#ifdef HAVE_IPV6 - else if (p->family == AF_INET6) - { - int len; - char buf[BUFSIZ]; - len = vty_out (vty, "%s", - inet_ntop (AF_INET6, &attr->extra->mp_nexthop_global, - buf, BUFSIZ)); - len = 16 - len; - if (len < 1) - vty_out (vty, "%s%*s", VTY_NEWLINE, 36, " "); - else - vty_out (vty, "%*s", len, " "); + /* + * NEXTHOP start + */ + + /* + * For ENCAP routes, nexthop address family is not + * neccessarily the same as the prefix address family. + * Both SAFI_MPLS_VPN and SAFI_ENCAP use the MP nexthop field + */ + if ((safi == SAFI_ENCAP) || (safi == SAFI_MPLS_VPN)) { + if (attr->extra) { + char buf[BUFSIZ]; + int af = NEXTHOP_FAMILY(attr->extra->mp_nexthop_len); + + switch (af) { + case AF_INET: + vty_out (vty, "%s", inet_ntop(af, + &attr->extra->mp_nexthop_global_in, buf, BUFSIZ)); + break; + case AF_INET6: + vty_out (vty, "%s", inet_ntop(af, + &attr->extra->mp_nexthop_global, buf, BUFSIZ)); + break; + default: + vty_out(vty, "?"); + } + } else { + vty_out(vty, "?"); } -#endif /* HAVE_IPV6 */ + } else { + + if (p->family == AF_INET) + { + vty_out (vty, "%-16s", inet_ntoa (attr->nexthop)); + } + else if (p->family == AF_INET6) + { + int len; + char buf[BUFSIZ]; + + len = vty_out (vty, "%s", + inet_ntop (AF_INET6, &attr->extra->mp_nexthop_global, + buf, BUFSIZ)); + len = 16 - len; + if (len < 1) + vty_out (vty, "%s%*s", VTY_NEWLINE, 36, " "); + else + vty_out (vty, "%*s", len, " "); + } + else + { + vty_out(vty, "?"); + } + } + + /* + * NEXTHOP end + */ + if (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_MULTI_EXIT_DISC)) vty_out (vty, "%10u", attr->med); else - vty_out (vty, " "); + vty_out (vty, " "); if (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_LOCAL_PREF)) vty_out (vty, "%7u", attr->local_pref); else - vty_out (vty, " "); + vty_out (vty, " "); vty_out (vty, "%7u ", (attr->extra ? attr->extra->weight : 0)); @@ -5596,13 +6074,12 @@ route_vty_out_tmp (struct vty *vty, struct prefix *p, { if (p->family == AF_INET) { - if (safi == SAFI_MPLS_VPN) + if ((safi == SAFI_MPLS_VPN) || (safi == SAFI_ENCAP)) vty_out (vty, "%-16s", inet_ntoa (attr->extra->mp_nexthop_global_in)); else vty_out (vty, "%-16s", inet_ntoa (attr->nexthop)); } -#ifdef HAVE_IPV6 else if (p->family == AF_INET6) { int len; @@ -5619,15 +6096,14 @@ route_vty_out_tmp (struct vty *vty, struct prefix *p, else vty_out (vty, "%*s", len, " "); } -#endif /* HAVE_IPV6 */ if (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_MULTI_EXIT_DISC)) - vty_out (vty, "%10u", attr->med); + vty_out (vty, "%10u ", attr->med); else vty_out (vty, " "); if (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_LOCAL_PREF)) - vty_out (vty, "%7u", attr->local_pref); + vty_out (vty, "%7u ", attr->local_pref); else vty_out (vty, " "); @@ -5669,13 +6145,12 @@ route_vty_out_tag (struct vty *vty, struct prefix *p, { if (p->family == AF_INET) { - if (safi == SAFI_MPLS_VPN) + if ((safi == SAFI_MPLS_VPN) || (safi == SAFI_ENCAP)) vty_out (vty, "%-16s", inet_ntoa (attr->extra->mp_nexthop_global_in)); else vty_out (vty, "%-16s", inet_ntoa (attr->nexthop)); } -#ifdef HAVE_IPV6 else if (p->family == AF_INET6) { assert (attr->extra); @@ -5693,7 +6168,6 @@ route_vty_out_tag (struct vty *vty, struct prefix *p, buf1, BUFSIZ)); } -#endif /* HAVE_IPV6 */ } label = decode_label (binfo->extra->tag); @@ -5852,11 +6326,10 @@ route_vty_out_detail (struct vty *vty, struct bgp *bgp, struct prefix *p, /* Line2 display Next-hop, Neighbor, Router-id */ if (p->family == AF_INET) { - vty_out (vty, " %s", safi == SAFI_MPLS_VPN ? + vty_out (vty, " %s", ((safi == SAFI_MPLS_VPN) || (safi == SAFI_ENCAP)) ? inet_ntoa (attr->extra->mp_nexthop_global_in) : inet_ntoa (attr->nexthop)); } -#ifdef HAVE_IPV6 else { assert (attr->extra); @@ -5864,7 +6337,6 @@ route_vty_out_detail (struct vty *vty, struct bgp *bgp, struct prefix *p, inet_ntop (AF_INET6, &attr->extra->mp_nexthop_global, buf, INET6_ADDRSTRLEN)); } -#endif /* HAVE_IPV6 */ if (binfo->peer == bgp->peer_self) { @@ -5877,8 +6349,12 @@ route_vty_out_detail (struct vty *vty, struct bgp *bgp, struct prefix *p, if (! CHECK_FLAG (binfo->flags, BGP_INFO_VALID)) vty_out (vty, " (inaccessible)"); else if (binfo->extra && binfo->extra->igpmetric) - vty_out (vty, " (metric %d)", binfo->extra->igpmetric); - vty_out (vty, " from %s", sockunion2str (&binfo->peer->su, buf, SU_ADDRSTRLEN)); + vty_out (vty, " (metric %u)", binfo->extra->igpmetric); + if (!sockunion2str (&binfo->peer->su, buf, sizeof(buf))) { + buf[0] = '?'; + buf[1] = 0; + } + vty_out (vty, " from %s", buf); if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_ORIGINATOR_ID)) vty_out (vty, " (%s)", inet_ntoa (attr->extra->originator_id)); else @@ -5886,7 +6362,6 @@ route_vty_out_detail (struct vty *vty, struct bgp *bgp, struct prefix *p, } vty_out (vty, "%s", VTY_NEWLINE); -#ifdef HAVE_IPV6 /* display nexthop local */ if (attr->extra && attr->extra->mp_nexthop_len == 32) { @@ -5895,9 +6370,8 @@ route_vty_out_detail (struct vty *vty, struct bgp *bgp, struct prefix *p, buf, INET6_ADDRSTRLEN), VTY_NEWLINE); } -#endif /* HAVE_IPV6 */ - /* Line 3 display Origin, Med, Locpref, Weight, valid, Int/Ext/Local, Atomic, best */ + /* Line 3 display Origin, Med, Locpref, Weight, Tag, valid, Int/Ext/Local, Atomic, best */ vty_out (vty, " Origin %s", bgp_origin_long_str[attr->origin]); if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC)) @@ -5910,8 +6384,13 @@ route_vty_out_detail (struct vty *vty, struct bgp *bgp, struct prefix *p, if (attr->extra && attr->extra->weight != 0) vty_out (vty, ", weight %u", attr->extra->weight); + + if (attr->extra && attr->extra->tag != 0) + vty_out (vty, ", tag %d", attr->extra->tag); - if (! CHECK_FLAG (binfo->flags, BGP_INFO_HISTORY)) + if (! CHECK_FLAG (binfo->flags, BGP_INFO_VALID)) + vty_out (vty, ", invalid"); + else if (! CHECK_FLAG (binfo->flags, BGP_INFO_HISTORY)) vty_out (vty, ", valid"); if (binfo->peer != bgp->peer_self) @@ -5932,6 +6411,11 @@ route_vty_out_detail (struct vty *vty, struct bgp *bgp, struct prefix *p, if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_ATOMIC_AGGREGATE)) vty_out (vty, ", atomic-aggregate"); + if (CHECK_FLAG (binfo->flags, BGP_INFO_MULTIPATH) || + (CHECK_FLAG (binfo->flags, BGP_INFO_SELECTED) && + bgp_info_mpath_count (binfo))) + vty_out (vty, ", multipath"); + if (CHECK_FLAG (binfo->flags, BGP_INFO_SELECTED)) vty_out (vty, ", best"); @@ -5946,8 +6430,13 @@ route_vty_out_detail (struct vty *vty, struct bgp *bgp, struct prefix *p, if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES)) vty_out (vty, " Extended Community: %s%s", attr->extra->ecommunity->str, VTY_NEWLINE); - - /* Line 6 display Originator, Cluster-id */ + + /* Line 6 display Large community */ + if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_LARGE_COMMUNITIES)) + vty_out (vty, " Large Community: %s%s", + attr->extra->lcommunity->str, VTY_NEWLINE); + + /* Line 7 display Originator, Cluster-id */ if ((attr->flag & ATTR_FLAG_BIT(BGP_ATTR_ORIGINATOR_ID)) || (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_CLUSTER_LIST))) { @@ -5970,7 +6459,7 @@ route_vty_out_detail (struct vty *vty, struct bgp *bgp, struct prefix *p, if (binfo->extra && binfo->extra->damp_info) bgp_damp_info_vty (vty, binfo); - /* Line 7 display Uptime */ + /* Line 8 display Uptime */ #ifdef HAVE_CLOCK_MONOTONIC tbuf = time(NULL) - (bgp_clock() - binfo->uptime); vty_out (vty, " Last update: %s", ctime(&tbuf)); @@ -5979,9 +6468,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" @@ -6002,6 +6493,9 @@ enum bgp_show_type bgp_show_type_community_exact, bgp_show_type_community_list, bgp_show_type_community_list_exact, + bgp_show_type_lcommunity_all, + bgp_show_type_lcommunity, + bgp_show_type_lcommunity_list, bgp_show_type_flap_statistics, bgp_show_type_flap_address, bgp_show_type_flap_prefix, @@ -6025,9 +6519,11 @@ bgp_show_table (struct vty *vty, struct bgp_table *table, struct in_addr *router int header = 1; int display; unsigned long output_count; + unsigned long total_count; /* This is first entry point, so reset total line. */ output_count = 0; + total_count = 0; /* Start processing of routes. */ for (rn = bgp_table_top (table); rn; rn = bgp_route_next (rn)) @@ -6037,6 +6533,7 @@ bgp_show_table (struct vty *vty, struct bgp_table *table, struct in_addr *router for (ri = rn->info; ri; ri = ri->next) { + total_count++; if (type == bgp_show_type_flap_statistics || type == bgp_show_type_flap_address || type == bgp_show_type_flap_prefix @@ -6082,17 +6579,17 @@ bgp_show_table (struct vty *vty, struct bgp_table *table, struct in_addr *router { struct route_map *rmap = output_arg; struct bgp_info binfo; - struct attr dummy_attr = { 0 }; + struct attr dummy_attr; + struct attr_extra dummy_extra; int ret; + dummy_attr.extra = &dummy_extra; bgp_attr_dup (&dummy_attr, ri->attr); + binfo.peer = ri->peer; binfo.attr = &dummy_attr; ret = route_map_apply (rmap, &rn->p, RMAP_BGP, &binfo); - - bgp_attr_extra_free (&dummy_attr); - if (ret == RMAP_DENYMATCH) continue; } @@ -6161,8 +6658,34 @@ bgp_show_table (struct vty *vty, struct bgp_table *table, struct in_addr *router if (! community_list_exact_match (ri->attr->community, list)) continue; } - if (type == bgp_show_type_flap_address - || type == bgp_show_type_flap_prefix) + if (type == bgp_show_type_community_all) + { + if (! ri->attr->community) + continue; + } + if (type == bgp_show_type_lcommunity) + { + struct lcommunity *lcom = output_arg; + + if (! ri->attr->extra || ! ri->attr->extra->lcommunity || + ! lcommunity_match (ri->attr->extra->lcommunity, lcom)) + continue; + } + if (type == bgp_show_type_lcommunity_list) + { + struct community_list *list = output_arg; + + if (! ri->attr->extra || + ! lcommunity_list_match (ri->attr->extra->lcommunity, list)) + continue; + } + if (type == bgp_show_type_lcommunity_all) + { + if (! ri->attr->extra || ! ri->attr->extra->lcommunity) + continue; + } + if (type == bgp_show_type_flap_address + || type == bgp_show_type_flap_prefix) { struct prefix *p = output_arg; @@ -6231,11 +6754,11 @@ bgp_show_table (struct vty *vty, struct bgp_table *table, struct in_addr *router if (output_count == 0) { if (type == bgp_show_type_normal) - vty_out (vty, "No BGP network exists%s", VTY_NEWLINE); + vty_out (vty, "No BGP prefixes displayed, %ld exist%s", total_count, VTY_NEWLINE); } else - vty_out (vty, "%sTotal number of prefixes %ld%s", - VTY_NEWLINE, output_count, VTY_NEWLINE); + vty_out (vty, "%sDisplayed %ld out of %ld total prefixes%s", + VTY_NEWLINE, output_count, total_count, VTY_NEWLINE); return CMD_SUCCESS; } @@ -6281,12 +6804,12 @@ route_vty_out_detail_header (struct vty *vty, struct bgp *bgp, int no_advertise = 0; int local_as = 0; int first = 0; + int printrd = ((safi == SAFI_MPLS_VPN) || (safi == SAFI_ENCAP)); p = &rn->p; vty_out (vty, "BGP routing table entry for %s%s%s/%d%s", - (safi == SAFI_MPLS_VPN ? - prefix_rd2str (prd, buf1, RD_ADDRSTRLEN) : ""), - safi == SAFI_MPLS_VPN ? ":" : "", + (printrd ? prefix_rd2str (prd, buf1, RD_ADDRSTRLEN) : ""), + printrd ? ":" : "", inet_ntop (p->family, &p->u.prefix, buf2, INET6_ADDRSTRLEN), p->prefixlen, VTY_NEWLINE); @@ -6350,7 +6873,7 @@ static int bgp_show_route_in_table (struct vty *vty, struct bgp *bgp, struct bgp_table *rib, const char *ip_str, afi_t afi, safi_t safi, struct prefix_rd *prd, - int prefix_check) + int prefix_check, enum bgp_path_type pathtype) { int ret; int header; @@ -6361,6 +6884,7 @@ bgp_show_route_in_table (struct vty *vty, struct bgp *bgp, struct bgp_info *ri; struct bgp_table *table; + memset (&match, 0, sizeof (struct prefix)); /* keep valgrind happy */ /* Check IP address argument. */ ret = str2prefix (ip_str, &match); if (! ret) @@ -6371,7 +6895,7 @@ bgp_show_route_in_table (struct vty *vty, struct bgp *bgp, match.family = afi2family (afi); - if (safi == SAFI_MPLS_VPN) + if ((safi == SAFI_MPLS_VPN) || (safi == SAFI_ENCAP)) { for (rn = bgp_table_top (rib); rn; rn = bgp_route_next (rn)) { @@ -6395,12 +6919,17 @@ bgp_show_route_in_table (struct vty *vty, struct bgp *bgp, if (header) { route_vty_out_detail_header (vty, bgp, rm, (struct prefix_rd *)&rn->p, - AFI_IP, SAFI_MPLS_VPN); + AFI_IP, safi); header = 0; } display++; - route_vty_out_detail (vty, bgp, &rm->p, ri, AFI_IP, SAFI_MPLS_VPN); + + if (pathtype == BGP_PATH_ALL || + (pathtype == BGP_PATH_BESTPATH && CHECK_FLAG (ri->flags, BGP_INFO_SELECTED)) || + (pathtype == BGP_PATH_MULTIPATH && + (CHECK_FLAG (ri->flags, BGP_INFO_MULTIPATH) || CHECK_FLAG (ri->flags, BGP_INFO_SELECTED)))) + route_vty_out_detail (vty, bgp, &rm->p, ri, AFI_IP, safi); } bgp_unlock_node (rm); @@ -6424,7 +6953,12 @@ bgp_show_route_in_table (struct vty *vty, struct bgp *bgp, header = 0; } display++; - route_vty_out_detail (vty, bgp, &rn->p, ri, afi, safi); + + if (pathtype == BGP_PATH_ALL || + (pathtype == BGP_PATH_BESTPATH && CHECK_FLAG (ri->flags, BGP_INFO_SELECTED)) || + (pathtype == BGP_PATH_MULTIPATH && + (CHECK_FLAG (ri->flags, BGP_INFO_MULTIPATH) || CHECK_FLAG (ri->flags, BGP_INFO_SELECTED)))) + route_vty_out_detail (vty, bgp, &rn->p, ri, afi, safi); } } @@ -6445,7 +6979,7 @@ bgp_show_route_in_table (struct vty *vty, struct bgp *bgp, static int bgp_show_route (struct vty *vty, const char *view_name, const char *ip_str, afi_t afi, safi_t safi, struct prefix_rd *prd, - int prefix_check) + int prefix_check, enum bgp_path_type pathtype) { struct bgp *bgp; @@ -6470,7 +7004,7 @@ bgp_show_route (struct vty *vty, const char *view_name, const char *ip_str, } return bgp_show_route_in_table (vty, bgp, bgp->rib[afi][safi], ip_str, - afi, safi, prd, prefix_check); + afi, safi, prd, prefix_check, pathtype); } /* BGP route print out function. */ @@ -6501,15 +7035,6 @@ DEFUN (show_ip_bgp_ipv4, return bgp_show (vty, NULL, AFI_IP, SAFI_UNICAST, bgp_show_type_normal, NULL); } -ALIAS (show_ip_bgp_ipv4, - show_bgp_ipv4_safi_cmd, - "show bgp ipv4 (unicast|multicast)", - SHOW_STR - BGP_STR - "Address family\n" - "Address Family modifier\n" - "Address Family modifier\n") - DEFUN (show_ip_bgp_route, show_ip_bgp_route_cmd, "show ip bgp A.B.C.D", @@ -6518,35 +7043,65 @@ DEFUN (show_ip_bgp_route, BGP_STR "Network in the BGP routing table to display\n") { - return bgp_show_route (vty, NULL, argv[0], AFI_IP, SAFI_UNICAST, NULL, 0); + return bgp_show_route (vty, NULL, argv[0], AFI_IP, SAFI_UNICAST, NULL, 0, BGP_PATH_ALL); } -DEFUN (show_ip_bgp_ipv4_route, - show_ip_bgp_ipv4_route_cmd, - "show ip bgp ipv4 (unicast|multicast) A.B.C.D", +DEFUN (show_ip_bgp_route_pathtype, + show_ip_bgp_route_pathtype_cmd, + "show ip bgp A.B.C.D (bestpath|multipath)", SHOW_STR IP_STR BGP_STR + "IP prefix /, e.g., 35.0.0.0/8\n" + "Display only the bestpath\n" + "Display only multipaths\n") +{ + if (strncmp (argv[1], "b", 1) == 0) + return bgp_show_route (vty, NULL, argv[0], AFI_IP, SAFI_UNICAST, NULL, 0, BGP_PATH_BESTPATH); + else + return bgp_show_route (vty, NULL, argv[0], AFI_IP, SAFI_UNICAST, NULL, 0, BGP_PATH_MULTIPATH); +} + +DEFUN (show_bgp_ipv4_safi_route_pathtype, + show_bgp_ipv4_safi_route_pathtype_cmd, + "show bgp ipv4 (unicast|multicast) A.B.C.D (bestpath|multipath)", + SHOW_STR + BGP_STR "Address family\n" "Address Family modifier\n" "Address Family modifier\n" - "Network in the BGP routing table to display\n") + "IP prefix /, e.g., 35.0.0.0/8\n" + "Display only the bestpath\n" + "Display only multipaths\n") { if (strncmp (argv[0], "m", 1) == 0) - return bgp_show_route (vty, NULL, argv[1], AFI_IP, SAFI_MULTICAST, NULL, 0); - - return bgp_show_route (vty, NULL, argv[1], AFI_IP, SAFI_UNICAST, NULL, 0); + if (strncmp (argv[2], "b", 1) == 0) + return bgp_show_route (vty, NULL, argv[1], AFI_IP, SAFI_MULTICAST, NULL, 0, BGP_PATH_BESTPATH); + else + return bgp_show_route (vty, NULL, argv[1], AFI_IP, SAFI_MULTICAST, NULL, 0, BGP_PATH_MULTIPATH); + else + if (strncmp (argv[2], "b", 1) == 0) + return bgp_show_route (vty, NULL, argv[1], AFI_IP, SAFI_UNICAST, NULL, 0, BGP_PATH_BESTPATH); + else + return bgp_show_route (vty, NULL, argv[1], AFI_IP, SAFI_UNICAST, NULL, 0, BGP_PATH_MULTIPATH); } -ALIAS (show_ip_bgp_ipv4_route, - show_bgp_ipv4_safi_route_cmd, - "show bgp ipv4 (unicast|multicast) A.B.C.D", +DEFUN (show_ip_bgp_ipv4_route, + show_ip_bgp_ipv4_route_cmd, + "show ip bgp ipv4 (unicast|multicast) A.B.C.D", SHOW_STR + IP_STR BGP_STR "Address family\n" "Address Family modifier\n" "Address Family modifier\n" "Network in the BGP routing table to display\n") +{ + if (strncmp (argv[0], "m", 1) == 0) + return bgp_show_route (vty, NULL, argv[1], AFI_IP, SAFI_MULTICAST, NULL, 0, BGP_PATH_ALL); + + return bgp_show_route (vty, NULL, argv[1], AFI_IP, SAFI_UNICAST, NULL, 0, BGP_PATH_ALL); +} DEFUN (show_ip_bgp_vpnv4_all_route, show_ip_bgp_vpnv4_all_route_cmd, @@ -6558,9 +7113,10 @@ DEFUN (show_ip_bgp_vpnv4_all_route, "Display information about all VPNv4 NLRIs\n" "Network in the BGP routing table to display\n") { - return bgp_show_route (vty, NULL, argv[0], AFI_IP, SAFI_MPLS_VPN, NULL, 0); + return bgp_show_route (vty, NULL, argv[0], AFI_IP, SAFI_MPLS_VPN, NULL, 0, BGP_PATH_ALL); } + DEFUN (show_ip_bgp_vpnv4_rd_route, show_ip_bgp_vpnv4_rd_route_cmd, "show ip bgp vpnv4 rd ASN:nn_or_IP-address:nn A.B.C.D", @@ -6581,7 +7137,7 @@ DEFUN (show_ip_bgp_vpnv4_rd_route, vty_out (vty, "%% Malformed Route Distinguisher%s", VTY_NEWLINE); return CMD_WARNING; } - return bgp_show_route (vty, NULL, argv[1], AFI_IP, SAFI_MPLS_VPN, &prd, 0); + return bgp_show_route (vty, NULL, argv[1], AFI_IP, SAFI_MPLS_VPN, &prd, 0, BGP_PATH_ALL); } DEFUN (show_ip_bgp_prefix, @@ -6592,7 +7148,23 @@ DEFUN (show_ip_bgp_prefix, BGP_STR "IP prefix /, e.g., 35.0.0.0/8\n") { - return bgp_show_route (vty, NULL, argv[0], AFI_IP, SAFI_UNICAST, NULL, 1); + return bgp_show_route (vty, NULL, argv[0], AFI_IP, SAFI_UNICAST, NULL, 1, BGP_PATH_ALL); +} + +DEFUN (show_ip_bgp_prefix_pathtype, + show_ip_bgp_prefix_pathtype_cmd, + "show ip bgp A.B.C.D/M (bestpath|multipath)", + SHOW_STR + IP_STR + BGP_STR + "IP prefix /, e.g., 35.0.0.0/8\n" + "Display only the bestpath\n" + "Display only multipaths\n") +{ + if (strncmp (argv[1], "b", 1) == 0) + return bgp_show_route (vty, NULL, argv[0], AFI_IP, SAFI_UNICAST, NULL, 1, BGP_PATH_BESTPATH); + else + return bgp_show_route (vty, NULL, argv[0], AFI_IP, SAFI_UNICAST, NULL, 1, BGP_PATH_MULTIPATH); } DEFUN (show_ip_bgp_ipv4_prefix, @@ -6607,20 +7179,47 @@ DEFUN (show_ip_bgp_ipv4_prefix, "IP prefix /, e.g., 35.0.0.0/8\n") { if (strncmp (argv[0], "m", 1) == 0) - return bgp_show_route (vty, NULL, argv[1], AFI_IP, SAFI_MULTICAST, NULL, 1); + return bgp_show_route (vty, NULL, argv[1], AFI_IP, SAFI_MULTICAST, NULL, 1, BGP_PATH_ALL); - return bgp_show_route (vty, NULL, argv[1], AFI_IP, SAFI_UNICAST, NULL, 1); + return bgp_show_route (vty, NULL, argv[1], AFI_IP, SAFI_UNICAST, NULL, 1, BGP_PATH_ALL); } -ALIAS (show_ip_bgp_ipv4_prefix, - show_bgp_ipv4_safi_prefix_cmd, - "show bgp ipv4 (unicast|multicast) A.B.C.D/M", +DEFUN (show_ip_bgp_ipv4_prefix_pathtype, + show_ip_bgp_ipv4_prefix_pathtype_cmd, + "show ip bgp ipv4 (unicast|multicast) A.B.C.D/M (bestpath|multipath)", SHOW_STR + IP_STR BGP_STR "Address family\n" "Address Family modifier\n" "Address Family modifier\n" - "IP prefix /, e.g., 35.0.0.0/8\n") + "IP prefix /, e.g., 35.0.0.0/8\n" + "Display only the bestpath\n" + "Display only multipaths\n") +{ + if (strncmp (argv[0], "m", 1) == 0) + if (strncmp (argv[2], "b", 1) == 0) + return bgp_show_route (vty, NULL, argv[1], AFI_IP, SAFI_MULTICAST, NULL, 1, BGP_PATH_BESTPATH); + else + return bgp_show_route (vty, NULL, argv[1], AFI_IP, SAFI_MULTICAST, NULL, 1, BGP_PATH_MULTIPATH); + else + if (strncmp (argv[2], "b", 1) == 0) + return bgp_show_route (vty, NULL, argv[1], AFI_IP, SAFI_UNICAST, NULL, 1, BGP_PATH_BESTPATH); + else + return bgp_show_route (vty, NULL, argv[1], AFI_IP, SAFI_UNICAST, NULL, 1, BGP_PATH_MULTIPATH); +} + +ALIAS (show_ip_bgp_ipv4_prefix_pathtype, + show_bgp_ipv4_safi_prefix_pathtype_cmd, + "show bgp ipv4 (unicast|multicast) A.B.C.D/M (bestpath|multipath)", + SHOW_STR + BGP_STR + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + "IP prefix /, e.g., 35.0.0.0/8\n" + "Display only the bestpath\n" + "Display only multipaths\n") DEFUN (show_ip_bgp_vpnv4_all_prefix, show_ip_bgp_vpnv4_all_prefix_cmd, @@ -6632,7 +7231,7 @@ DEFUN (show_ip_bgp_vpnv4_all_prefix, "Display information about all VPNv4 NLRIs\n" "IP prefix /, e.g., 35.0.0.0/8\n") { - return bgp_show_route (vty, NULL, argv[0], AFI_IP, SAFI_MPLS_VPN, NULL, 1); + return bgp_show_route (vty, NULL, argv[0], AFI_IP, SAFI_MPLS_VPN, NULL, 1, BGP_PATH_ALL); } DEFUN (show_ip_bgp_vpnv4_rd_prefix, @@ -6655,7 +7254,7 @@ DEFUN (show_ip_bgp_vpnv4_rd_prefix, vty_out (vty, "%% Malformed Route Distinguisher%s", VTY_NEWLINE); return CMD_WARNING; } - return bgp_show_route (vty, NULL, argv[1], AFI_IP, SAFI_MPLS_VPN, &prd, 1); + return bgp_show_route (vty, NULL, argv[1], AFI_IP, SAFI_MPLS_VPN, &prd, 1, BGP_PATH_ALL); } DEFUN (show_ip_bgp_view, @@ -6665,7 +7264,7 @@ DEFUN (show_ip_bgp_view, IP_STR BGP_STR "BGP view\n" - "BGP view name\n") + "View name\n") { struct bgp *bgp; @@ -6687,10 +7286,10 @@ 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); + return bgp_show_route (vty, argv[0], argv[1], AFI_IP, SAFI_UNICAST, NULL, 0, BGP_PATH_ALL); } DEFUN (show_ip_bgp_view_prefix, @@ -6700,13 +7299,12 @@ 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); + return bgp_show_route (vty, argv[0], argv[1], AFI_IP, SAFI_UNICAST, NULL, 1, BGP_PATH_ALL); } -#ifdef HAVE_IPV6 DEFUN (show_bgp, show_bgp_cmd, "show bgp", @@ -6724,22 +7322,6 @@ ALIAS (show_bgp, BGP_STR "Address family\n") -DEFUN (show_bgp_ipv6_safi, - show_bgp_ipv6_safi_cmd, - "show bgp ipv6 (unicast|multicast)", - SHOW_STR - BGP_STR - "Address family\n" - "Address Family modifier\n" - "Address Family modifier\n") -{ - if (strncmp (argv[0], "m", 1) == 0) - return bgp_show (vty, NULL, AFI_IP6, SAFI_MULTICAST, bgp_show_type_normal, - NULL); - - return bgp_show (vty, NULL, AFI_IP6, SAFI_UNICAST, bgp_show_type_normal, NULL); -} - /* old command */ DEFUN (show_ipv6_bgp, show_ipv6_bgp_cmd, @@ -6759,20 +7341,28 @@ DEFUN (show_bgp_route, BGP_STR "Network in the BGP routing table to display\n") { - return bgp_show_route (vty, NULL, argv[0], AFI_IP6, SAFI_UNICAST, NULL, 0); + return bgp_show_route (vty, NULL, argv[0], AFI_IP6, SAFI_UNICAST, NULL, 0, BGP_PATH_ALL); } -ALIAS (show_bgp_route, - show_bgp_ipv6_route_cmd, - "show bgp ipv6 X:X::X:X", +DEFUN (show_bgp_ipv4_safi, + show_bgp_ipv4_safi_cmd, + "show bgp ipv4 (unicast|multicast)", SHOW_STR BGP_STR "Address family\n" - "Network in the BGP routing table to display\n") + "Address Family modifier\n" + "Address Family modifier\n") +{ + if (strncmp (argv[0], "m", 1) == 0) + return bgp_show (vty, NULL, AFI_IP, SAFI_MULTICAST, bgp_show_type_normal, + NULL); + + return bgp_show (vty, NULL, AFI_IP, SAFI_UNICAST, bgp_show_type_normal, NULL); +} -DEFUN (show_bgp_ipv6_safi_route, - show_bgp_ipv6_safi_route_cmd, - "show bgp ipv6 (unicast|multicast) X:X::X:X", +DEFUN (show_bgp_ipv4_safi_route, + show_bgp_ipv4_safi_route_cmd, + "show bgp ipv4 (unicast|multicast) A.B.C.D", SHOW_STR BGP_STR "Address family\n" @@ -6781,3068 +7371,6600 @@ DEFUN (show_bgp_ipv6_safi_route, "Network in the BGP routing table to display\n") { if (strncmp (argv[0], "m", 1) == 0) - return bgp_show_route (vty, NULL, argv[1], AFI_IP6, SAFI_MULTICAST, NULL, 0); - - return bgp_show_route (vty, NULL, argv[1], AFI_IP6, SAFI_UNICAST, NULL, 0); -} + return bgp_show_route (vty, NULL, argv[1], AFI_IP, SAFI_MULTICAST, NULL, 0, BGP_PATH_ALL); -/* old command */ -DEFUN (show_ipv6_bgp_route, - show_ipv6_bgp_route_cmd, - "show ipv6 bgp X:X::X:X", - SHOW_STR - IP_STR - BGP_STR - "Network in the BGP routing table to display\n") -{ - return bgp_show_route (vty, NULL, argv[0], AFI_IP6, SAFI_UNICAST, NULL, 0); + return bgp_show_route (vty, NULL, argv[1], AFI_IP, SAFI_UNICAST, NULL, 0, BGP_PATH_ALL); } -DEFUN (show_bgp_prefix, - show_bgp_prefix_cmd, - "show bgp X:X::X:X/M", +DEFUN (show_bgp_route_pathtype, + show_bgp_route_pathtype_cmd, + "show bgp X:X::X:X (bestpath|multipath)", SHOW_STR BGP_STR - "IPv6 prefix /\n") + "Network in the BGP routing table to display\n" + "Display only the bestpath\n" + "Display only multipaths\n") { - return bgp_show_route (vty, NULL, argv[0], AFI_IP6, SAFI_UNICAST, NULL, 1); + if (strncmp (argv[1], "b", 1) == 0) + return bgp_show_route (vty, NULL, argv[0], AFI_IP6, SAFI_UNICAST, NULL, 0, BGP_PATH_BESTPATH); + else + return bgp_show_route (vty, NULL, argv[0], AFI_IP6, SAFI_UNICAST, NULL, 0, BGP_PATH_MULTIPATH); } -ALIAS (show_bgp_prefix, - show_bgp_ipv6_prefix_cmd, - "show bgp ipv6 X:X::X:X/M", +ALIAS (show_bgp_route_pathtype, + show_bgp_ipv6_route_pathtype_cmd, + "show bgp ipv6 X:X::X:X (bestpath|multipath)", SHOW_STR BGP_STR "Address family\n" - "IPv6 prefix /\n") + "Network in the BGP routing table to display\n" + "Display only the bestpath\n" + "Display only multipaths\n") -DEFUN (show_bgp_ipv6_safi_prefix, - show_bgp_ipv6_safi_prefix_cmd, - "show bgp ipv6 (unicast|multicast) X:X::X:X/M", +DEFUN (show_bgp_ipv6_safi_route_pathtype, + show_bgp_ipv6_safi_route_pathtype_cmd, + "show bgp ipv6 (unicast|multicast) X:X::X:X (bestpath|multipath)", SHOW_STR BGP_STR "Address family\n" "Address Family modifier\n" "Address Family modifier\n" - "IPv6 prefix /, e.g., 3ffe::/16\n") + "Network in the BGP routing table to display\n" + "Display only the bestpath\n" + "Display only multipaths\n") { if (strncmp (argv[0], "m", 1) == 0) - return bgp_show_route (vty, NULL, argv[1], AFI_IP6, SAFI_MULTICAST, NULL, 1); - - return bgp_show_route (vty, NULL, argv[1], AFI_IP6, SAFI_UNICAST, NULL, 1); + if (strncmp (argv[2], "b", 1) == 0) + return bgp_show_route (vty, NULL, argv[1], AFI_IP6, SAFI_MULTICAST, NULL, 0, BGP_PATH_BESTPATH); + else + return bgp_show_route (vty, NULL, argv[1], AFI_IP6, SAFI_MULTICAST, NULL, 0, BGP_PATH_MULTIPATH); + else + if (strncmp (argv[2], "b", 1) == 0) + return bgp_show_route (vty, NULL, argv[1], AFI_IP6, SAFI_UNICAST, NULL, 0, BGP_PATH_BESTPATH); + else + return bgp_show_route (vty, NULL, argv[1], AFI_IP6, SAFI_UNICAST, NULL, 0, BGP_PATH_MULTIPATH); } -/* old command */ -DEFUN (show_ipv6_bgp_prefix, - show_ipv6_bgp_prefix_cmd, - "show ipv6 bgp X:X::X:X/M", +DEFUN (show_bgp_ipv4_vpn_route, + show_bgp_ipv4_vpn_route_cmd, + "show bgp ipv4 vpn A.B.C.D", SHOW_STR - IP_STR BGP_STR - "IPv6 prefix /, e.g., 3ffe::/16\n") + "Address Family\n" + "Display VPN NLRI specific information\n" + "Network in the BGP routing table to display\n") { - return bgp_show_route (vty, NULL, argv[0], AFI_IP6, SAFI_UNICAST, NULL, 1); + return bgp_show_route (vty, NULL, argv[0], AFI_IP, SAFI_MPLS_VPN, NULL, 0, BGP_PATH_ALL); } -DEFUN (show_bgp_view, - show_bgp_view_cmd, - "show bgp view WORD", +DEFUN (show_bgp_ipv6_vpn_route, + show_bgp_ipv6_vpn_route_cmd, + "show bgp ipv6 vpn X:X::X:X", SHOW_STR BGP_STR - "BGP view\n" - "View name\n") + "Address Family\n" + "Display VPN NLRI specific information\n" + "Network in the BGP routing table to display\n") { - struct bgp *bgp; - - /* BGP structure lookup. */ - bgp = bgp_lookup_by_name (argv[0]); - if (bgp == NULL) - { - vty_out (vty, "Can't find BGP view %s%s", argv[0], VTY_NEWLINE); - return CMD_WARNING; - } - - return bgp_show (vty, bgp, AFI_IP6, SAFI_UNICAST, bgp_show_type_normal, NULL); + return bgp_show_route (vty, NULL, argv[0], AFI_IP6, SAFI_MPLS_VPN, NULL, 0, BGP_PATH_ALL); } -ALIAS (show_bgp_view, - show_bgp_view_ipv6_cmd, - "show bgp view WORD ipv6", - SHOW_STR - BGP_STR - "BGP view\n" - "View name\n" - "Address family\n") - -DEFUN (show_bgp_view_route, - show_bgp_view_route_cmd, - "show bgp view WORD X:X::X:X", +DEFUN (show_bgp_ipv4_vpn_rd_route, + show_bgp_ipv4_vpn_rd_route_cmd, + "show bgp ipv4 vpn rd ASN:nn_or_IP-address:nn A.B.C.D", SHOW_STR BGP_STR - "BGP view\n" - "View name\n" + IP_STR + "Display VPN NLRI specific information\n" + "Display information for a route distinguisher\n" + "VPN Route Distinguisher\n" "Network in the BGP routing table to display\n") { - return bgp_show_route (vty, argv[0], argv[1], AFI_IP6, SAFI_UNICAST, NULL, 0); + int ret; + struct prefix_rd prd; + + ret = str2prefix_rd (argv[0], &prd); + if (! ret) + { + vty_out (vty, "%% Malformed Route Distinguisher%s", VTY_NEWLINE); + return CMD_WARNING; + } + return bgp_show_route (vty, NULL, argv[1], AFI_IP, SAFI_MPLS_VPN, &prd, 0, BGP_PATH_ALL); } -ALIAS (show_bgp_view_route, - show_bgp_view_ipv6_route_cmd, - "show bgp view WORD ipv6 X:X::X:X", +DEFUN (show_bgp_ipv6_vpn_rd_route, + show_bgp_ipv6_vpn_rd_route_cmd, + "show bgp ipv6 vpn rd ASN:nn_or_IP-address:nn X:X::X:X", SHOW_STR BGP_STR - "BGP view\n" - "View name\n" - "Address family\n" + "Address Family\n" + "Display VPN NLRI specific information\n" + "Display information for a route distinguisher\n" + "VPN Route Distinguisher\n" "Network in the BGP routing table to display\n") +{ + int ret; + struct prefix_rd prd; -DEFUN (show_bgp_view_prefix, - show_bgp_view_prefix_cmd, - "show bgp view WORD X:X::X:X/M", + ret = str2prefix_rd (argv[0], &prd); + if (! ret) + { + vty_out (vty, "%% Malformed Route Distinguisher%s", VTY_NEWLINE); + return CMD_WARNING; + } + return bgp_show_route (vty, NULL, argv[1], AFI_IP6, SAFI_MPLS_VPN, &prd, 0, BGP_PATH_ALL); +} + +DEFUN (show_bgp_prefix_pathtype, + show_bgp_prefix_pathtype_cmd, + "show bgp X:X::X:X/M (bestpath|multipath)", SHOW_STR BGP_STR - "BGP view\n" - "View name\n" - "IPv6 prefix /\n") + "IPv6 prefix /\n" + "Display only the bestpath\n" + "Display only multipaths\n") { - return bgp_show_route (vty, argv[0], argv[1], AFI_IP6, SAFI_UNICAST, NULL, 1); + if (strncmp (argv[1], "b", 1) == 0) + return bgp_show_route (vty, NULL, argv[0], AFI_IP6, SAFI_UNICAST, NULL, 1, BGP_PATH_BESTPATH); + else + return bgp_show_route (vty, NULL, argv[0], AFI_IP6, SAFI_UNICAST, NULL, 1, BGP_PATH_MULTIPATH); } -ALIAS (show_bgp_view_prefix, - show_bgp_view_ipv6_prefix_cmd, - "show bgp view WORD ipv6 X:X::X:X/M", +ALIAS (show_bgp_prefix_pathtype, + show_bgp_ipv6_prefix_pathtype_cmd, + "show bgp ipv6 X:X::X:X/M (bestpath|multipath)", SHOW_STR BGP_STR - "BGP view\n" - "View name\n" "Address family\n" - "IPv6 prefix /\n") + "IPv6 prefix /\n" + "Display only the bestpath\n" + "Display only multipaths\n") -/* old command */ -DEFUN (show_ipv6_mbgp, - show_ipv6_mbgp_cmd, - "show ipv6 mbgp", +DEFUN (show_bgp_ipv6_safi_prefix_pathtype, + show_bgp_ipv6_safi_prefix_pathtype_cmd, + "show bgp ipv6 (unicast|multicast) X:X::X:X/M (bestpath|multipath)", SHOW_STR - IP_STR - MBGP_STR) + BGP_STR + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + "IPv6 prefix /, e.g., 3ffe::/16\n" + "Display only the bestpath\n" + "Display only multipaths\n") { - return bgp_show (vty, NULL, AFI_IP6, SAFI_MULTICAST, bgp_show_type_normal, - NULL); + if (strncmp (argv[0], "m", 1) == 0) + if (strncmp (argv[2], "b", 1) == 0) + return bgp_show_route (vty, NULL, argv[1], AFI_IP6, SAFI_MULTICAST, NULL, 1, BGP_PATH_BESTPATH); + else + return bgp_show_route (vty, NULL, argv[1], AFI_IP6, SAFI_MULTICAST, NULL, 1, BGP_PATH_MULTIPATH); + else + if (strncmp (argv[2], "b", 1) == 0) + return bgp_show_route (vty, NULL, argv[1], AFI_IP6, SAFI_UNICAST, NULL, 1, BGP_PATH_BESTPATH); + else + return bgp_show_route (vty, NULL, argv[1], AFI_IP6, SAFI_UNICAST, NULL, 1, BGP_PATH_MULTIPATH); } -/* old command */ -DEFUN (show_ipv6_mbgp_route, - show_ipv6_mbgp_route_cmd, - "show ipv6 mbgp X:X::X:X", +DEFUN (show_bgp_ipv4_encap_route, + show_bgp_ipv4_encap_route_cmd, + "show bgp ipv4 encap A.B.C.D", SHOW_STR + BGP_STR IP_STR - MBGP_STR - "Network in the MBGP routing table to display\n") + "Display ENCAP NLRI specific information\n" + "Network in the BGP routing table to display\n") { - return bgp_show_route (vty, NULL, argv[0], AFI_IP6, SAFI_MULTICAST, NULL, 0); + return bgp_show_route (vty, NULL, argv[0], AFI_IP, SAFI_ENCAP, NULL, 0, BGP_PATH_ALL); } -/* old command */ -DEFUN (show_ipv6_mbgp_prefix, - show_ipv6_mbgp_prefix_cmd, - "show ipv6 mbgp X:X::X:X/M", +DEFUN (show_bgp_ipv6_encap_route, + show_bgp_ipv6_encap_route_cmd, + "show bgp ipv6 encap X:X::X:X", SHOW_STR - IP_STR - MBGP_STR - "IPv6 prefix /, e.g., 3ffe::/16\n") + BGP_STR + IP6_STR + "Display ENCAP NLRI specific information\n" + "Network in the BGP routing table to display\n") { - return bgp_show_route (vty, NULL, argv[0], AFI_IP6, SAFI_MULTICAST, NULL, 1); + return bgp_show_route (vty, NULL, argv[0], AFI_IP6, SAFI_ENCAP, NULL, 0, BGP_PATH_ALL); } -#endif - -static int -bgp_show_regexp (struct vty *vty, int argc, const char **argv, afi_t afi, - safi_t safi, enum bgp_show_type type) +DEFUN (show_bgp_ipv4_safi_rd_route, + show_bgp_ipv4_safi_rd_route_cmd, + "show bgp ipv4 (encap|vpn) rd ASN:nn_or_IP-address:nn A.B.C.D", + SHOW_STR + BGP_STR + "Address Family\n" + "Address Family Modifier\n" + "Address Family Modifier\n" + "Display information for a route distinguisher\n" + "ENCAP Route Distinguisher\n" + "Network in the BGP routing table to display\n") { - int i; - struct buffer *b; - char *regstr; - int first; - regex_t *regex; - int rc; - - first = 0; - b = buffer_new (1024); - for (i = 0; i < argc; i++) - { - if (first) - buffer_putc (b, ' '); - else - { - if ((strcmp (argv[i], "unicast") == 0) || (strcmp (argv[i], "multicast") == 0)) - continue; - first = 1; - } - - buffer_putstr (b, argv[i]); - } - buffer_putc (b, '\0'); - - regstr = buffer_getstr (b); - buffer_free (b); + int ret; + struct prefix_rd prd; + safi_t safi; - regex = bgp_regcomp (regstr); - XFREE(MTYPE_TMP, regstr); - if (! regex) + if (bgp_parse_safi(argv[0], &safi)) { + vty_out (vty, "Error: Bad SAFI: %s%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + ret = str2prefix_rd (argv[1], &prd); + if (! ret) { - vty_out (vty, "Can't compile regexp %s%s", argv[0], - VTY_NEWLINE); + vty_out (vty, "%% Malformed Route Distinguisher%s", VTY_NEWLINE); return CMD_WARNING; } - - rc = bgp_show (vty, NULL, afi, safi, type, regex); - bgp_regex_free (regex); - return rc; + return bgp_show_route (vty, NULL, argv[2], AFI_IP, safi, &prd, 0, BGP_PATH_ALL); } -DEFUN (show_ip_bgp_regexp, - show_ip_bgp_regexp_cmd, - "show ip bgp regexp .LINE", +DEFUN (show_bgp_ipv6_safi_rd_route, + show_bgp_ipv6_safi_rd_route_cmd, + "show bgp ipv6 (encap|vpn) rd ASN:nn_or_IP-address:nn X:X::X:X", SHOW_STR - IP_STR BGP_STR - "Display routes matching the AS path regular expression\n" - "A regular-expression to match the BGP AS paths\n") + "Address Family\n" + "Address Family Modifier\n" + "Address Family Modifier\n" + "Display information for a route distinguisher\n" + "ENCAP Route Distinguisher\n" + "Network in the BGP routing table to display\n") { - return bgp_show_regexp (vty, argc, argv, AFI_IP, SAFI_UNICAST, - bgp_show_type_regexp); + int ret; + struct prefix_rd prd; + safi_t safi; + + if (bgp_parse_safi(argv[0], &safi)) { + vty_out (vty, "Error: Bad SAFI: %s%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + ret = str2prefix_rd (argv[1], &prd); + if (! ret) + { + vty_out (vty, "%% Malformed Route Distinguisher%s", VTY_NEWLINE); + return CMD_WARNING; + } + return bgp_show_route (vty, NULL, argv[2], AFI_IP6, SAFI_ENCAP, &prd, 0, BGP_PATH_ALL); } -DEFUN (show_ip_bgp_flap_regexp, - show_ip_bgp_flap_regexp_cmd, - "show ip bgp flap-statistics regexp .LINE", +DEFUN (show_bgp_ipv4_prefix, + show_bgp_ipv4_prefix_cmd, + "show bgp ipv4 A.B.C.D/M", SHOW_STR - IP_STR BGP_STR - "Display flap statistics of routes\n" - "Display routes matching the AS path regular expression\n" - "A regular-expression to match the BGP AS paths\n") + IP_STR + "IP prefix /, e.g., 35.0.0.0/8\n") { - return bgp_show_regexp (vty, argc, argv, AFI_IP, SAFI_UNICAST, - bgp_show_type_flap_regexp); + return bgp_show_route (vty, NULL, argv[0], AFI_IP, SAFI_UNICAST, NULL, 1, BGP_PATH_ALL); } -DEFUN (show_ip_bgp_ipv4_regexp, - show_ip_bgp_ipv4_regexp_cmd, - "show ip bgp ipv4 (unicast|multicast) regexp .LINE", +DEFUN (show_bgp_ipv4_safi_prefix, + show_bgp_ipv4_safi_prefix_cmd, + "show bgp ipv4 (unicast|multicast) A.B.C.D/M", SHOW_STR - IP_STR BGP_STR "Address family\n" "Address Family modifier\n" "Address Family modifier\n" - "Display routes matching the AS path regular expression\n" - "A regular-expression to match the BGP AS paths\n") + "IP prefix /, e.g., 35.0.0.0/8\n") { if (strncmp (argv[0], "m", 1) == 0) - return bgp_show_regexp (vty, argc, argv, AFI_IP, SAFI_MULTICAST, - bgp_show_type_regexp); + return bgp_show_route (vty, NULL, argv[1], AFI_IP, SAFI_MULTICAST, NULL, 1, BGP_PATH_ALL); - return bgp_show_regexp (vty, argc, argv, AFI_IP, SAFI_UNICAST, - bgp_show_type_regexp); + return bgp_show_route (vty, NULL, argv[1], AFI_IP, SAFI_UNICAST, NULL, 1, BGP_PATH_ALL); } -#ifdef HAVE_IPV6 -DEFUN (show_bgp_regexp, - show_bgp_regexp_cmd, - "show bgp regexp .LINE", +DEFUN (show_bgp_ipv4_vpn_prefix, + show_bgp_ipv4_vpn_prefix_cmd, + "show bgp ipv4 vpn A.B.C.D/M", SHOW_STR BGP_STR - "Display routes matching the AS path regular expression\n" - "A regular-expression to match the BGP AS paths\n") + IP_STR + "Display VPN NLRI specific information\n" + "IP prefix /, e.g., 35.0.0.0/8\n") { - return bgp_show_regexp (vty, argc, argv, AFI_IP6, SAFI_UNICAST, - bgp_show_type_regexp); + return bgp_show_route (vty, NULL, argv[0], AFI_IP, SAFI_MPLS_VPN, NULL, 1, BGP_PATH_ALL); } -ALIAS (show_bgp_regexp, - show_bgp_ipv6_regexp_cmd, - "show bgp ipv6 regexp .LINE", +DEFUN (show_bgp_ipv6_vpn_prefix, + show_bgp_ipv6_vpn_prefix_cmd, + "show bgp ipv6 vpn X:X::X:X/M", SHOW_STR BGP_STR - "Address family\n" - "Display routes matching the AS path regular expression\n" - "A regular-expression to match the BGP AS paths\n") + "Address Family\n" + "Display VPN NLRI specific information\n" + "IP prefix /, e.g., 35.0.0.0/8\n") +{ + return bgp_show_route (vty, NULL, argv[0], AFI_IP6, SAFI_MPLS_VPN, NULL, 1, BGP_PATH_ALL); +} -/* old command */ -DEFUN (show_ipv6_bgp_regexp, - show_ipv6_bgp_regexp_cmd, - "show ipv6 bgp regexp .LINE", +DEFUN (show_bgp_ipv4_encap_prefix, + show_bgp_ipv4_encap_prefix_cmd, + "show bgp ipv4 encap A.B.C.D/M", SHOW_STR - IP_STR BGP_STR - "Display routes matching the AS path regular expression\n" - "A regular-expression to match the BGP AS paths\n") + IP_STR + "Display ENCAP NLRI specific information\n" + "Display information about ENCAP NLRIs\n" + "IP prefix /, e.g., 35.0.0.0/8\n") { - return bgp_show_regexp (vty, argc, argv, AFI_IP6, SAFI_UNICAST, - bgp_show_type_regexp); + return bgp_show_route (vty, NULL, argv[0], AFI_IP, SAFI_ENCAP, NULL, 1, BGP_PATH_ALL); } -/* old command */ -DEFUN (show_ipv6_mbgp_regexp, - show_ipv6_mbgp_regexp_cmd, - "show ipv6 mbgp regexp .LINE", +DEFUN (show_bgp_ipv6_encap_prefix, + show_bgp_ipv6_encap_prefix_cmd, + "show bgp ipv6 encap X:X::X:X/M", SHOW_STR - IP_STR BGP_STR - "Display routes matching the AS path regular expression\n" - "A regular-expression to match the MBGP AS paths\n") + IP_STR + "Display ENCAP NLRI specific information\n" + "Display information about ENCAP NLRIs\n" + "IP prefix /, e.g., 35.0.0.0/8\n") { - return bgp_show_regexp (vty, argc, argv, AFI_IP6, SAFI_MULTICAST, - bgp_show_type_regexp); + return bgp_show_route (vty, NULL, argv[0], AFI_IP6, SAFI_ENCAP, NULL, 1, BGP_PATH_ALL); } -#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) + +DEFUN (show_bgp_ipv4_safi_rd_prefix, + show_bgp_ipv4_safi_rd_prefix_cmd, + "show bgp ipv4 (encap|vpn) rd ASN:nn_or_IP-address:nn A.B.C.D/M", + SHOW_STR + BGP_STR + "Address Family\n" + "Address Family Modifier\n" + "Address Family Modifier\n" + "Display information for a route distinguisher\n" + "ENCAP Route Distinguisher\n" + "IP prefix /, e.g., 35.0.0.0/8\n") { - struct prefix_list *plist; + int ret; + struct prefix_rd prd; + safi_t safi; - plist = prefix_list_lookup (afi, prefix_list_str); - if (plist == NULL) + if (bgp_parse_safi(argv[0], &safi)) { + vty_out (vty, "Error: Bad SAFI: %s%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + + ret = str2prefix_rd (argv[1], &prd); + if (! ret) { - vty_out (vty, "%% %s is not a valid prefix-list name%s", - prefix_list_str, VTY_NEWLINE); + vty_out (vty, "%% Malformed Route Distinguisher%s", VTY_NEWLINE); return CMD_WARNING; } - - return bgp_show (vty, NULL, afi, safi, type, plist); + return bgp_show_route (vty, NULL, argv[2], AFI_IP, safi, &prd, 1, BGP_PATH_ALL); } -DEFUN (show_ip_bgp_prefix_list, - show_ip_bgp_prefix_list_cmd, - "show ip bgp prefix-list WORD", +DEFUN (show_bgp_ipv6_safi_rd_prefix, + show_bgp_ipv6_safi_rd_prefix_cmd, + "show bgp ipv6 (encap|vpn) rd ASN:nn_or_IP-address:nn X:X::X:X/M", SHOW_STR - IP_STR BGP_STR - "Display routes conforming to the prefix-list\n" - "IP prefix-list name\n") + "Address Family\n" + "Address Family Modifier\n" + "Address Family Modifier\n" + "Display information for a route distinguisher\n" + "ENCAP Route Distinguisher\n" + "IP prefix /, e.g., 35.0.0.0/8\n") { - return bgp_show_prefix_list (vty, argv[0], AFI_IP, SAFI_UNICAST, - bgp_show_type_prefix_list); + int ret; + struct prefix_rd prd; + safi_t safi; + + if (bgp_parse_safi(argv[0], &safi)) { + vty_out (vty, "Error: Bad SAFI: %s%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + + ret = str2prefix_rd (argv[1], &prd); + if (! ret) + { + vty_out (vty, "%% Malformed Route Distinguisher%s", VTY_NEWLINE); + return CMD_WARNING; + } + return bgp_show_route (vty, NULL, argv[2], AFI_IP6, safi, &prd, 1, BGP_PATH_ALL); } -DEFUN (show_ip_bgp_flap_prefix_list, - show_ip_bgp_flap_prefix_list_cmd, - "show ip bgp flap-statistics prefix-list WORD", +DEFUN (show_bgp_afi_safi_view, + show_bgp_afi_safi_view_cmd, + "show bgp view WORD (ipv4|ipv6) (encap|mulicast|unicast|vpn)", SHOW_STR - IP_STR BGP_STR - "Display flap statistics of routes\n" - "Display routes conforming to the prefix-list\n" - "IP prefix-list name\n") + "BGP view\n" + "BGP view name\n" + "Address Family\n" + "Address Family\n" + "Address Family Modifier\n" + "Address Family Modifier\n" + "Address Family Modifier\n" + "Address Family Modifier\n" + ) { - return bgp_show_prefix_list (vty, argv[0], AFI_IP, SAFI_UNICAST, - bgp_show_type_flap_prefix_list); + struct bgp *bgp; + safi_t safi; + afi_t afi; + + if (bgp_parse_afi(argv[1], &afi)) { + vty_out (vty, "Error: Bad AFI: %s%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + if (bgp_parse_safi(argv[2], &safi)) { + vty_out (vty, "Error: Bad SAFI: %s%s", argv[1], VTY_NEWLINE); + return CMD_WARNING; + } + + /* BGP structure lookup. */ + bgp = bgp_lookup_by_name (argv[0]); + if (bgp == NULL) + { + vty_out (vty, "Can't find BGP view %s%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + + return bgp_show (vty, bgp, afi, safi, bgp_show_type_normal, NULL); } -DEFUN (show_ip_bgp_ipv4_prefix_list, - show_ip_bgp_ipv4_prefix_list_cmd, - "show ip bgp ipv4 (unicast|multicast) prefix-list WORD", +DEFUN (show_bgp_view_afi_safi_route, + show_bgp_view_afi_safi_route_cmd, + "show bgp view WORD (ipv4|ipv6) (encap|multicast|unicast|vpn) A.B.C.D", SHOW_STR - IP_STR BGP_STR - "Address family\n" - "Address Family modifier\n" - "Address Family modifier\n" - "Display routes conforming to the prefix-list\n" - "IP prefix-list name\n") + "BGP view\n" + "View name\n" + "Address Family\n" + "Address Family\n" + "Address Family Modifier\n" + "Address Family Modifier\n" + "Address Family Modifier\n" + "Address Family Modifier\n" + "Network in the BGP routing table to display\n") { - if (strncmp (argv[0], "m", 1) == 0) - return bgp_show_prefix_list (vty, argv[1], AFI_IP, SAFI_MULTICAST, - bgp_show_type_prefix_list); + safi_t safi; + afi_t afi; - return bgp_show_prefix_list (vty, argv[1], AFI_IP, SAFI_UNICAST, - bgp_show_type_prefix_list); + if (bgp_parse_afi(argv[1], &afi)) { + vty_out (vty, "Error: Bad AFI: %s%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + if (bgp_parse_safi(argv[2], &safi)) { + vty_out (vty, "Error: Bad SAFI: %s%s", argv[1], VTY_NEWLINE); + return CMD_WARNING; + } + return bgp_show_route (vty, argv[0], argv[3], afi, safi, NULL, 0, BGP_PATH_ALL); } -#ifdef HAVE_IPV6 -DEFUN (show_bgp_prefix_list, - show_bgp_prefix_list_cmd, - "show bgp prefix-list WORD", +DEFUN (show_bgp_view_afi_safi_prefix, + show_bgp_view_afi_safi_prefix_cmd, + "show bgp view WORD (ipv4|ipv6) (encap|multicast|unicast|vpn) A.B.C.D/M", SHOW_STR BGP_STR - "Display routes conforming to the prefix-list\n" - "IPv6 prefix-list name\n") + "BGP view\n" + "View name\n" + "Address Family\n" + "Address Family\n" + "Address Family Modifier\n" + "Address Family Modifier\n" + "Address Family Modifier\n" + "Address Family Modifier\n" + "IP prefix /, e.g., 35.0.0.0/8\n") { - return bgp_show_prefix_list (vty, argv[0], AFI_IP6, SAFI_UNICAST, - bgp_show_type_prefix_list); + safi_t safi; + afi_t afi; + + if (bgp_parse_afi(argv[1], &afi)) { + vty_out (vty, "Error: Bad AFI: %s%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + if (bgp_parse_safi(argv[2], &safi)) { + vty_out (vty, "Error: Bad SAFI: %s%s", argv[1], VTY_NEWLINE); + return CMD_WARNING; + } + return bgp_show_route (vty, argv[0], argv[3], afi, safi, NULL, 1, BGP_PATH_ALL); } -ALIAS (show_bgp_prefix_list, - show_bgp_ipv6_prefix_list_cmd, - "show bgp ipv6 prefix-list WORD", +/* new001 */ +DEFUN (show_bgp_afi, + show_bgp_afi_cmd, + "show bgp (ipv4|ipv6)", SHOW_STR BGP_STR "Address family\n" - "Display routes conforming to the prefix-list\n" - "IPv6 prefix-list name\n") - -/* old command */ -DEFUN (show_ipv6_bgp_prefix_list, - show_ipv6_bgp_prefix_list_cmd, - "show ipv6 bgp prefix-list WORD", - SHOW_STR - IPV6_STR - BGP_STR - "Display routes matching the prefix-list\n" - "IPv6 prefix-list name\n") -{ - return bgp_show_prefix_list (vty, argv[0], AFI_IP6, SAFI_UNICAST, - bgp_show_type_prefix_list); -} - -/* old command */ -DEFUN (show_ipv6_mbgp_prefix_list, - show_ipv6_mbgp_prefix_list_cmd, - "show ipv6 mbgp prefix-list WORD", - SHOW_STR - IPV6_STR - MBGP_STR - "Display routes matching the prefix-list\n" - "IPv6 prefix-list name\n") -{ - return bgp_show_prefix_list (vty, argv[0], AFI_IP6, SAFI_MULTICAST, - 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) + "Address family\n") { - struct as_list *as_list; - - as_list = as_list_lookup (filter); - if (as_list == NULL) - { - vty_out (vty, "%% %s is not a valid AS-path access-list name%s", filter, VTY_NEWLINE); - return CMD_WARNING; - } + afi_t afi; - return bgp_show (vty, NULL, afi, safi, type, as_list); + if (bgp_parse_afi(argv[0], &afi)) { + vty_out (vty, "Error: Bad AFI: %s%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + return bgp_show (vty, NULL, afi, SAFI_UNICAST, bgp_show_type_normal, + NULL); } -DEFUN (show_ip_bgp_filter_list, - show_ip_bgp_filter_list_cmd, - "show ip bgp filter-list WORD", +DEFUN (show_bgp_ipv6_safi, + show_bgp_ipv6_safi_cmd, + "show bgp ipv6 (unicast|multicast)", SHOW_STR - IP_STR BGP_STR - "Display routes conforming to the filter-list\n" - "Regular expression access list name\n") + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n") { - return bgp_show_filter_list (vty, argv[0], AFI_IP, SAFI_UNICAST, - bgp_show_type_filter_list); + if (strncmp (argv[0], "m", 1) == 0) + return bgp_show (vty, NULL, AFI_IP6, SAFI_MULTICAST, bgp_show_type_normal, + NULL); + + return bgp_show (vty, NULL, AFI_IP6, SAFI_UNICAST, bgp_show_type_normal, NULL); } -DEFUN (show_ip_bgp_flap_filter_list, - show_ip_bgp_flap_filter_list_cmd, - "show ip bgp flap-statistics filter-list WORD", +DEFUN (show_bgp_ipv6_route, + show_bgp_ipv6_route_cmd, + "show bgp ipv6 X:X::X:X", SHOW_STR - IP_STR BGP_STR - "Display flap statistics of routes\n" - "Display routes conforming to the filter-list\n" - "Regular expression access list name\n") + "Address family\n" + "Network in the BGP routing table to display\n") { - return bgp_show_filter_list (vty, argv[0], AFI_IP, SAFI_UNICAST, - bgp_show_type_flap_filter_list); + return bgp_show_route (vty, NULL, argv[0], AFI_IP6, SAFI_UNICAST, NULL, 0, BGP_PATH_ALL); } -DEFUN (show_ip_bgp_ipv4_filter_list, - show_ip_bgp_ipv4_filter_list_cmd, - "show ip bgp ipv4 (unicast|multicast) filter-list WORD", +DEFUN (show_bgp_ipv6_safi_route, + show_bgp_ipv6_safi_route_cmd, + "show bgp ipv6 (unicast|multicast) X:X::X:X", SHOW_STR - IP_STR BGP_STR "Address family\n" "Address Family modifier\n" "Address Family modifier\n" - "Display routes conforming to the filter-list\n" - "Regular expression access list name\n") + "Network in the BGP routing table to display\n") { if (strncmp (argv[0], "m", 1) == 0) - return bgp_show_filter_list (vty, argv[1], AFI_IP, SAFI_MULTICAST, - bgp_show_type_filter_list); - - return bgp_show_filter_list (vty, argv[1], AFI_IP, SAFI_UNICAST, - bgp_show_type_filter_list); + return bgp_show_route (vty, NULL, argv[1], AFI_IP6, SAFI_MULTICAST, NULL, 0, BGP_PATH_ALL); + + return bgp_show_route (vty, NULL, argv[1], AFI_IP6, SAFI_UNICAST, NULL, 0, BGP_PATH_ALL); } -#ifdef HAVE_IPV6 -DEFUN (show_bgp_filter_list, - show_bgp_filter_list_cmd, - "show bgp filter-list WORD", +/* old command */ +DEFUN (show_ipv6_bgp_route, + show_ipv6_bgp_route_cmd, + "show ipv6 bgp X:X::X:X", SHOW_STR + IP_STR BGP_STR - "Display routes conforming to the filter-list\n" - "Regular expression access list name\n") + "Network in the BGP routing table to display\n") { - return bgp_show_filter_list (vty, argv[0], AFI_IP6, SAFI_UNICAST, - bgp_show_type_filter_list); + return bgp_show_route (vty, NULL, argv[0], AFI_IP6, SAFI_UNICAST, NULL, 0, BGP_PATH_ALL); } -ALIAS (show_bgp_filter_list, - show_bgp_ipv6_filter_list_cmd, - "show bgp ipv6 filter-list WORD", - SHOW_STR - BGP_STR - "Address family\n" - "Display routes conforming to the filter-list\n" - "Regular expression access list name\n") - -/* old command */ -DEFUN (show_ipv6_bgp_filter_list, - show_ipv6_bgp_filter_list_cmd, - "show ipv6 bgp filter-list WORD", +DEFUN (show_bgp_prefix, + show_bgp_prefix_cmd, + "show bgp X:X::X:X/M", SHOW_STR - IPV6_STR BGP_STR - "Display routes conforming to the filter-list\n" - "Regular expression access list name\n") + "IPv6 prefix /\n") { - return bgp_show_filter_list (vty, argv[0], AFI_IP6, SAFI_UNICAST, - bgp_show_type_filter_list); + return bgp_show_route (vty, NULL, argv[0], AFI_IP6, SAFI_UNICAST, NULL, 1, BGP_PATH_ALL); } -/* old command */ -DEFUN (show_ipv6_mbgp_filter_list, - show_ipv6_mbgp_filter_list_cmd, - "show ipv6 mbgp filter-list WORD", + +/* new002 */ +DEFUN (show_bgp_ipv6_prefix, + show_bgp_ipv6_prefix_cmd, + "show bgp ipv6 X:X::X:X/M", SHOW_STR - IPV6_STR - MBGP_STR - "Display routes conforming to the filter-list\n" - "Regular expression access list name\n") + BGP_STR + "Address family\n" + "IPv6 prefix /, e.g., 3ffe::/16\n") { - return bgp_show_filter_list (vty, argv[0], AFI_IP6, SAFI_MULTICAST, - bgp_show_type_filter_list); + return bgp_show_route (vty, NULL, argv[0], AFI_IP6, SAFI_UNICAST, NULL, 1, BGP_PATH_ALL); } -#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) +DEFUN (show_bgp_ipv6_safi_prefix, + show_bgp_ipv6_safi_prefix_cmd, + "show bgp ipv6 (unicast|multicast) X:X::X:X/M", + SHOW_STR + BGP_STR + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + "IPv6 prefix /, e.g., 3ffe::/16\n") { - struct route_map *rmap; - - rmap = route_map_lookup_by_name (rmap_str); - if (! rmap) - { - vty_out (vty, "%% %s is not a valid route-map name%s", - rmap_str, VTY_NEWLINE); - return CMD_WARNING; - } + if (strncmp (argv[0], "m", 1) == 0) + return bgp_show_route (vty, NULL, argv[1], AFI_IP6, SAFI_MULTICAST, NULL, 1, BGP_PATH_ALL); - return bgp_show (vty, NULL, afi, safi, type, rmap); + return bgp_show_route (vty, NULL, argv[1], AFI_IP6, SAFI_UNICAST, NULL, 1, BGP_PATH_ALL); } -DEFUN (show_ip_bgp_route_map, - show_ip_bgp_route_map_cmd, - "show ip bgp route-map WORD", +/* old command */ +DEFUN (show_ipv6_bgp_prefix, + show_ipv6_bgp_prefix_cmd, + "show ipv6 bgp X:X::X:X/M", SHOW_STR IP_STR BGP_STR - "Display routes matching the route-map\n" - "A route-map to match on\n") + "IPv6 prefix /, e.g., 3ffe::/16\n") { - return bgp_show_route_map (vty, argv[0], AFI_IP, SAFI_UNICAST, - bgp_show_type_route_map); + return bgp_show_route (vty, NULL, argv[0], AFI_IP6, SAFI_UNICAST, NULL, 1, BGP_PATH_ALL); } -DEFUN (show_ip_bgp_flap_route_map, - show_ip_bgp_flap_route_map_cmd, - "show ip bgp flap-statistics route-map WORD", +DEFUN (show_bgp_view, + show_bgp_view_cmd, + "show bgp view WORD", SHOW_STR - IP_STR BGP_STR - "Display flap statistics of routes\n" - "Display routes matching the route-map\n" - "A route-map to match on\n") + "BGP view\n" + "View name\n") { - return bgp_show_route_map (vty, argv[0], AFI_IP, SAFI_UNICAST, - bgp_show_type_flap_route_map); + struct bgp *bgp; + + /* BGP structure lookup. */ + bgp = bgp_lookup_by_name (argv[0]); + if (bgp == NULL) + { + vty_out (vty, "Can't find BGP view %s%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + + return bgp_show (vty, bgp, AFI_IP6, SAFI_UNICAST, bgp_show_type_normal, NULL); } -DEFUN (show_ip_bgp_ipv4_route_map, - show_ip_bgp_ipv4_route_map_cmd, - "show ip bgp ipv4 (unicast|multicast) route-map WORD", +DEFUN (show_bgp_view_ipv6, + show_bgp_view_ipv6_cmd, + "show bgp view WORD ipv6", SHOW_STR - IP_STR - BGP_STR - "Address family\n" - "Address Family modifier\n" - "Address Family modifier\n" - "Display routes matching the route-map\n" - "A route-map to match on\n") + BGP_STR + "BGP view\n" + "View name\n" + "Address family\n") { - if (strncmp (argv[0], "m", 1) == 0) - return bgp_show_route_map (vty, argv[1], AFI_IP, SAFI_MULTICAST, - bgp_show_type_route_map); + struct bgp *bgp; - return bgp_show_route_map (vty, argv[1], AFI_IP, SAFI_UNICAST, - bgp_show_type_route_map); + /* BGP structure lookup. */ + bgp = bgp_lookup_by_name (argv[0]); + if (bgp == NULL) + { + vty_out (vty, "Can't find BGP view %s%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + + return bgp_show (vty, bgp, AFI_IP6, SAFI_UNICAST, bgp_show_type_normal, NULL); } - -DEFUN (show_bgp_route_map, - show_bgp_route_map_cmd, - "show bgp route-map WORD", + +DEFUN (show_bgp_view_route, + show_bgp_view_route_cmd, + "show bgp view WORD X:X::X:X", SHOW_STR BGP_STR - "Display routes matching the route-map\n" - "A route-map to match on\n") + "BGP view\n" + "View name\n" + "Network in the BGP routing table to display\n") { - return bgp_show_route_map (vty, argv[0], AFI_IP6, SAFI_UNICAST, - bgp_show_type_route_map); + return bgp_show_route (vty, argv[0], argv[1], AFI_IP6, SAFI_UNICAST, NULL, 0, BGP_PATH_ALL); } -ALIAS (show_bgp_route_map, - show_bgp_ipv6_route_map_cmd, - "show bgp ipv6 route-map WORD", +DEFUN (show_bgp_view_ipv6_route, + show_bgp_view_ipv6_route_cmd, + "show bgp view WORD ipv6 X:X::X:X", SHOW_STR BGP_STR + "BGP view\n" + "View name\n" "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", - SHOW_STR - IP_STR - BGP_STR - "Display only routes with non-natural netmasks\n") + "Network in the BGP routing table to display\n") { - return bgp_show (vty, NULL, AFI_IP, SAFI_UNICAST, - bgp_show_type_cidr_only, NULL); + return bgp_show_route (vty, argv[0], argv[1], AFI_IP6, SAFI_UNICAST, NULL, 0, BGP_PATH_ALL); } -DEFUN (show_ip_bgp_flap_cidr_only, - show_ip_bgp_flap_cidr_only_cmd, - "show ip bgp flap-statistics cidr-only", +/* old command */ +DEFUN (show_ipv6_mbgp, + show_ipv6_mbgp_cmd, + "show ipv6 mbgp", SHOW_STR IP_STR - BGP_STR - "Display flap statistics of routes\n" - "Display only routes with non-natural netmasks\n") + MBGP_STR) { - return bgp_show (vty, NULL, AFI_IP, SAFI_UNICAST, - bgp_show_type_flap_cidr_only, NULL); + return bgp_show (vty, NULL, AFI_IP6, SAFI_MULTICAST, bgp_show_type_normal, + NULL); } -DEFUN (show_ip_bgp_ipv4_cidr_only, - show_ip_bgp_ipv4_cidr_only_cmd, - "show ip bgp ipv4 (unicast|multicast) cidr-only", - SHOW_STR - IP_STR - BGP_STR - "Address family\n" - "Address Family modifier\n" - "Address Family modifier\n" - "Display only routes with non-natural netmasks\n") -{ - if (strncmp (argv[0], "m", 1) == 0) - return bgp_show (vty, NULL, AFI_IP, SAFI_MULTICAST, - bgp_show_type_cidr_only, NULL); - - 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", +/* old command */ +DEFUN (show_ipv6_mbgp_route, + show_ipv6_mbgp_route_cmd, + "show ipv6 mbgp X:X::X:X", SHOW_STR IP_STR - BGP_STR - "Display routes matching the communities\n") + MBGP_STR + "Network in the MBGP routing table to display\n") { - return bgp_show (vty, NULL, AFI_IP, SAFI_UNICAST, - bgp_show_type_community_all, NULL); + return bgp_show_route (vty, NULL, argv[0], AFI_IP6, SAFI_MULTICAST, NULL, 0, BGP_PATH_ALL); } -DEFUN (show_ip_bgp_ipv4_community_all, - show_ip_bgp_ipv4_community_all_cmd, - "show ip bgp ipv4 (unicast|multicast) community", +/* old command */ +DEFUN (show_ipv6_mbgp_prefix, + show_ipv6_mbgp_prefix_cmd, + "show ipv6 mbgp X:X::X:X/M", SHOW_STR IP_STR - BGP_STR - "Address family\n" - "Address Family modifier\n" - "Address Family modifier\n" - "Display routes matching the communities\n") + MBGP_STR + "IPv6 prefix /, e.g., 3ffe::/16\n") { - if (strncmp (argv[0], "m", 1) == 0) - return bgp_show (vty, NULL, AFI_IP, SAFI_MULTICAST, - bgp_show_type_community_all, NULL); - - return bgp_show (vty, NULL, AFI_IP, SAFI_UNICAST, - bgp_show_type_community_all, NULL); + return bgp_show_route (vty, NULL, argv[0], AFI_IP6, SAFI_MULTICAST, NULL, 1, BGP_PATH_ALL); } -#ifdef HAVE_IPV6 -DEFUN (show_bgp_community_all, - show_bgp_community_all_cmd, - "show bgp community", +DEFUN (show_bgp_view_prefix, + show_bgp_view_prefix_cmd, + "show bgp view WORD X:X::X:X/M", SHOW_STR BGP_STR - "Display routes matching the communities\n") + "BGP view\n" + "View name\n" + "IPv6 prefix /\n") { - return bgp_show (vty, NULL, AFI_IP6, SAFI_UNICAST, - bgp_show_type_community_all, NULL); + return bgp_show_route (vty, argv[0], argv[1], AFI_IP6, SAFI_UNICAST, NULL, 1, BGP_PATH_ALL); } -ALIAS (show_bgp_community_all, - show_bgp_ipv6_community_all_cmd, - "show bgp ipv6 community", +DEFUN (show_bgp_view_ipv6_prefix, + show_bgp_view_ipv6_prefix_cmd, + "show bgp view WORD ipv6 X:X::X:X/M", SHOW_STR BGP_STR + "BGP view\n" + "View name\n" "Address family\n" - "Display routes matching the communities\n") - -/* old command */ -DEFUN (show_ipv6_bgp_community_all, - show_ipv6_bgp_community_all_cmd, - "show ipv6 bgp community", - SHOW_STR - IPV6_STR - BGP_STR - "Display routes matching the communities\n") + "IPv6 prefix /\n") { - return bgp_show (vty, NULL, AFI_IP6, SAFI_UNICAST, - bgp_show_type_community_all, NULL); + return bgp_show_route (vty, argv[0], argv[1], AFI_IP6, SAFI_UNICAST, NULL, 1, BGP_PATH_ALL); } -/* old command */ -DEFUN (show_ipv6_mbgp_community_all, - show_ipv6_mbgp_community_all_cmd, - "show ipv6 mbgp community", - SHOW_STR - IPV6_STR - MBGP_STR - "Display routes matching the communities\n") -{ - return bgp_show (vty, NULL, AFI_IP6, SAFI_MULTICAST, - 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) +bgp_show_regexp (struct vty *vty, int argc, const char **argv, afi_t afi, + safi_t safi, enum bgp_show_type type) { - struct community *com; - struct buffer *b; - struct bgp *bgp; int i; - char *str; - int first = 0; - - /* BGP structure lookup */ - if (view_name) - { - bgp = bgp_lookup_by_name (view_name); - if (bgp == NULL) - { - vty_out (vty, "Can't find BGP view %s%s", view_name, VTY_NEWLINE); - return CMD_WARNING; - } - } - else - { - bgp = bgp_get_default (); - if (bgp == NULL) - { - vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE); - return CMD_WARNING; - } - } - + struct buffer *b; + char *regstr; + int first; + regex_t *regex; + int rc; + + first = 0; b = buffer_new (1024); for (i = 0; i < argc; i++) { if (first) - buffer_putc (b, ' '); + buffer_putc (b, ' '); else { if ((strcmp (argv[i], "unicast") == 0) || (strcmp (argv[i], "multicast") == 0)) continue; first = 1; } - + buffer_putstr (b, argv[i]); } buffer_putc (b, '\0'); - str = buffer_getstr (b); + regstr = buffer_getstr (b); buffer_free (b); - com = community_str2com (str); - XFREE (MTYPE_TMP, str); - if (! com) + regex = bgp_regcomp (regstr); + XFREE(MTYPE_TMP, regstr); + if (! regex) { - vty_out (vty, "%% Community malformed: %s", VTY_NEWLINE); + vty_out (vty, "Can't compile regexp %s%s", argv[0], + VTY_NEWLINE); return CMD_WARNING; } - return bgp_show (vty, bgp, afi, safi, - (exact ? bgp_show_type_community_exact : - bgp_show_type_community), com); + rc = bgp_show (vty, NULL, afi, safi, type, regex); + bgp_regex_free (regex); + return rc; } -DEFUN (show_ip_bgp_community, - show_ip_bgp_community_cmd, - "show ip bgp community (AA:NN|local-AS|no-advertise|no-export)", + +DEFUN (show_ip_bgp_regexp, + show_ip_bgp_regexp_cmd, + "show ip bgp regexp .LINE", SHOW_STR IP_STR BGP_STR - "Display routes matching the communities\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n") + "Display routes matching the AS path regular expression\n" + "A regular-expression to match the BGP AS paths\n") { - return bgp_show_community (vty, NULL, argc, argv, 0, AFI_IP, SAFI_UNICAST); + return bgp_show_regexp (vty, argc, argv, AFI_IP, SAFI_UNICAST, + bgp_show_type_regexp); } -ALIAS (show_ip_bgp_community, - show_ip_bgp_community2_cmd, - "show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)", +DEFUN (show_ip_bgp_flap_regexp, + show_ip_bgp_flap_regexp_cmd, + "show ip bgp flap-statistics regexp .LINE", SHOW_STR IP_STR BGP_STR - "Display routes matching the communities\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n") - -ALIAS (show_ip_bgp_community, - show_ip_bgp_community3_cmd, - "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)", + "Display flap statistics of routes\n" + "Display routes matching the AS path regular expression\n" + "A regular-expression to match the BGP AS paths\n") +{ + return bgp_show_regexp (vty, argc, argv, AFI_IP, SAFI_UNICAST, + bgp_show_type_flap_regexp); +} + +ALIAS (show_ip_bgp_flap_regexp, + show_ip_bgp_damp_flap_regexp_cmd, + "show ip bgp dampening flap-statistics regexp .LINE", SHOW_STR IP_STR BGP_STR - "Display routes matching the communities\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n") - -ALIAS (show_ip_bgp_community, - show_ip_bgp_community4_cmd, - "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)", - SHOW_STR - IP_STR - BGP_STR - "Display routes matching the communities\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n") - -DEFUN (show_ip_bgp_ipv4_community, - show_ip_bgp_ipv4_community_cmd, - "show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export)", + "Display detailed information about dampening\n" + "Display flap statistics of routes\n" + "Display routes matching the AS path regular expression\n" + "A regular-expression to match the BGP AS paths\n") + +DEFUN (show_ip_bgp_ipv4_regexp, + show_ip_bgp_ipv4_regexp_cmd, + "show ip bgp ipv4 (unicast|multicast) regexp .LINE", SHOW_STR IP_STR BGP_STR "Address family\n" "Address Family modifier\n" "Address Family modifier\n" - "Display routes matching the communities\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n") + "Display routes matching the AS path regular expression\n" + "A regular-expression to match the BGP AS paths\n") { if (strncmp (argv[0], "m", 1) == 0) - return bgp_show_community (vty, NULL, argc, argv, 0, AFI_IP, SAFI_MULTICAST); - - return bgp_show_community (vty, NULL, argc, argv, 0, AFI_IP, SAFI_UNICAST); + return bgp_show_regexp (vty, argc, argv, AFI_IP, SAFI_MULTICAST, + bgp_show_type_regexp); + + return bgp_show_regexp (vty, argc, argv, AFI_IP, SAFI_UNICAST, + bgp_show_type_regexp); } -ALIAS (show_ip_bgp_ipv4_community, - show_ip_bgp_ipv4_community2_cmd, - "show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)", +DEFUN (show_bgp_regexp, + show_bgp_regexp_cmd, + "show bgp regexp .LINE", + SHOW_STR + BGP_STR + "Display routes matching the AS path regular expression\n" + "A regular-expression to match the BGP AS paths\n") +{ + return bgp_show_regexp (vty, argc, argv, AFI_IP6, SAFI_UNICAST, + bgp_show_type_regexp); +} + +/* old command */ +DEFUN (show_ipv6_bgp_regexp, + show_ipv6_bgp_regexp_cmd, + "show ipv6 bgp regexp .LINE", SHOW_STR IP_STR BGP_STR - "Address family\n" - "Address Family modifier\n" - "Address Family modifier\n" - "Display routes matching the communities\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n") - -ALIAS (show_ip_bgp_ipv4_community, - show_ip_bgp_ipv4_community3_cmd, - "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)", + "Display routes matching the AS path regular expression\n" + "A regular-expression to match the BGP AS paths\n") +{ + return bgp_show_regexp (vty, argc, argv, AFI_IP6, SAFI_UNICAST, + bgp_show_type_regexp); +} + +/* old command */ +DEFUN (show_ipv6_mbgp_regexp, + show_ipv6_mbgp_regexp_cmd, + "show ipv6 mbgp regexp .LINE", SHOW_STR IP_STR BGP_STR - "Address family\n" - "Address Family modifier\n" - "Address Family modifier\n" - "Display routes matching the communities\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n") - -ALIAS (show_ip_bgp_ipv4_community, - show_ip_bgp_ipv4_community4_cmd, - "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)", + "Display routes matching the AS path regular expression\n" + "A regular-expression to match the MBGP AS paths\n") +{ + return bgp_show_regexp (vty, argc, argv, AFI_IP6, SAFI_MULTICAST, + bgp_show_type_regexp); +} + +DEFUN (show_bgp_ipv4_safi_flap_regexp, + show_bgp_ipv4_safi_flap_regexp_cmd, + "show bgp ipv4 (encap|multicast|unicast|vpn) flap-statistics regexp .LINE", SHOW_STR + BGP_STR IP_STR + "Address Family Modifier\n" + "Address Family Modifier\n" + "Address Family Modifier\n" + "Address Family Modifier\n" + "Display flap statistics of routes\n" + "Display routes matching the AS path regular expression\n" + "A regular-expression to match the BGP AS paths\n") +{ + safi_t safi; + + if (bgp_parse_safi(argv[0], &safi)) { + vty_out (vty, "Error: Bad SAFI: %s%s", argv[1], VTY_NEWLINE); + return CMD_WARNING; + } + return bgp_show_regexp (vty, argc-1, argv+1, AFI_IP, safi, + bgp_show_type_flap_regexp); +} + +ALIAS (show_bgp_ipv4_safi_flap_regexp, + show_bgp_ipv4_safi_damp_flap_regexp_cmd, + "show bgp ipv4 (encap|multicast|unicast|vpn) dampening flap-statistics regexp .LINE", + SHOW_STR BGP_STR - "Address family\n" - "Address Family modifier\n" - "Address Family modifier\n" - "Display routes matching the communities\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n") + IP_STR + "Address Family Modifier\n" + "Address Family Modifier\n" + "Address Family Modifier\n" + "Address Family Modifier\n" + "Display detailed information about dampening\n" + "Display flap statistics of routes\n" + "Display routes matching the AS path regular expression\n" + "A regular-expression to match the BGP AS paths\n") -DEFUN (show_bgp_view_afi_safi_community_all, - show_bgp_view_afi_safi_community_all_cmd, -#ifdef HAVE_IPV6 - "show bgp view WORD (ipv4|ipv6) (unicast|multicast) community", -#else - "show bgp view WORD ipv4 (unicast|multicast) community", -#endif +DEFUN (show_bgp_ipv6_safi_flap_regexp, + show_bgp_ipv6_safi_flap_regexp_cmd, + "show bgp ipv6 (encap|multicast|unicast|vpn) flap-statistics regexp .LINE", + SHOW_STR + BGP_STR + IPV6_STR + "Address Family Modifier\n" + "Address Family Modifier\n" + "Address Family Modifier\n" + "Address Family Modifier\n" + "Display flap statistics of routes\n" + "Display routes matching the AS path regular expression\n" + "A regular-expression to match the BGP AS paths\n") +{ + safi_t safi; + + if (bgp_parse_safi(argv[0], &safi)) { + vty_out (vty, "Error: Bad SAFI: %s%s", argv[1], VTY_NEWLINE); + return CMD_WARNING; + } + return bgp_show_regexp (vty, argc-1, argv+1, AFI_IP6, safi, + bgp_show_type_flap_regexp); +} + +ALIAS (show_bgp_ipv6_safi_flap_regexp, + show_bgp_ipv6_safi_damp_flap_regexp_cmd, + "show bgp ipv6 (encap|multicast|unicast|vpn) dampening flap-statistics regexp .LINE", + SHOW_STR + BGP_STR + IPV6_STR + "Address Family Modifier\n" + "Address Family Modifier\n" + "Address Family Modifier\n" + "Address Family Modifier\n" + "Display detailed information about dampening\n" + "Display flap statistics of routes\n" + "Display routes matching the AS path regular expression\n" + "A regular-expression to match the BGP AS paths\n") + +DEFUN (show_bgp_ipv4_safi_regexp, + show_bgp_ipv4_safi_regexp_cmd, + "show bgp ipv4 (encap|multicast|unicast|vpn) regexp .LINE", SHOW_STR BGP_STR - "BGP view\n" - "BGP 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") + "Address Family modifier\n" + "Address Family modifier\n" + "Display routes matching the AS path regular expression\n" + "A regular-expression to match the BGP AS paths\n") { - int afi; - int safi; - struct bgp *bgp; - - /* BGP structure lookup. */ - bgp = bgp_lookup_by_name (argv[0]); - if (bgp == NULL) - { - vty_out (vty, "Can't find BGP view %s%s", argv[0], VTY_NEWLINE); - return CMD_WARNING; - } + safi_t safi; + if (bgp_parse_safi(argv[0], &safi)) { + vty_out (vty, "Error: Bad SAFI: %s%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } -#ifdef HAVE_IPV6 - afi = (strncmp (argv[1], "ipv6", 4) == 0) ? AFI_IP6 : AFI_IP; - safi = (strncmp (argv[2], "m", 1) == 0) ? SAFI_MULTICAST : SAFI_UNICAST; -#else - afi = AFI_IP; - safi = (strncmp (argv[1], "m", 1) == 0) ? SAFI_MULTICAST : SAFI_UNICAST; -#endif - return bgp_show (vty, bgp, afi, safi, bgp_show_type_community_all, NULL); + return bgp_show_regexp (vty, argc-1, argv+1, AFI_IP, safi, + bgp_show_type_regexp); } -DEFUN (show_bgp_view_afi_safi_community, - show_bgp_view_afi_safi_community_cmd, -#ifdef HAVE_IPV6 - "show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export)", -#else - "show bgp view WORD ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export)", -#endif +DEFUN (show_bgp_ipv6_safi_regexp, + show_bgp_ipv6_safi_regexp_cmd, + "show bgp ipv6 (encap|multicast|unicast|vpn) regexp .LINE", SHOW_STR BGP_STR - "BGP view\n" - "BGP view name\n" - "Address family\n" -#ifdef HAVE_IPV6 "Address family\n" -#endif - "Address family modifier\n" - "Address family modifier\n" - "Display routes matching the communities\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n") + "Address Family modifier\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Display routes matching the AS path regular expression\n" + "A regular-expression to match the BGP AS paths\n") { - int afi; - int safi; + safi_t safi; + if (bgp_parse_safi(argv[0], &safi)) { + vty_out (vty, "Error: Bad SAFI: %s%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } -#ifdef HAVE_IPV6 - afi = (strncmp (argv[1], "ipv6", 4) == 0) ? AFI_IP6 : AFI_IP; - safi = (strncmp (argv[2], "m", 1) == 0) ? SAFI_MULTICAST : SAFI_UNICAST; - return bgp_show_community (vty, argv[0], argc-3, &argv[3], 0, afi, safi); -#else - afi = AFI_IP; - safi = (strncmp (argv[1], "m", 1) == 0) ? SAFI_MULTICAST : SAFI_UNICAST; - return bgp_show_community (vty, argv[0], argc-2, &argv[2], 0, afi, safi); -#endif + return bgp_show_regexp (vty, argc-1, argv+1, AFI_IP6, safi, + bgp_show_type_regexp); } -ALIAS (show_bgp_view_afi_safi_community, - show_bgp_view_afi_safi_community2_cmd, -#ifdef HAVE_IPV6 - "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)", -#else - "show bgp view WORD ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)", -#endif +DEFUN (show_bgp_ipv6_regexp, + show_bgp_ipv6_regexp_cmd, + "show bgp ipv6 regexp .LINE", SHOW_STR BGP_STR - "BGP view\n" - "BGP view name\n" - "Address family\n" -#ifdef HAVE_IPV6 "Address family\n" -#endif - "Address family modifier\n" - "Address family modifier\n" - "Display routes matching the communities\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n") - -ALIAS (show_bgp_view_afi_safi_community, - show_bgp_view_afi_safi_community3_cmd, -#ifdef HAVE_IPV6 - "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)", -#else - "show bgp view WORD 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)", -#endif - SHOW_STR - BGP_STR - "BGP view\n" - "BGP view name\n" - "Address family\n" -#ifdef HAVE_IPV6 - "Address family\n" -#endif - "Address family modifier\n" - "Address family modifier\n" - "Display routes matching the communities\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n") + "Display routes matching the AS path regular expression\n" + "A regular-expression to match the BGP AS paths\n") +{ + return bgp_show_regexp (vty, argc, argv, AFI_IP6, SAFI_UNICAST, + bgp_show_type_regexp); +} -ALIAS (show_bgp_view_afi_safi_community, - show_bgp_view_afi_safi_community4_cmd, -#ifdef HAVE_IPV6 - "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)", -#else - "show bgp view WORD 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)", -#endif - SHOW_STR - BGP_STR - "BGP view\n" - "BGP view name\n" - "Address family\n" -#ifdef HAVE_IPV6 - "Address family\n" -#endif - "Address family modifier\n" - "Address family modifier\n" - "Display routes matching the communities\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n") +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) +{ + struct prefix_list *plist; -DEFUN (show_ip_bgp_community_exact, - show_ip_bgp_community_exact_cmd, - "show ip bgp community (AA:NN|local-AS|no-advertise|no-export) exact-match", + plist = prefix_list_lookup (afi, prefix_list_str); + if (plist == NULL) + { + vty_out (vty, "%% %s is not a valid prefix-list name%s", + prefix_list_str, VTY_NEWLINE); + return CMD_WARNING; + } + + return bgp_show (vty, NULL, afi, safi, type, plist); +} +DEFUN (show_ip_bgp_prefix_list, + show_ip_bgp_prefix_list_cmd, + "show ip bgp prefix-list WORD", SHOW_STR IP_STR BGP_STR - "Display routes matching the communities\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "Exact match of the communities") + "Display routes conforming to the prefix-list\n" + "IP prefix-list name\n") { - return bgp_show_community (vty, NULL, argc, argv, 1, AFI_IP, SAFI_UNICAST); + return bgp_show_prefix_list (vty, argv[0], AFI_IP, SAFI_UNICAST, + bgp_show_type_prefix_list); } -ALIAS (show_ip_bgp_community_exact, - show_ip_bgp_community2_exact_cmd, - "show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match", - SHOW_STR - IP_STR - BGP_STR - "Display routes matching the communities\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "Exact match of the communities") - -ALIAS (show_ip_bgp_community_exact, - show_ip_bgp_community3_exact_cmd, - "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) exact-match", +DEFUN (show_ip_bgp_flap_prefix_list, + show_ip_bgp_flap_prefix_list_cmd, + "show ip bgp flap-statistics prefix-list WORD", SHOW_STR IP_STR BGP_STR - "Display routes matching the communities\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "Exact match of the communities") + "Display flap statistics of routes\n" + "Display routes conforming to the prefix-list\n" + "IP prefix-list name\n") +{ + return bgp_show_prefix_list (vty, argv[0], AFI_IP, SAFI_UNICAST, + bgp_show_type_flap_prefix_list); +} -ALIAS (show_ip_bgp_community_exact, - show_ip_bgp_community4_exact_cmd, - "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) exact-match", +ALIAS (show_ip_bgp_flap_prefix_list, + show_ip_bgp_damp_flap_prefix_list_cmd, + "show ip bgp dampening flap-statistics prefix-list WORD", SHOW_STR IP_STR BGP_STR - "Display routes matching the communities\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "Exact match of the communities") + "Display detailed information about dampening\n" + "Display flap statistics of routes\n" + "Display routes conforming to the prefix-list\n" + "IP prefix-list name\n") -DEFUN (show_ip_bgp_ipv4_community_exact, - show_ip_bgp_ipv4_community_exact_cmd, - "show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) exact-match", +DEFUN (show_ip_bgp_ipv4_prefix_list, + show_ip_bgp_ipv4_prefix_list_cmd, + "show ip bgp ipv4 (unicast|multicast) prefix-list WORD", SHOW_STR IP_STR BGP_STR "Address family\n" "Address Family modifier\n" "Address Family modifier\n" - "Display routes matching the communities\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "Exact match of the communities") + "Display routes conforming to the prefix-list\n" + "IP prefix-list name\n") { if (strncmp (argv[0], "m", 1) == 0) - return bgp_show_community (vty, NULL, argc, argv, 1, AFI_IP, SAFI_MULTICAST); - - return bgp_show_community (vty, NULL, argc, argv, 1, AFI_IP, SAFI_UNICAST); + return bgp_show_prefix_list (vty, argv[1], AFI_IP, SAFI_MULTICAST, + bgp_show_type_prefix_list); + + return bgp_show_prefix_list (vty, argv[1], AFI_IP, SAFI_UNICAST, + bgp_show_type_prefix_list); } -ALIAS (show_ip_bgp_ipv4_community_exact, - show_ip_bgp_ipv4_community2_exact_cmd, - "show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match", +DEFUN (show_bgp_prefix_list, + show_bgp_prefix_list_cmd, + "show bgp prefix-list WORD", SHOW_STR - IP_STR BGP_STR - "Address family\n" - "Address Family modifier\n" - "Address Family modifier\n" - "Display routes matching the communities\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "Exact match of the communities") + "Display routes conforming to the prefix-list\n" + "IPv6 prefix-list name\n") +{ + return bgp_show_prefix_list (vty, argv[0], AFI_IP6, SAFI_UNICAST, + bgp_show_type_prefix_list); +} -ALIAS (show_ip_bgp_ipv4_community_exact, - show_ip_bgp_ipv4_community3_exact_cmd, - "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) exact-match", +ALIAS (show_bgp_prefix_list, + show_bgp_ipv6_prefix_list_cmd, + "show bgp ipv6 prefix-list WORD", SHOW_STR - IP_STR BGP_STR "Address family\n" - "Address Family modifier\n" - "Address Family modifier\n" - "Display routes matching the communities\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "Exact match of the communities") - -ALIAS (show_ip_bgp_ipv4_community_exact, - show_ip_bgp_ipv4_community4_exact_cmd, - "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", + "Display routes conforming to the prefix-list\n" + "IPv6 prefix-list name\n") + +/* old command */ +DEFUN (show_ipv6_bgp_prefix_list, + show_ipv6_bgp_prefix_list_cmd, + "show ipv6 bgp prefix-list WORD", SHOW_STR - IP_STR + IPV6_STR BGP_STR - "Address family\n" - "Address Family modifier\n" - "Address Family modifier\n" - "Display routes matching the communities\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "Exact match of the communities") + "Display routes matching the prefix-list\n" + "IPv6 prefix-list name\n") +{ + return bgp_show_prefix_list (vty, argv[0], AFI_IP6, SAFI_UNICAST, + bgp_show_type_prefix_list); +} -#ifdef HAVE_IPV6 -DEFUN (show_bgp_community, - show_bgp_community_cmd, - "show bgp community (AA:NN|local-AS|no-advertise|no-export)", +/* old command */ +DEFUN (show_ipv6_mbgp_prefix_list, + show_ipv6_mbgp_prefix_list_cmd, + "show ipv6 mbgp prefix-list WORD", SHOW_STR - BGP_STR - "Display routes matching the communities\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n") + IPV6_STR + MBGP_STR + "Display routes matching the prefix-list\n" + "IPv6 prefix-list name\n") { - return bgp_show_community (vty, NULL, argc, argv, 0, AFI_IP6, SAFI_UNICAST); + return bgp_show_prefix_list (vty, argv[0], AFI_IP6, SAFI_MULTICAST, + bgp_show_type_prefix_list); } -ALIAS (show_bgp_community, - show_bgp_ipv6_community_cmd, - "show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export)", +DEFUN (show_bgp_ipv4_prefix_list, + show_bgp_ipv4_prefix_list_cmd, + "show bgp ipv4 prefix-list WORD", SHOW_STR BGP_STR - "Address family\n" - "Display routes matching the communities\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n") + IP_STR + "Display routes conforming to the prefix-list\n" + "IP prefix-list name\n") +{ + return bgp_show_prefix_list (vty, argv[0], AFI_IP, SAFI_UNICAST, + bgp_show_type_prefix_list); +} -ALIAS (show_bgp_community, - show_bgp_community2_cmd, - "show bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)", +DEFUN (show_bgp_ipv4_safi_flap_prefix_list, + show_bgp_ipv4_safi_flap_prefix_list_cmd, + "show bgp ipv4 (encap|multicast|unicast|vpn) flap-statistics prefix-list WORD", SHOW_STR BGP_STR - "Display routes matching the communities\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n") + IP_STR + "Address Family Modifier\n" + "Address Family Modifier\n" + "Address Family Modifier\n" + "Address Family Modifier\n" + "Display flap statistics of routes\n" + "Display routes conforming to the prefix-list\n" + "IP prefix-list name\n") +{ + safi_t safi; + if (bgp_parse_safi(argv[0], &safi)) { + vty_out (vty, "Error: Bad SAFI: %s%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + return bgp_show_prefix_list (vty, argv[1], AFI_IP, safi, + bgp_show_type_flap_prefix_list); +} -ALIAS (show_bgp_community, - show_bgp_ipv6_community2_cmd, - "show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)", +ALIAS (show_bgp_ipv4_safi_flap_prefix_list, + show_bgp_ipv4_safi_damp_flap_prefix_list_cmd, + "show bgp ipv4 (encap|multicast|unicast|vpn) dampening flap-statistics prefix-list WORD", SHOW_STR BGP_STR - "Address family\n" - "Display routes matching the communities\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n") - -ALIAS (show_bgp_community, - show_bgp_community3_cmd, - "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)", + IP_STR + "Address Family Modifier\n" + "Address Family Modifier\n" + "Address Family Modifier\n" + "Address Family Modifier\n" + "Display detailed information about dampening\n" + "Display flap statistics of routes\n" + "Display routes conforming to the prefix-list\n" + "IP prefix-list name\n") + +DEFUN (show_bgp_ipv6_safi_flap_prefix_list, + show_bgp_ipv6_safi_flap_prefix_list_cmd, + "show bgp ipv6 (encap|multicast|unicast|vpn) flap-statistics prefix-list WORD", SHOW_STR BGP_STR - "Display routes matching the communities\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n") - -ALIAS (show_bgp_community, - show_bgp_ipv6_community3_cmd, - "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)", + IPV6_STR + "Address Family Modifier\n" + "Address Family Modifier\n" + "Address Family Modifier\n" + "Address Family Modifier\n" + "Display flap statistics of routes\n" + "Display routes conforming to the prefix-list\n" + "IP prefix-list name\n") +{ + safi_t safi; + if (bgp_parse_safi(argv[0], &safi)) { + vty_out (vty, "Error: Bad SAFI: %s%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + return bgp_show_prefix_list (vty, argv[1], AFI_IP6, safi, + bgp_show_type_flap_prefix_list); +} +ALIAS (show_bgp_ipv6_safi_flap_prefix_list, + show_bgp_ipv6_safi_damp_flap_prefix_list_cmd, + "show bgp ipv6 (encap|multicast|unicast|vpn) dampening flap-statistics prefix-list WORD", SHOW_STR BGP_STR - "Address family\n" - "Display routes matching the communities\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n") + IPV6_STR + "Address Family Modifier\n" + "Address Family Modifier\n" + "Address Family Modifier\n" + "Address Family Modifier\n" + "Display detailed information about dampening\n" + "Display flap statistics of routes\n" + "Display routes conforming to the prefix-list\n" + "IP prefix-list name\n") -ALIAS (show_bgp_community, - show_bgp_community4_cmd, - "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)", +DEFUN (show_bgp_ipv4_safi_prefix_list, + show_bgp_ipv4_safi_prefix_list_cmd, + "show bgp ipv4 (encap|multicast|unicast|vpn) prefix-list WORD", SHOW_STR BGP_STR - "Display routes matching the communities\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n") + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Display routes conforming to the prefix-list\n" + "IP prefix-list name\n") +{ + safi_t safi; + if (bgp_parse_safi(argv[0], &safi)) { + vty_out (vty, "Error: Bad SAFI: %s%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + return bgp_show_prefix_list (vty, argv[1], AFI_IP, safi, + bgp_show_type_prefix_list); +} -ALIAS (show_bgp_community, - show_bgp_ipv6_community4_cmd, - "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)", +DEFUN (show_bgp_ipv6_safi_prefix_list, + show_bgp_ipv6_safi_prefix_list_cmd, + "show bgp ipv6 (encap|multicast|unicast|vpn) prefix-list WORD", SHOW_STR BGP_STR "Address family\n" - "Display routes matching the communities\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n") + "Address Family modifier\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Display routes conforming to the prefix-list\n" + "IP prefix-list name\n") +{ + safi_t safi; + if (bgp_parse_safi(argv[0], &safi)) { + vty_out (vty, "Error: Bad SAFI: %s%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + return bgp_show_prefix_list (vty, argv[1], AFI_IP6, safi, + bgp_show_type_prefix_list); +} -/* old command */ -DEFUN (show_ipv6_bgp_community, - show_ipv6_bgp_community_cmd, - "show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export)", +static int +bgp_show_filter_list (struct vty *vty, const char *filter, afi_t afi, + safi_t safi, enum bgp_show_type type) +{ + struct as_list *as_list; + + as_list = as_list_lookup (filter); + if (as_list == NULL) + { + vty_out (vty, "%% %s is not a valid AS-path access-list name%s", filter, VTY_NEWLINE); + return CMD_WARNING; + } + + return bgp_show (vty, NULL, afi, safi, type, as_list); +} + +DEFUN (show_ip_bgp_filter_list, + show_ip_bgp_filter_list_cmd, + "show ip bgp filter-list WORD", SHOW_STR - IPV6_STR + IP_STR BGP_STR - "Display routes matching the communities\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n") + "Display routes conforming to the filter-list\n" + "Regular expression access list name\n") { - return bgp_show_community (vty, NULL, argc, argv, 0, AFI_IP6, SAFI_UNICAST); + return bgp_show_filter_list (vty, argv[0], AFI_IP, SAFI_UNICAST, + bgp_show_type_filter_list); } -/* old command */ -ALIAS (show_ipv6_bgp_community, - show_ipv6_bgp_community2_cmd, - "show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)", +DEFUN (show_ip_bgp_flap_filter_list, + show_ip_bgp_flap_filter_list_cmd, + "show ip bgp flap-statistics filter-list WORD", SHOW_STR - IPV6_STR + IP_STR BGP_STR - "Display routes matching the communities\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n") + "Display flap statistics of routes\n" + "Display routes conforming to the filter-list\n" + "Regular expression access list name\n") +{ + return bgp_show_filter_list (vty, argv[0], AFI_IP, SAFI_UNICAST, + bgp_show_type_flap_filter_list); +} + +ALIAS (show_ip_bgp_flap_filter_list, + show_ip_bgp_damp_flap_filter_list_cmd, + "show ip bgp dampening flap-statistics filter-list WORD", + SHOW_STR + IP_STR + BGP_STR + "Display detailed information about dampening\n" + "Display flap statistics of routes\n" + "Display routes conforming to the filter-list\n" + "Regular expression access list name\n") + +DEFUN (show_ip_bgp_ipv4_filter_list, + show_ip_bgp_ipv4_filter_list_cmd, + "show ip bgp ipv4 (unicast|multicast) filter-list WORD", + SHOW_STR + IP_STR + BGP_STR + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Display routes conforming to the filter-list\n" + "Regular expression access list name\n") +{ + if (strncmp (argv[0], "m", 1) == 0) + return bgp_show_filter_list (vty, argv[1], AFI_IP, SAFI_MULTICAST, + bgp_show_type_filter_list); + + return bgp_show_filter_list (vty, argv[1], AFI_IP, SAFI_UNICAST, + bgp_show_type_filter_list); +} + +DEFUN (show_bgp_filter_list, + show_bgp_filter_list_cmd, + "show bgp filter-list WORD", + SHOW_STR + BGP_STR + "Display routes conforming to the filter-list\n" + "Regular expression access list name\n") +{ + return bgp_show_filter_list (vty, argv[0], AFI_IP6, SAFI_UNICAST, + bgp_show_type_filter_list); +} /* old command */ -ALIAS (show_ipv6_bgp_community, - show_ipv6_bgp_community3_cmd, - "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)", +DEFUN (show_ipv6_bgp_filter_list, + show_ipv6_bgp_filter_list_cmd, + "show ipv6 bgp filter-list WORD", SHOW_STR IPV6_STR BGP_STR - "Display routes matching the communities\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n") + "Display routes conforming to the filter-list\n" + "Regular expression access list name\n") +{ + return bgp_show_filter_list (vty, argv[0], AFI_IP6, SAFI_UNICAST, + bgp_show_type_filter_list); +} /* old command */ -ALIAS (show_ipv6_bgp_community, - show_ipv6_bgp_community4_cmd, - "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)", +DEFUN (show_ipv6_mbgp_filter_list, + show_ipv6_mbgp_filter_list_cmd, + "show ipv6 mbgp filter-list WORD", SHOW_STR IPV6_STR + MBGP_STR + "Display routes conforming to the filter-list\n" + "Regular expression access list name\n") +{ + return bgp_show_filter_list (vty, argv[0], AFI_IP6, SAFI_MULTICAST, + bgp_show_type_filter_list); +} + +DEFUN (show_ip_bgp_dampening_info, + show_ip_bgp_dampening_params_cmd, + "show ip bgp dampening parameters", + SHOW_STR + IP_STR BGP_STR - "Display routes matching the communities\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n") + "Display detailed information about dampening\n" + "Display detail of configured dampening parameters\n") +{ + return bgp_show_dampening_parameters (vty, AFI_IP, SAFI_UNICAST); +} -DEFUN (show_bgp_community_exact, - show_bgp_community_exact_cmd, - "show bgp community (AA:NN|local-AS|no-advertise|no-export) exact-match", +DEFUN (show_bgp_ipv4_filter_list, + show_bgp_ipv4_filter_list_cmd, + "show bgp ipv4 filter-list WORD", SHOW_STR BGP_STR - "Display routes matching the communities\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "Exact match of the communities") + IP_STR + "Display routes conforming to the filter-list\n" + "Regular expression access list name\n") { - return bgp_show_community (vty, NULL, argc, argv, 1, AFI_IP6, SAFI_UNICAST); + return bgp_show_filter_list (vty, argv[0], AFI_IP, SAFI_UNICAST, + bgp_show_type_filter_list); } -ALIAS (show_bgp_community_exact, - show_bgp_ipv6_community_exact_cmd, - "show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) exact-match", +DEFUN (show_bgp_ipv4_safi_flap_filter_list, + show_bgp_ipv4_safi_flap_filter_list_cmd, + "show bgp ipv4 (encap|multicast|unicast|vpn) flap-statistics filter-list WORD", SHOW_STR BGP_STR - "Address family\n" - "Display routes matching the communities\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "Exact match of the communities") + IP_STR + "Address Family modifier\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Display flap statistics of routes\n" + "Display routes conforming to the filter-list\n" + "Regular expression access list name\n") +{ + safi_t safi; -ALIAS (show_bgp_community_exact, - show_bgp_community2_exact_cmd, - "show bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match", + if (bgp_parse_safi(argv[0], &safi)) { + vty_out (vty, "Error: Bad SAFI: %s%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + return bgp_show_filter_list (vty, argv[1], AFI_IP, safi, + bgp_show_type_flap_filter_list); +} + +ALIAS (show_bgp_ipv4_safi_flap_filter_list, + show_bgp_ipv4_safi_damp_flap_filter_list_cmd, + "show bgp ipv4 (encap|multicast|unicast|vpn) dampening flap-statistics filter-list WORD", SHOW_STR BGP_STR - "Display routes matching the communities\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "Exact match of the communities") + IP_STR + "Address Family modifier\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Display detailed information about dampening\n" + "Display flap statistics of routes\n" + "Display routes conforming to the filter-list\n" + "Regular expression access list name\n") -ALIAS (show_bgp_community_exact, - show_bgp_ipv6_community2_exact_cmd, - "show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match", +DEFUN (show_bgp_ipv6_safi_flap_filter_list, + show_bgp_ipv6_safi_flap_filter_list_cmd, + "show bgp ipv6 (encap|multicast|unicast|vpn) flap-statistics filter-list WORD", SHOW_STR BGP_STR - "Address family\n" - "Display routes matching the communities\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "Exact match of the communities") + IPV6_STR + "Address Family modifier\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Display flap statistics of routes\n" + "Display routes conforming to the filter-list\n" + "Regular expression access list name\n") +{ + safi_t safi; -ALIAS (show_bgp_community_exact, - show_bgp_community3_exact_cmd, - "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) exact-match", + if (bgp_parse_safi(argv[0], &safi)) { + vty_out (vty, "Error: Bad SAFI: %s%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + return bgp_show_filter_list (vty, argv[1], AFI_IP6, safi, + bgp_show_type_flap_filter_list); +} +ALIAS (show_bgp_ipv6_safi_flap_filter_list, + show_bgp_ipv6_safi_damp_flap_filter_list_cmd, + "show bgp ipv6 (encap|multicast|unicast|vpn) dampening flap-statistics filter-list WORD", SHOW_STR BGP_STR - "Display routes matching the communities\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "Exact match of the communities") + IPV6_STR + "Address Family modifier\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Display detailed information about dampening\n" + "Display flap statistics of routes\n" + "Display routes conforming to the filter-list\n" + "Regular expression access list name\n") -ALIAS (show_bgp_community_exact, - show_bgp_ipv6_community3_exact_cmd, - "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) exact-match", +DEFUN (show_bgp_ipv4_safi_filter_list, + show_bgp_ipv4_safi_filter_list_cmd, + "show bgp ipv4 (encap|multicast|unicast|vpn) filter-list WORD", SHOW_STR BGP_STR - "Address family\n" - "Display routes matching the communities\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "Exact match of the communities") + "Address Family modifier\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Display routes conforming to the filter-list\n" + "Regular expression access list name\n") +{ + safi_t safi; -ALIAS (show_bgp_community_exact, - show_bgp_community4_exact_cmd, - "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", + if (bgp_parse_safi(argv[0], &safi)) { + vty_out (vty, "Error: Bad SAFI: %s%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + return bgp_show_filter_list (vty, argv[1], AFI_IP, safi, + bgp_show_type_filter_list); +} + +DEFUN (show_bgp_ipv6_safi_filter_list, + show_bgp_ipv6_safi_filter_list_cmd, + "show bgp ipv6 (encap|multicast|unicast|vpn) filter-list WORD", SHOW_STR BGP_STR - "Display routes matching the communities\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "Exact match of the communities") - -ALIAS (show_bgp_community_exact, - show_bgp_ipv6_community4_exact_cmd, - "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", + "Address Family modifier\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Display routes conforming to the filter-list\n" + "Regular expression access list name\n") +{ + safi_t safi; + + if (bgp_parse_safi(argv[0], &safi)) { + vty_out (vty, "Error: Bad SAFI: %s%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + return bgp_show_filter_list (vty, argv[1], AFI_IP6, safi, + bgp_show_type_filter_list); +} + +DEFUN (show_bgp_ipv6_filter_list, + show_bgp_ipv6_filter_list_cmd, + "show bgp ipv6 filter-list WORD", SHOW_STR BGP_STR "Address family\n" - "Display routes matching the communities\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "Exact match of the communities") + "Display routes conforming to the filter-list\n" + "Regular expression access list name\n") +{ + return bgp_show_filter_list (vty, argv[0], AFI_IP6, SAFI_UNICAST, + bgp_show_type_filter_list); +} -/* old command */ -DEFUN (show_ipv6_bgp_community_exact, - show_ipv6_bgp_community_exact_cmd, - "show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) exact-match", + +DEFUN (show_ip_bgp_ipv4_dampening_parameters, + show_ip_bgp_ipv4_dampening_parameters_cmd, + "show ip bgp ipv4 (unicast|multicast) dampening parameters", SHOW_STR - IPV6_STR + IP_STR BGP_STR - "Display routes matching the communities\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "Exact match of the communities") + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Display detailed information about dampening\n" + "Display detail of configured dampening parameters\n") { - return bgp_show_community (vty, NULL, argc, argv, 1, AFI_IP6, SAFI_UNICAST); + if (strncmp(argv[0], "m", 1) == 0) + return bgp_show_dampening_parameters (vty, AFI_IP, SAFI_MULTICAST); + + return bgp_show_dampening_parameters (vty, AFI_IP, SAFI_UNICAST); } -/* old command */ -ALIAS (show_ipv6_bgp_community_exact, - show_ipv6_bgp_community2_exact_cmd, - "show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match", + +DEFUN (show_ip_bgp_ipv4_dampening_flap_stats, + show_ip_bgp_ipv4_dampening_flap_stats_cmd, + "show ip bgp ipv4 (unicast|multicast) dampening flap-statistics", SHOW_STR - IPV6_STR + IP_STR BGP_STR - "Display routes matching the communities\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "Exact match of the communities") + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Display detailed information about dampening\n" + "Display flap statistics of routes\n") +{ + if (strncmp(argv[0], "m", 1) == 0) + return bgp_show (vty, NULL, AFI_IP, SAFI_MULTICAST, + bgp_show_type_flap_statistics, NULL); -/* old command */ -ALIAS (show_ipv6_bgp_community_exact, - show_ipv6_bgp_community3_exact_cmd, - "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) exact-match", + return bgp_show (vty, NULL, AFI_IP, SAFI_MULTICAST, + bgp_show_type_flap_statistics, NULL); +} + +DEFUN (show_ip_bgp_ipv4_dampening_dampd_paths, + show_ip_bgp_ipv4_dampening_dampd_paths_cmd, + "show ip bgp ipv4 (unicast|multicast) dampening dampened-paths", SHOW_STR - IPV6_STR + IP_STR BGP_STR - "Display routes matching the communities\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "Exact match of the communities") + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Display detailed information about dampening\n" + "Display paths suppressed due to dampening\n") +{ + if (strncmp(argv[0], "m", 1) == 0) + return bgp_show (vty, NULL, AFI_IP, SAFI_MULTICAST, + bgp_show_type_dampend_paths, NULL); -/* old command */ -ALIAS (show_ipv6_bgp_community_exact, - show_ipv6_bgp_community4_exact_cmd, - "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", + return bgp_show (vty, NULL, AFI_IP, SAFI_MULTICAST, + bgp_show_type_dampend_paths, NULL); +} + +static int +bgp_show_route_map (struct vty *vty, const char *rmap_str, afi_t afi, + safi_t safi, enum bgp_show_type type) +{ + struct route_map *rmap; + + rmap = route_map_lookup_by_name (rmap_str); + if (! rmap) + { + vty_out (vty, "%% %s is not a valid route-map name%s", + rmap_str, VTY_NEWLINE); + return CMD_WARNING; + } + + return bgp_show (vty, NULL, afi, safi, type, rmap); +} + +DEFUN (show_ip_bgp_route_map, + show_ip_bgp_route_map_cmd, + "show ip bgp route-map WORD", SHOW_STR - IPV6_STR + IP_STR BGP_STR - "Display routes matching the communities\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "Exact match of the communities") - -/* old command */ -DEFUN (show_ipv6_mbgp_community, - show_ipv6_mbgp_community_cmd, - "show ipv6 mbgp community (AA:NN|local-AS|no-advertise|no-export)", - SHOW_STR - IPV6_STR - MBGP_STR - "Display routes matching the communities\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n") + "Display routes matching the route-map\n" + "A route-map to match on\n") { - return bgp_show_community (vty, NULL, argc, argv, 0, AFI_IP6, SAFI_MULTICAST); + return bgp_show_route_map (vty, argv[0], AFI_IP, SAFI_UNICAST, + bgp_show_type_route_map); } -/* old command */ -ALIAS (show_ipv6_mbgp_community, - show_ipv6_mbgp_community2_cmd, - "show ipv6 mbgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)", +DEFUN (show_ip_bgp_flap_route_map, + show_ip_bgp_flap_route_map_cmd, + "show ip bgp flap-statistics route-map WORD", SHOW_STR - IPV6_STR - MBGP_STR - "Display routes matching the communities\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n") + IP_STR + BGP_STR + "Display flap statistics of routes\n" + "Display routes matching the route-map\n" + "A route-map to match on\n") +{ + return bgp_show_route_map (vty, argv[0], AFI_IP, SAFI_UNICAST, + bgp_show_type_flap_route_map); +} -/* old command */ -ALIAS (show_ipv6_mbgp_community, - show_ipv6_mbgp_community3_cmd, - "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)", +ALIAS (show_ip_bgp_flap_route_map, + show_ip_bgp_damp_flap_route_map_cmd, + "show ip bgp dampening flap-statistics route-map WORD", SHOW_STR - IPV6_STR - MBGP_STR - "Display routes matching the communities\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n") + IP_STR + BGP_STR + "Display detailed information about dampening\n" + "Display flap statistics of routes\n" + "Display routes matching the route-map\n" + "A route-map to match on\n") -/* old command */ -ALIAS (show_ipv6_mbgp_community, - show_ipv6_mbgp_community4_cmd, - "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)", +DEFUN (show_ip_bgp_ipv4_route_map, + show_ip_bgp_ipv4_route_map_cmd, + "show ip bgp ipv4 (unicast|multicast) route-map WORD", SHOW_STR - IPV6_STR - MBGP_STR - "Display routes matching the communities\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n") - -/* old command */ -DEFUN (show_ipv6_mbgp_community_exact, - show_ipv6_mbgp_community_exact_cmd, - "show ipv6 mbgp community (AA:NN|local-AS|no-advertise|no-export) exact-match", - SHOW_STR - IPV6_STR - MBGP_STR - "Display routes matching the communities\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "Exact match of the communities") + IP_STR + BGP_STR + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Display routes matching the route-map\n" + "A route-map to match on\n") { - return bgp_show_community (vty, NULL, argc, argv, 1, AFI_IP6, SAFI_MULTICAST); -} + if (strncmp (argv[0], "m", 1) == 0) + return bgp_show_route_map (vty, argv[1], AFI_IP, SAFI_MULTICAST, + bgp_show_type_route_map); -/* old command */ -ALIAS (show_ipv6_mbgp_community_exact, - show_ipv6_mbgp_community2_exact_cmd, - "show ipv6 mbgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match", - SHOW_STR - IPV6_STR - MBGP_STR - "Display routes matching the communities\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "Exact match of the communities") + return bgp_show_route_map (vty, argv[1], AFI_IP, SAFI_UNICAST, + bgp_show_type_route_map); +} -/* old command */ -ALIAS (show_ipv6_mbgp_community_exact, - show_ipv6_mbgp_community3_exact_cmd, - "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) exact-match", +DEFUN (show_bgp_route_map, + show_bgp_route_map_cmd, + "show bgp route-map WORD", SHOW_STR - IPV6_STR - MBGP_STR - "Display routes matching the communities\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "Exact match of the communities") + BGP_STR + "Display routes matching the route-map\n" + "A route-map to match on\n") +{ + return bgp_show_route_map (vty, argv[0], AFI_IP6, SAFI_UNICAST, + bgp_show_type_route_map); +} -/* old command */ -ALIAS (show_ipv6_mbgp_community_exact, - show_ipv6_mbgp_community4_exact_cmd, - "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", +DEFUN (show_ip_bgp_cidr_only, + show_ip_bgp_cidr_only_cmd, + "show ip bgp cidr-only", SHOW_STR - IPV6_STR - MBGP_STR - "Display routes matching the communities\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "Do not export to next AS (well-known community)\n" - "community number\n" - "Do not send outside local AS (well-known community)\n" - "Do not advertise to any peer (well-known community)\n" - "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) + IP_STR + BGP_STR + "Display only routes with non-natural netmasks\n") { - struct community_list *list; - - list = community_list_lookup (bgp_clist, com, COMMUNITY_LIST_MASTER); - if (list == NULL) - { - vty_out (vty, "%% %s is not a valid community-list name%s", com, - VTY_NEWLINE); - return CMD_WARNING; - } - - return bgp_show (vty, NULL, afi, safi, - (exact ? bgp_show_type_community_list_exact : - bgp_show_type_community_list), list); + return bgp_show (vty, NULL, AFI_IP, SAFI_UNICAST, + bgp_show_type_cidr_only, NULL); } -DEFUN (show_ip_bgp_community_list, - show_ip_bgp_community_list_cmd, - "show ip bgp community-list (<1-500>|WORD)", +DEFUN (show_ip_bgp_flap_cidr_only, + show_ip_bgp_flap_cidr_only_cmd, + "show ip bgp flap-statistics cidr-only", SHOW_STR IP_STR BGP_STR - "Display routes matching the community-list\n" - "community-list number\n" - "community-list name\n") + "Display flap statistics of routes\n" + "Display only routes with non-natural netmasks\n") { - return bgp_show_community_list (vty, argv[0], 0, AFI_IP, SAFI_UNICAST); + return bgp_show (vty, NULL, AFI_IP, SAFI_UNICAST, + bgp_show_type_flap_cidr_only, NULL); } -DEFUN (show_ip_bgp_ipv4_community_list, - show_ip_bgp_ipv4_community_list_cmd, - "show ip bgp ipv4 (unicast|multicast) community-list (<1-500>|WORD)", +ALIAS (show_ip_bgp_flap_cidr_only, + show_ip_bgp_damp_flap_cidr_only_cmd, + "show ip bgp dampening flap-statistics cidr-only", + SHOW_STR + IP_STR + BGP_STR + "Display detailed information about dampening\n" + "Display flap statistics of routes\n" + "Display only routes with non-natural netmasks\n") + +DEFUN (show_ip_bgp_ipv4_cidr_only, + show_ip_bgp_ipv4_cidr_only_cmd, + "show ip bgp ipv4 (unicast|multicast) cidr-only", SHOW_STR IP_STR BGP_STR "Address family\n" "Address Family modifier\n" "Address Family modifier\n" - "Display routes matching the community-list\n" - "community-list number\n" - "community-list name\n") + "Display only routes with non-natural netmasks\n") { if (strncmp (argv[0], "m", 1) == 0) - return bgp_show_community_list (vty, argv[1], 0, AFI_IP, SAFI_MULTICAST); - - return bgp_show_community_list (vty, argv[1], 0, AFI_IP, SAFI_UNICAST); + return bgp_show (vty, NULL, AFI_IP, SAFI_MULTICAST, + bgp_show_type_cidr_only, NULL); + + return bgp_show (vty, NULL, AFI_IP, SAFI_UNICAST, + bgp_show_type_cidr_only, NULL); } -DEFUN (show_ip_bgp_community_list_exact, - show_ip_bgp_community_list_exact_cmd, - "show ip bgp community-list (<1-500>|WORD) exact-match", +DEFUN (show_ip_bgp_community_all, + show_ip_bgp_community_all_cmd, + "show ip bgp community", SHOW_STR IP_STR BGP_STR - "Display routes matching the community-list\n" - "community-list number\n" - "community-list name\n" - "Exact match of the communities\n") + "Display routes matching the communities\n") { - return bgp_show_community_list (vty, argv[0], 1, AFI_IP, SAFI_UNICAST); + return bgp_show (vty, NULL, AFI_IP, SAFI_UNICAST, + bgp_show_type_community_all, NULL); } -DEFUN (show_ip_bgp_ipv4_community_list_exact, - show_ip_bgp_ipv4_community_list_exact_cmd, - "show ip bgp ipv4 (unicast|multicast) community-list (<1-500>|WORD) exact-match", +DEFUN (show_ip_bgp_ipv4_community_all, + show_ip_bgp_ipv4_community_all_cmd, + "show ip bgp ipv4 (unicast|multicast) community", SHOW_STR IP_STR BGP_STR "Address family\n" "Address Family modifier\n" "Address Family modifier\n" - "Display routes matching the community-list\n" - "community-list number\n" - "community-list name\n" - "Exact match of the communities\n") + "Display routes matching the communities\n") { if (strncmp (argv[0], "m", 1) == 0) - return bgp_show_community_list (vty, argv[1], 1, AFI_IP, SAFI_MULTICAST); + return bgp_show (vty, NULL, AFI_IP, SAFI_MULTICAST, + bgp_show_type_community_all, NULL); - return bgp_show_community_list (vty, argv[1], 1, AFI_IP, SAFI_UNICAST); + return bgp_show (vty, NULL, AFI_IP, SAFI_UNICAST, + bgp_show_type_community_all, NULL); } -#ifdef HAVE_IPV6 -DEFUN (show_bgp_community_list, - show_bgp_community_list_cmd, - "show bgp community-list (<1-500>|WORD)", +DEFUN (show_bgp_community_all, + show_bgp_community_all_cmd, + "show bgp community", SHOW_STR BGP_STR - "Display routes matching the community-list\n" - "community-list number\n" - "community-list name\n") + "Display routes matching the communities\n") { - return bgp_show_community_list (vty, argv[0], 0, AFI_IP6, SAFI_UNICAST); + return bgp_show (vty, NULL, AFI_IP6, SAFI_UNICAST, + bgp_show_type_community_all, NULL); } -ALIAS (show_bgp_community_list, - show_bgp_ipv6_community_list_cmd, - "show bgp ipv6 community-list (<1-500>|WORD)", +ALIAS (show_bgp_community_all, + show_bgp_ipv6_community_all_cmd, + "show bgp ipv6 community", SHOW_STR BGP_STR "Address family\n" - "Display routes matching the community-list\n" - "community-list number\n" - "community-list name\n") + "Display routes matching the communities\n") /* old command */ -DEFUN (show_ipv6_bgp_community_list, - show_ipv6_bgp_community_list_cmd, - "show ipv6 bgp community-list WORD", +DEFUN (show_ipv6_bgp_community_all, + show_ipv6_bgp_community_all_cmd, + "show ipv6 bgp community", SHOW_STR IPV6_STR BGP_STR - "Display routes matching the community-list\n" - "community-list name\n") + "Display routes matching the communities\n") { - return bgp_show_community_list (vty, argv[0], 0, AFI_IP6, SAFI_UNICAST); + return bgp_show (vty, NULL, AFI_IP6, SAFI_UNICAST, + bgp_show_type_community_all, NULL); } /* old command */ -DEFUN (show_ipv6_mbgp_community_list, - show_ipv6_mbgp_community_list_cmd, - "show ipv6 mbgp community-list WORD", +DEFUN (show_ipv6_mbgp_community_all, + show_ipv6_mbgp_community_all_cmd, + "show ipv6 mbgp community", SHOW_STR IPV6_STR MBGP_STR - "Display routes matching the community-list\n" - "community-list name\n") + "Display routes matching the communities\n") { - return bgp_show_community_list (vty, argv[0], 0, AFI_IP6, SAFI_MULTICAST); -} + return bgp_show (vty, NULL, AFI_IP6, SAFI_MULTICAST, + bgp_show_type_community_all, NULL); +} -DEFUN (show_bgp_community_list_exact, - show_bgp_community_list_exact_cmd, - "show bgp community-list (<1-500>|WORD) exact-match", +/* large-community */ +DEFUN (show_ip_bgp_lcommunity_all, + show_ip_bgp_lcommunity_all_cmd, + "show ip bgp large-community", SHOW_STR + IP_STR BGP_STR - "Display routes matching the community-list\n" - "community-list number\n" - "community-list name\n" - "Exact match of the communities\n") + "Display routes matching the large-communities\n") { - return bgp_show_community_list (vty, argv[0], 1, AFI_IP6, SAFI_UNICAST); + return bgp_show (vty, NULL, AFI_IP, SAFI_UNICAST, + bgp_show_type_lcommunity_all, NULL); } -ALIAS (show_bgp_community_list_exact, - show_bgp_ipv6_community_list_exact_cmd, - "show bgp ipv6 community-list (<1-500>|WORD) exact-match", +DEFUN (show_ip_bgp_ipv4_lcommunity_all, + show_ip_bgp_ipv4_lcommunity_all_cmd, + "show ip bgp ipv4 (unicast|multicast) large-community", SHOW_STR + IP_STR BGP_STR "Address family\n" - "Display routes matching the community-list\n" - "community-list number\n" - "community-list name\n" - "Exact match of the communities\n") + "Address Family modifier\n" + "Address Family modifier\n" + "Display routes matching the large-communities\n") +{ + if (strncmp (argv[0], "m", 1) == 0) + return bgp_show (vty, NULL, AFI_IP, SAFI_MULTICAST, + bgp_show_type_lcommunity_all, NULL); + + return bgp_show (vty, NULL, AFI_IP, SAFI_UNICAST, + bgp_show_type_lcommunity_all, NULL); +} + +DEFUN (show_bgp_lcommunity_all, + show_bgp_lcommunity_all_cmd, + "show bgp large-community", + SHOW_STR + BGP_STR + "Display routes matching the large-communities\n") +{ + return bgp_show (vty, NULL, AFI_IP6, SAFI_UNICAST, + bgp_show_type_lcommunity_all, NULL); +} + +ALIAS (show_bgp_lcommunity_all, + show_bgp_ipv6_lcommunity_all_cmd, + "show bgp ipv6 large-community", + SHOW_STR + BGP_STR + "Address family\n" + "Display routes matching the large-communities\n") /* old command */ -DEFUN (show_ipv6_bgp_community_list_exact, - show_ipv6_bgp_community_list_exact_cmd, - "show ipv6 bgp community-list WORD exact-match", +DEFUN (show_ipv6_bgp_lcommunity_all, + show_ipv6_bgp_lcommunity_all_cmd, + "show ipv6 bgp large-community", SHOW_STR IPV6_STR BGP_STR - "Display routes matching the community-list\n" - "community-list name\n" - "Exact match of the communities\n") + "Display routes matching the large-communities\n") { - return bgp_show_community_list (vty, argv[0], 1, AFI_IP6, SAFI_UNICAST); + return bgp_show (vty, NULL, AFI_IP6, SAFI_UNICAST, + bgp_show_type_lcommunity_all, NULL); } /* old command */ -DEFUN (show_ipv6_mbgp_community_list_exact, - show_ipv6_mbgp_community_list_exact_cmd, - "show ipv6 mbgp community-list WORD exact-match", +DEFUN (show_ipv6_mbgp_lcommunity_all, + show_ipv6_mbgp_lcommunity_all_cmd, + "show ipv6 mbgp large-community", SHOW_STR IPV6_STR MBGP_STR - "Display routes matching the community-list\n" - "community-list name\n" - "Exact match of the communities\n") + "Display routes matching the large-communities\n") { - return bgp_show_community_list (vty, argv[0], 1, AFI_IP6, SAFI_MULTICAST); + return bgp_show (vty, NULL, AFI_IP6, SAFI_MULTICAST, + bgp_show_type_lcommunity_all, NULL); } -#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) -{ - int ret; - struct prefix *p; - - p = prefix_new(); - ret = str2prefix (prefix, p); - if (! ret) - { - vty_out (vty, "%% Malformed Prefix%s", VTY_NEWLINE); - return CMD_WARNING; - } - ret = bgp_show (vty, NULL, afi, safi, type, p); - prefix_free(p); - return ret; +DEFUN (show_bgp_ipv4_route_map, + show_bgp_ipv4_route_map_cmd, + "show bgp ipv4 route-map WORD", + SHOW_STR + BGP_STR + IP_STR + "Display routes matching the route-map\n" + "A route-map to match on\n") +{ + return bgp_show_route_map (vty, argv[0], AFI_IP, SAFI_UNICAST, + bgp_show_type_route_map); } -DEFUN (show_ip_bgp_prefix_longer, - show_ip_bgp_prefix_longer_cmd, - "show ip bgp A.B.C.D/M longer-prefixes", +DEFUN (show_bgp_ipv4_safi_flap_route_map, + show_bgp_ipv4_safi_flap_route_map_cmd, + "show bgp ipv4 (encap|multicast|unicast|vpn) flap-statistics route-map WORD", SHOW_STR - IP_STR BGP_STR - "IP prefix /, e.g., 35.0.0.0/8\n" - "Display route and more specific routes\n") + IP_STR + "Address Family Modifier\n" + "Address Family Modifier\n" + "Address Family Modifier\n" + "Address Family Modifier\n" + "Display flap statistics of routes\n" + "Display routes matching the route-map\n" + "A route-map to match on\n") { - return bgp_show_prefix_longer (vty, argv[0], AFI_IP, SAFI_UNICAST, - bgp_show_type_prefix_longer); + safi_t safi; + if (bgp_parse_safi(argv[0], &safi)) { + vty_out (vty, "Error: Bad SAFI: %s%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + return bgp_show_route_map (vty, argv[1], AFI_IP, safi, + bgp_show_type_flap_route_map); } -DEFUN (show_ip_bgp_flap_prefix_longer, - show_ip_bgp_flap_prefix_longer_cmd, - "show ip bgp flap-statistics A.B.C.D/M longer-prefixes", +ALIAS (show_bgp_ipv4_safi_flap_route_map, + show_bgp_ipv4_safi_damp_flap_route_map_cmd, + "show bgp ipv4 (encap|multicast|unicast|vpn) dampening flap-statistics route-map WORD", SHOW_STR + BGP_STR IP_STR + "Address Family Modifier\n" + "Address Family Modifier\n" + "Address Family Modifier\n" + "Address Family Modifier\n" + "Display detailed information about dampening\n" + "Display flap statistics of routes\n" + "Display routes matching the route-map\n" + "A route-map to match on\n") + +DEFUN (show_bgp_ipv6_safi_flap_route_map, + show_bgp_ipv6_safi_flap_route_map_cmd, + "show bgp ipv6 (encap|multicast|unicast|vpn) flap-statistics route-map WORD", + SHOW_STR BGP_STR + IPV6_STR + "Address Family Modifier\n" + "Address Family Modifier\n" + "Address Family Modifier\n" + "Address Family Modifier\n" "Display flap statistics of routes\n" - "IP prefix /, e.g., 35.0.0.0/8\n" - "Display route and more specific routes\n") + "Display routes matching the route-map\n" + "A route-map to match on\n") { - return bgp_show_prefix_longer (vty, argv[0], AFI_IP, SAFI_UNICAST, - bgp_show_type_flap_prefix_longer); + safi_t safi; + if (bgp_parse_safi(argv[0], &safi)) { + vty_out (vty, "Error: Bad SAFI: %s%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + return bgp_show_route_map (vty, argv[1], AFI_IP6, safi, + bgp_show_type_flap_route_map); } +ALIAS (show_bgp_ipv6_safi_flap_route_map, + show_bgp_ipv6_safi_damp_flap_route_map_cmd, + "show bgp ipv6 (encap|multicast|unicast|vpn) dampening flap-statistics route-map WORD", + SHOW_STR + BGP_STR + IPV6_STR + "Address Family Modifier\n" + "Address Family Modifier\n" + "Address Family Modifier\n" + "Address Family Modifier\n" + "Display detailed information about dampening\n" + "Display flap statistics of routes\n" + "Display routes matching the route-map\n" + "A route-map to match on\n") -DEFUN (show_ip_bgp_ipv4_prefix_longer, - show_ip_bgp_ipv4_prefix_longer_cmd, - "show ip bgp ipv4 (unicast|multicast) A.B.C.D/M longer-prefixes", +DEFUN (show_bgp_ipv4_safi_route_map, + show_bgp_ipv4_safi_route_map_cmd, + "show bgp ipv4 (encap|multicast|unicast|vpn) route-map WORD", SHOW_STR - IP_STR BGP_STR "Address family\n" "Address Family modifier\n" "Address Family modifier\n" - "IP prefix /, e.g., 35.0.0.0/8\n" - "Display route and more specific routes\n") + "Address Family modifier\n" + "Address Family modifier\n" + "Display routes matching the route-map\n" + "A route-map to match on\n") { - if (strncmp (argv[0], "m", 1) == 0) - return bgp_show_prefix_longer (vty, argv[1], AFI_IP, SAFI_MULTICAST, - bgp_show_type_prefix_longer); + safi_t safi; + if (bgp_parse_safi(argv[0], &safi)) { + vty_out (vty, "Error: Bad SAFI: %s%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + return bgp_show_route_map (vty, argv[1], AFI_IP, safi, + bgp_show_type_route_map); +} - return bgp_show_prefix_longer (vty, argv[1], AFI_IP, SAFI_UNICAST, - bgp_show_type_prefix_longer); +DEFUN (show_bgp_ipv6_safi_route_map, + show_bgp_ipv6_safi_route_map_cmd, + "show bgp ipv6 (encap|multicast|unicast|vpn) route-map WORD", + SHOW_STR + BGP_STR + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Display routes matching the route-map\n" + "A route-map to match on\n") +{ + safi_t safi; + if (bgp_parse_safi(argv[0], &safi)) { + vty_out (vty, "Error: Bad SAFI: %s%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + return bgp_show_route_map (vty, argv[1], AFI_IP6, safi, + bgp_show_type_route_map); } -DEFUN (show_ip_bgp_flap_address, - show_ip_bgp_flap_address_cmd, - "show ip bgp flap-statistics A.B.C.D", +DEFUN (show_bgp_ipv6_route_map, + show_bgp_ipv6_route_map_cmd, + "show bgp ipv6 route-map WORD", SHOW_STR - IP_STR BGP_STR - "Display flap statistics of routes\n" - "Network in the BGP routing table to display\n") + "Address family\n" + "Display routes matching the route-map\n" + "A route-map to match on\n") { - return bgp_show_prefix_longer (vty, argv[0], AFI_IP, SAFI_UNICAST, - bgp_show_type_flap_address); + return bgp_show_route_map (vty, argv[0], AFI_IP6, SAFI_UNICAST, + bgp_show_type_route_map); } -DEFUN (show_ip_bgp_flap_prefix, - show_ip_bgp_flap_prefix_cmd, - "show ip bgp flap-statistics A.B.C.D/M", +DEFUN (show_bgp_ipv4_cidr_only, + show_bgp_ipv4_cidr_only_cmd, + "show bgp ipv4 cidr-only", SHOW_STR - IP_STR BGP_STR - "Display flap statistics of routes\n" - "IP prefix /, e.g., 35.0.0.0/8\n") + IP_STR + "Display only routes with non-natural netmasks\n") { - return bgp_show_prefix_longer (vty, argv[0], AFI_IP, SAFI_UNICAST, - bgp_show_type_flap_prefix); + return bgp_show (vty, NULL, AFI_IP, SAFI_UNICAST, + bgp_show_type_cidr_only, NULL); } -#ifdef HAVE_IPV6 -DEFUN (show_bgp_prefix_longer, - show_bgp_prefix_longer_cmd, - "show bgp X:X::X:X/M longer-prefixes", + +DEFUN (show_bgp_ipv4_safi_flap_cidr_only, + show_bgp_ipv4_safi_flap_cidr_only_cmd, + "show bgp ipv4 (encap|multicast|unicast|vpn) flap-statistics cidr-only", SHOW_STR BGP_STR - "IPv6 prefix /\n" - "Display route and more specific routes\n") + "Address Family\n" + "Address Family Modifier\n" + "Address Family Modifier\n" + "Address Family Modifier\n" + "Address Family Modifier\n" + "Display flap statistics of routes\n" + "Display only routes with non-natural netmasks\n") { - return bgp_show_prefix_longer (vty, argv[0], AFI_IP6, SAFI_UNICAST, - bgp_show_type_prefix_longer); + safi_t safi; + + if (bgp_parse_safi(argv[0], &safi)) { + vty_out (vty, "Error: Bad SAFI: %s%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + return bgp_show (vty, NULL, AFI_IP, safi, bgp_show_type_flap_cidr_only, NULL); } -ALIAS (show_bgp_prefix_longer, - show_bgp_ipv6_prefix_longer_cmd, - "show bgp ipv6 X:X::X:X/M longer-prefixes", +ALIAS (show_bgp_ipv4_safi_flap_cidr_only, + show_bgp_ipv4_safi_damp_flap_cidr_only_cmd, + "show bgp ipv4 (encap|multicast|unicast|vpn) dampening flap-statistics cidr-only", SHOW_STR BGP_STR - "Address family\n" - "IPv6 prefix /\n" - "Display route and more specific routes\n") + "Address Family\n" + "Address Family Modifier\n" + "Address Family Modifier\n" + "Address Family Modifier\n" + "Address Family Modifier\n" + "Display detailed information about dampening\n" + "Display flap statistics of routes\n" + "Display only routes with non-natural netmasks\n") -/* old command */ -DEFUN (show_ipv6_bgp_prefix_longer, - show_ipv6_bgp_prefix_longer_cmd, - "show ipv6 bgp X:X::X:X/M longer-prefixes", +DEFUN (show_bgp_ipv4_safi_cidr_only, + show_bgp_ipv4_safi_cidr_only_cmd, + "show bgp ipv4 (unicast|multicast) cidr-only", SHOW_STR - IPV6_STR BGP_STR - "IPv6 prefix /, e.g., 3ffe::/16\n" - "Display route and more specific routes\n") + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Display only routes with non-natural netmasks\n") { - return bgp_show_prefix_longer (vty, argv[0], AFI_IP6, SAFI_UNICAST, - bgp_show_type_prefix_longer); + if (strncmp (argv[0], "m", 1) == 0) + return bgp_show (vty, NULL, AFI_IP, SAFI_MULTICAST, + bgp_show_type_cidr_only, NULL); + + return bgp_show (vty, NULL, AFI_IP, SAFI_UNICAST, + bgp_show_type_cidr_only, NULL); } -/* old command */ -DEFUN (show_ipv6_mbgp_prefix_longer, - show_ipv6_mbgp_prefix_longer_cmd, - "show ipv6 mbgp X:X::X:X/M longer-prefixes", +/* new046 */ +DEFUN (show_bgp_afi_safi_community_all, + show_bgp_afi_safi_community_all_cmd, + "show bgp (ipv4|ipv6) (encap|multicast|unicast|vpn) community", SHOW_STR - IPV6_STR - MBGP_STR - "IPv6 prefix /, e.g., 3ffe::/16\n" - "Display route and more specific routes\n") + BGP_STR + "Address family\n" + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Display routes matching the communities\n") { - return bgp_show_prefix_longer (vty, argv[0], AFI_IP6, SAFI_MULTICAST, - bgp_show_type_prefix_longer); -} -#endif /* HAVE_IPV6 */ + safi_t safi; + afi_t afi; -static struct peer * -peer_lookup_in_view (struct vty *vty, const char *view_name, - const char *ip_str) -{ - int ret; - struct bgp *bgp; - struct peer *peer; - union sockunion su; + if (bgp_parse_afi(argv[0], &afi)) { + vty_out (vty, "Error: Bad AFI: %s%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + if (bgp_parse_safi(argv[1], &safi)) { + vty_out (vty, "Error: Bad SAFI: %s%s", argv[1], VTY_NEWLINE); + return CMD_WARNING; + } - /* BGP structure lookup. */ - if (view_name) - { - bgp = bgp_lookup_by_name (view_name); - if (! bgp) - { - vty_out (vty, "Can't find BGP view %s%s", view_name, VTY_NEWLINE); - return NULL; - } + return bgp_show (vty, NULL, afi, safi, bgp_show_type_community_all, NULL); +} +DEFUN (show_bgp_afi_community_all, + show_bgp_afi_community_all_cmd, + "show bgp (ipv4|ipv6) community", + SHOW_STR + BGP_STR + "Address family\n" + "Address family\n" + "Display routes matching the communities\n") +{ + afi_t afi; + safi_t safi = SAFI_UNICAST; + + if (bgp_parse_afi(argv[0], &afi)) { + vty_out (vty, "Error: Bad AFI: %s%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + return bgp_show (vty, NULL, afi, safi, bgp_show_type_community_all, NULL); +} + +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) +{ + struct community *com; + struct buffer *b; + struct bgp *bgp; + int i, rv; + char *str; + int first = 0; + + /* BGP structure lookup */ + if (view_name) + { + bgp = bgp_lookup_by_name (view_name); + if (bgp == NULL) + { + vty_out (vty, "Can't find BGP view %s%s", view_name, VTY_NEWLINE); + return CMD_WARNING; + } } else { bgp = bgp_get_default (); - if (! bgp) - { - vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE); - return NULL; - } + if (bgp == NULL) + { + vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + } + + b = buffer_new (1024); + for (i = 0; i < argc; i++) + { + if (first) + buffer_putc (b, ' '); + else + { + if ((strcmp (argv[i], "unicast") == 0) || (strcmp (argv[i], "multicast") == 0)) + continue; + first = 1; + } + + buffer_putstr (b, argv[i]); + } + buffer_putc (b, '\0'); + + str = buffer_getstr (b); + buffer_free (b); + + com = community_str2com (str); + XFREE (MTYPE_TMP, str); + if (! com) + { + vty_out (vty, "%% Community malformed: %s", VTY_NEWLINE); + return CMD_WARNING; } - /* Get peer sockunion. */ - ret = str2sockunion (ip_str, &su); - if (ret < 0) - { - vty_out (vty, "Malformed address: %s%s", ip_str, VTY_NEWLINE); - return NULL; - } + rv = bgp_show (vty, bgp, afi, safi, + (exact ? bgp_show_type_community_exact : + bgp_show_type_community), com); + community_free(com); + return rv; +} + +DEFUN (show_ip_bgp_community, + show_ip_bgp_community_cmd, + "show ip bgp community (AA:NN|local-AS|no-advertise|no-export)", + SHOW_STR + IP_STR + BGP_STR + "Display routes matching the communities\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n") +{ + return bgp_show_community (vty, NULL, argc, argv, 0, AFI_IP, SAFI_UNICAST); +} + +ALIAS (show_ip_bgp_community, + show_ip_bgp_community2_cmd, + "show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)", + SHOW_STR + IP_STR + BGP_STR + "Display routes matching the communities\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n") + +ALIAS (show_ip_bgp_community, + show_ip_bgp_community3_cmd, + "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)", + SHOW_STR + IP_STR + BGP_STR + "Display routes matching the communities\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n") + +ALIAS (show_ip_bgp_community, + show_ip_bgp_community4_cmd, + "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)", + SHOW_STR + IP_STR + BGP_STR + "Display routes matching the communities\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n") + +DEFUN (show_ip_bgp_ipv4_community, + show_ip_bgp_ipv4_community_cmd, + "show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export)", + SHOW_STR + IP_STR + BGP_STR + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Display routes matching the communities\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n") +{ + if (strncmp (argv[0], "m", 1) == 0) + return bgp_show_community (vty, NULL, argc, argv, 0, AFI_IP, SAFI_MULTICAST); + + return bgp_show_community (vty, NULL, argc, argv, 0, AFI_IP, SAFI_UNICAST); +} + +ALIAS (show_ip_bgp_ipv4_community, + show_ip_bgp_ipv4_community2_cmd, + "show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)", + SHOW_STR + IP_STR + BGP_STR + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Display routes matching the communities\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n") + +ALIAS (show_ip_bgp_ipv4_community, + show_ip_bgp_ipv4_community3_cmd, + "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)", + SHOW_STR + IP_STR + BGP_STR + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Display routes matching the communities\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n") + +ALIAS (show_ip_bgp_ipv4_community, + show_ip_bgp_ipv4_community4_cmd, + "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)", + SHOW_STR + IP_STR + BGP_STR + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Display routes matching the communities\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n") + +DEFUN (show_ip_bgp_community_exact, + show_ip_bgp_community_exact_cmd, + "show ip bgp community (AA:NN|local-AS|no-advertise|no-export) exact-match", + SHOW_STR + IP_STR + BGP_STR + "Display routes matching the communities\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "Exact match of the communities") +{ + return bgp_show_community (vty, NULL, argc, argv, 1, AFI_IP, SAFI_UNICAST); +} + +ALIAS (show_ip_bgp_community_exact, + show_ip_bgp_community2_exact_cmd, + "show ip bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match", + SHOW_STR + IP_STR + BGP_STR + "Display routes matching the communities\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "Exact match of the communities") + +ALIAS (show_ip_bgp_community_exact, + show_ip_bgp_community3_exact_cmd, + "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) exact-match", + SHOW_STR + IP_STR + BGP_STR + "Display routes matching the communities\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "Exact match of the communities") + +ALIAS (show_ip_bgp_community_exact, + show_ip_bgp_community4_exact_cmd, + "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) exact-match", + SHOW_STR + IP_STR + BGP_STR + "Display routes matching the communities\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "Exact match of the communities") + +DEFUN (show_ip_bgp_ipv4_community_exact, + show_ip_bgp_ipv4_community_exact_cmd, + "show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) exact-match", + SHOW_STR + IP_STR + BGP_STR + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Display routes matching the communities\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "Exact match of the communities") +{ + if (strncmp (argv[0], "m", 1) == 0) + return bgp_show_community (vty, NULL, argc, argv, 1, AFI_IP, SAFI_MULTICAST); + + return bgp_show_community (vty, NULL, argc, argv, 1, AFI_IP, SAFI_UNICAST); +} + +ALIAS (show_ip_bgp_ipv4_community_exact, + show_ip_bgp_ipv4_community2_exact_cmd, + "show ip bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match", + SHOW_STR + IP_STR + BGP_STR + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Display routes matching the communities\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "Exact match of the communities") + +ALIAS (show_ip_bgp_ipv4_community_exact, + show_ip_bgp_ipv4_community3_exact_cmd, + "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) exact-match", + SHOW_STR + IP_STR + BGP_STR + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Display routes matching the communities\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "Exact match of the communities") + +ALIAS (show_ip_bgp_ipv4_community_exact, + show_ip_bgp_ipv4_community4_exact_cmd, + "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", + SHOW_STR + IP_STR + BGP_STR + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Display routes matching the communities\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "Exact match of the communities") + +DEFUN (show_bgp_community, + show_bgp_community_cmd, + "show bgp community (AA:NN|local-AS|no-advertise|no-export)", + SHOW_STR + BGP_STR + "Display routes matching the communities\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n") +{ + return bgp_show_community (vty, NULL, argc, argv, 0, AFI_IP6, SAFI_UNICAST); +} + +ALIAS (show_bgp_community, + show_bgp_ipv6_community_cmd, + "show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export)", + SHOW_STR + BGP_STR + "Address family\n" + "Display routes matching the communities\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n") + +ALIAS (show_bgp_community, + show_bgp_community2_cmd, + "show bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)", + SHOW_STR + BGP_STR + "Display routes matching the communities\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n") + +ALIAS (show_bgp_community, + show_bgp_ipv6_community2_cmd, + "show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)", + SHOW_STR + BGP_STR + "Address family\n" + "Display routes matching the communities\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n") + +ALIAS (show_bgp_community, + show_bgp_community3_cmd, + "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)", + SHOW_STR + BGP_STR + "Display routes matching the communities\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n") + +ALIAS (show_bgp_community, + show_bgp_ipv6_community3_cmd, + "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)", + SHOW_STR + BGP_STR + "Address family\n" + "Display routes matching the communities\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n") + +ALIAS (show_bgp_community, + show_bgp_community4_cmd, + "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)", + SHOW_STR + BGP_STR + "Display routes matching the communities\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n") + +ALIAS (show_bgp_community, + show_bgp_ipv6_community4_cmd, + "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)", + SHOW_STR + BGP_STR + "Address family\n" + "Display routes matching the communities\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n") + +/* old command */ +DEFUN (show_ipv6_bgp_community, + show_ipv6_bgp_community_cmd, + "show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export)", + SHOW_STR + IPV6_STR + BGP_STR + "Display routes matching the communities\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n") +{ + return bgp_show_community (vty, NULL, argc, argv, 0, AFI_IP6, SAFI_UNICAST); +} + +/* old command */ +ALIAS (show_ipv6_bgp_community, + show_ipv6_bgp_community2_cmd, + "show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)", + SHOW_STR + IPV6_STR + BGP_STR + "Display routes matching the communities\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n") + +/* old command */ +ALIAS (show_ipv6_bgp_community, + show_ipv6_bgp_community3_cmd, + "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)", + SHOW_STR + IPV6_STR + BGP_STR + "Display routes matching the communities\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n") + +/* old command */ +ALIAS (show_ipv6_bgp_community, + show_ipv6_bgp_community4_cmd, + "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)", + SHOW_STR + IPV6_STR + BGP_STR + "Display routes matching the communities\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n") + +DEFUN (show_bgp_community_exact, + show_bgp_community_exact_cmd, + "show bgp community (AA:NN|local-AS|no-advertise|no-export) exact-match", + SHOW_STR + BGP_STR + "Display routes matching the communities\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "Exact match of the communities") +{ + return bgp_show_community (vty, NULL, argc, argv, 1, AFI_IP6, SAFI_UNICAST); +} + +ALIAS (show_bgp_community_exact, + show_bgp_ipv6_community_exact_cmd, + "show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) exact-match", + SHOW_STR + BGP_STR + "Address family\n" + "Display routes matching the communities\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "Exact match of the communities") + +ALIAS (show_bgp_community_exact, + show_bgp_community2_exact_cmd, + "show bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match", + SHOW_STR + BGP_STR + "Display routes matching the communities\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "Exact match of the communities") + +ALIAS (show_bgp_community_exact, + show_bgp_ipv6_community2_exact_cmd, + "show bgp ipv6 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match", + SHOW_STR + BGP_STR + "Address family\n" + "Display routes matching the communities\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "Exact match of the communities") + +ALIAS (show_bgp_community_exact, + show_bgp_community3_exact_cmd, + "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) exact-match", + SHOW_STR + BGP_STR + "Display routes matching the communities\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "Exact match of the communities") + +ALIAS (show_bgp_community_exact, + show_bgp_ipv6_community3_exact_cmd, + "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) exact-match", + SHOW_STR + BGP_STR + "Address family\n" + "Display routes matching the communities\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "Exact match of the communities") + +ALIAS (show_bgp_community_exact, + show_bgp_community4_exact_cmd, + "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", + SHOW_STR + BGP_STR + "Display routes matching the communities\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "Exact match of the communities") + +ALIAS (show_bgp_community_exact, + show_bgp_ipv6_community4_exact_cmd, + "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", + SHOW_STR + BGP_STR + "Address family\n" + "Display routes matching the communities\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "Exact match of the communities") + +/* old command */ +DEFUN (show_ipv6_bgp_community_exact, + show_ipv6_bgp_community_exact_cmd, + "show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) exact-match", + SHOW_STR + IPV6_STR + BGP_STR + "Display routes matching the communities\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "Exact match of the communities") +{ + return bgp_show_community (vty, NULL, argc, argv, 1, AFI_IP6, SAFI_UNICAST); +} + +/* old command */ +ALIAS (show_ipv6_bgp_community_exact, + show_ipv6_bgp_community2_exact_cmd, + "show ipv6 bgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match", + SHOW_STR + IPV6_STR + BGP_STR + "Display routes matching the communities\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "Exact match of the communities") + +/* old command */ +ALIAS (show_ipv6_bgp_community_exact, + show_ipv6_bgp_community3_exact_cmd, + "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) exact-match", + SHOW_STR + IPV6_STR + BGP_STR + "Display routes matching the communities\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "Exact match of the communities") + +/* old command */ +ALIAS (show_ipv6_bgp_community_exact, + show_ipv6_bgp_community4_exact_cmd, + "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", + SHOW_STR + IPV6_STR + BGP_STR + "Display routes matching the communities\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "Exact match of the communities") + +/* old command */ +DEFUN (show_ipv6_mbgp_community, + show_ipv6_mbgp_community_cmd, + "show ipv6 mbgp community (AA:NN|local-AS|no-advertise|no-export)", + SHOW_STR + IPV6_STR + MBGP_STR + "Display routes matching the communities\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n") +{ + return bgp_show_community (vty, NULL, argc, argv, 0, AFI_IP6, SAFI_MULTICAST); +} + +/* old command */ +ALIAS (show_ipv6_mbgp_community, + show_ipv6_mbgp_community2_cmd, + "show ipv6 mbgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)", + SHOW_STR + IPV6_STR + MBGP_STR + "Display routes matching the communities\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n") + +/* old command */ +ALIAS (show_ipv6_mbgp_community, + show_ipv6_mbgp_community3_cmd, + "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)", + SHOW_STR + IPV6_STR + MBGP_STR + "Display routes matching the communities\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n") + +/* old command */ +ALIAS (show_ipv6_mbgp_community, + show_ipv6_mbgp_community4_cmd, + "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)", + SHOW_STR + IPV6_STR + MBGP_STR + "Display routes matching the communities\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n") + +/* old command */ +DEFUN (show_ipv6_mbgp_community_exact, + show_ipv6_mbgp_community_exact_cmd, + "show ipv6 mbgp community (AA:NN|local-AS|no-advertise|no-export) exact-match", + SHOW_STR + IPV6_STR + MBGP_STR + "Display routes matching the communities\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "Exact match of the communities") +{ + return bgp_show_community (vty, NULL, argc, argv, 1, AFI_IP6, SAFI_MULTICAST); +} + +/* old command */ +ALIAS (show_ipv6_mbgp_community_exact, + show_ipv6_mbgp_community2_exact_cmd, + "show ipv6 mbgp community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match", + SHOW_STR + IPV6_STR + MBGP_STR + "Display routes matching the communities\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "Exact match of the communities") + +/* old command */ +ALIAS (show_ipv6_mbgp_community_exact, + show_ipv6_mbgp_community3_exact_cmd, + "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) exact-match", + SHOW_STR + IPV6_STR + MBGP_STR + "Display routes matching the communities\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "Exact match of the communities") + +/* old command */ +ALIAS (show_ipv6_mbgp_community_exact, + show_ipv6_mbgp_community4_exact_cmd, + "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", + SHOW_STR + IPV6_STR + MBGP_STR + "Display routes matching the communities\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "Exact match of the communities") + +DEFUN (show_bgp_ipv4_community, + show_bgp_ipv4_community_cmd, + "show bgp ipv4 community (AA:NN|local-AS|no-advertise|no-export)", + SHOW_STR + BGP_STR + IP_STR + "Display routes matching the communities\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n") +{ + return bgp_show_community (vty, NULL, argc, argv, 0, AFI_IP, SAFI_UNICAST); +} + +ALIAS (show_bgp_ipv4_community, + show_bgp_ipv4_community2_cmd, + "show bgp ipv4 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)", + SHOW_STR + BGP_STR + IP_STR + "Display routes matching the communities\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n") + +ALIAS (show_bgp_ipv4_community, + show_bgp_ipv4_community3_cmd, + "show bgp ipv4 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)", + SHOW_STR + BGP_STR + IP_STR + "Display routes matching the communities\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n") + +ALIAS (show_bgp_ipv4_community, + show_bgp_ipv4_community4_cmd, + "show bgp ipv4 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)", + SHOW_STR + BGP_STR + IP_STR + "Display routes matching the communities\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n") + +DEFUN (show_bgp_ipv4_safi_community, + show_bgp_ipv4_safi_community_cmd, + "show bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export)", + SHOW_STR + BGP_STR + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Display routes matching the communities\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n") +{ + if (strncmp (argv[0], "m", 1) == 0) + return bgp_show_community (vty, NULL, argc, argv, 0, AFI_IP, SAFI_MULTICAST); + + return bgp_show_community (vty, NULL, argc, argv, 0, AFI_IP, SAFI_UNICAST); +} + +ALIAS (show_bgp_ipv4_safi_community, + show_bgp_ipv4_safi_community2_cmd, + "show bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)", + SHOW_STR + BGP_STR + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Display routes matching the communities\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n") + +ALIAS (show_bgp_ipv4_safi_community, + show_bgp_ipv4_safi_community3_cmd, + "show 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)", + SHOW_STR + BGP_STR + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Display routes matching the communities\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n") + +ALIAS (show_bgp_ipv4_safi_community, + show_bgp_ipv4_safi_community4_cmd, + "show 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)", + SHOW_STR + BGP_STR + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Display routes matching the communities\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n") + +DEFUN (show_bgp_view_afi_safi_community_all, + show_bgp_view_afi_safi_community_all_cmd, + "show bgp view WORD (ipv4|ipv6) (unicast|multicast) community", + SHOW_STR + BGP_STR + "BGP view\n" + "View name\n" + "Address family\n" + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Display routes matching the communities\n") +{ + int afi; + int safi; + struct bgp *bgp; + + /* BGP structure lookup. */ + bgp = bgp_lookup_by_name (argv[0]); + if (bgp == NULL) + { + vty_out (vty, "Can't find BGP view %s%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + + afi = (strncmp (argv[1], "ipv6", 4) == 0) ? AFI_IP6 : AFI_IP; + safi = (strncmp (argv[2], "m", 1) == 0) ? SAFI_MULTICAST : SAFI_UNICAST; + return bgp_show (vty, bgp, afi, safi, bgp_show_type_community_all, NULL); +} + +DEFUN (show_bgp_view_afi_safi_community, + show_bgp_view_afi_safi_community_cmd, + "show bgp view WORD (ipv4|ipv6) (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export)", + SHOW_STR + BGP_STR + "BGP view\n" + "View name\n" + "Address family\n" + "Address family\n" + "Address family modifier\n" + "Address family modifier\n" + "Display routes matching the communities\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n") +{ + int afi; + int safi; + + afi = (strncmp (argv[1], "ipv6", 4) == 0) ? AFI_IP6 : AFI_IP; + safi = (strncmp (argv[2], "m", 1) == 0) ? SAFI_MULTICAST : SAFI_UNICAST; + return bgp_show_community (vty, argv[0], argc-3, &argv[3], 0, afi, safi); +} + +ALIAS (show_bgp_view_afi_safi_community, + show_bgp_view_afi_safi_community2_cmd, + "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)", + SHOW_STR + BGP_STR + "BGP view\n" + "View name\n" + "Address family\n" + "Address family\n" + "Address family modifier\n" + "Address family modifier\n" + "Display routes matching the communities\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n") + +ALIAS (show_bgp_view_afi_safi_community, + show_bgp_view_afi_safi_community3_cmd, + "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)", + SHOW_STR + BGP_STR + "BGP view\n" + "View name\n" + "Address family\n" + "Address family\n" + "Address family modifier\n" + "Address family modifier\n" + "Display routes matching the communities\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n") + +ALIAS (show_bgp_view_afi_safi_community, + show_bgp_view_afi_safi_community4_cmd, + "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)", + SHOW_STR + BGP_STR + "BGP view\n" + "View name\n" + "Address family\n" + "Address family\n" + "Address family modifier\n" + "Address family modifier\n" + "Display routes matching the communities\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n") + +DEFUN (show_bgp_ipv4_community_exact, + show_bgp_ipv4_community_exact_cmd, + "show bgp ipv4 community (AA:NN|local-AS|no-advertise|no-export) exact-match", + SHOW_STR + BGP_STR + IP_STR + "Display routes matching the communities\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "Exact match of the communities") +{ + return bgp_show_community (vty, NULL, argc, argv, 1, AFI_IP, SAFI_UNICAST); +} + +ALIAS (show_bgp_ipv4_community_exact, + show_bgp_ipv4_community2_exact_cmd, + "show bgp ipv4 community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match", + SHOW_STR + BGP_STR + IP_STR + "Display routes matching the communities\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "Exact match of the communities") + +ALIAS (show_bgp_ipv4_community_exact, + show_bgp_ipv4_community3_exact_cmd, + "show bgp ipv4 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) exact-match", + SHOW_STR + BGP_STR + IP_STR + "Display routes matching the communities\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "Exact match of the communities") + +ALIAS (show_bgp_ipv4_community_exact, + show_bgp_ipv4_community4_exact_cmd, + "show bgp ipv4 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", + SHOW_STR + BGP_STR + IP_STR + "Display routes matching the communities\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "Exact match of the communities") + +DEFUN (show_bgp_ipv4_safi_community4_exact, + show_bgp_ipv4_safi_community_exact_cmd, + "show bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) exact-match", + SHOW_STR + BGP_STR + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Display routes matching the communities\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "Exact match of the communities") +{ + if (strncmp (argv[0], "m", 1) == 0) + return bgp_show_community (vty, NULL, argc, argv, 1, AFI_IP, SAFI_MULTICAST); + + return bgp_show_community (vty, NULL, argc, argv, 1, AFI_IP, SAFI_UNICAST); +} + +ALIAS (show_bgp_ipv4_safi_community4_exact, + show_bgp_ipv4_safi_community2_exact_cmd, + "show bgp ipv4 (unicast|multicast) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match", + SHOW_STR + BGP_STR + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Display routes matching the communities\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "Exact match of the communities") + +ALIAS (show_bgp_ipv4_safi_community4_exact, + show_bgp_ipv4_safi_community3_exact_cmd, + "show 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) exact-match", + SHOW_STR + BGP_STR + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Display routes matching the communities\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "Exact match of the communities") + +ALIAS (show_bgp_ipv4_safi_community4_exact, + show_bgp_ipv4_safi_community4_exact_cmd, + "show 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", + SHOW_STR + BGP_STR + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Display routes matching the communities\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "Exact match of the communities") + +DEFUN (show_bgp_ipv6_safi_community, + show_bgp_ipv6_safi_community_cmd, + "show bgp ipv6 (encap|multicast|unicast|vpn) community (AA:NN|local-AS|no-advertise|no-export)", + SHOW_STR + BGP_STR + "Address family\n" + "Address family modifier\n" + "Address family modifier\n" + "Address family modifier\n" + "Address family modifier\n" + "Display routes matching the communities\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n") +{ + safi_t safi; + + if (bgp_parse_safi(argv[0], &safi)) { + vty_out (vty, "Error: Bad SAFI: %s%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + return bgp_show_community (vty, NULL, argc-1, argv+1, 0, AFI_IP6, safi); +} + +ALIAS (show_bgp_ipv6_safi_community, + show_bgp_ipv6_safi_community2_cmd, + "show bgp ipv6 (encap|multicast|unicast|vpn) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export)", + SHOW_STR + BGP_STR + "Address family\n" + "Address family modifier\n" + "Address family modifier\n" + "Address family modifier\n" + "Address family modifier\n" + "Display routes matching the communities\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n") + +ALIAS (show_bgp_ipv6_safi_community, + show_bgp_ipv6_safi_community3_cmd, + "show bgp ipv6 (encap|multicast|unicast|vpn) 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)", + SHOW_STR + BGP_STR + "Address family\n" + "Address family modifier\n" + "Address family modifier\n" + "Address family modifier\n" + "Address family modifier\n" + "Display routes matching the communities\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n") + +ALIAS (show_bgp_ipv6_safi_community, + show_bgp_ipv6_safi_community4_cmd, + "show bgp ipv6 (encap|multicast|unicast|vpn) 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)", + SHOW_STR + BGP_STR + "Address family\n" + "Address family modifier\n" + "Address family modifier\n" + "Address family modifier\n" + "Address family modifier\n" + "Display routes matching the communities\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n") + + +DEFUN (show_bgp_ipv6_safi_community_exact, + show_bgp_ipv6_safi_community_exact_cmd, + "show bgp ipv6 (encap|multicast|unicast|vpn) community (AA:NN|local-AS|no-advertise|no-export) exact-match", + SHOW_STR + BGP_STR + "Address family\n" + "Address family modifier\n" + "Address family modifier\n" + "Address family modifier\n" + "Address family modifier\n" + "Display routes matching the communities\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "Exact match of the communities") +{ + safi_t safi; + + if (bgp_parse_safi(argv[0], &safi)) { + vty_out (vty, "Error: Bad SAFI: %s%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + return bgp_show_community (vty, NULL, argc-1, argv+1, 1, AFI_IP6, safi); +} + + +ALIAS (show_bgp_community_exact, + show_bgp_ipv6_safi_community2_exact_cmd, + "show bgp ipv6 (encap|multicast|unicast|vpn) community (AA:NN|local-AS|no-advertise|no-export) (AA:NN|local-AS|no-advertise|no-export) exact-match", + SHOW_STR + BGP_STR + "Address family\n" + "Address family modifier\n" + "Address family modifier\n" + "Address family modifier\n" + "Address family modifier\n" + "Display routes matching the communities\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "Exact match of the communities") + +ALIAS (show_bgp_community_exact, + show_bgp_ipv6_safi_community3_exact_cmd, + "show bgp ipv6 (encap|multicast|unicast|vpn) 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) exact-match", + SHOW_STR + BGP_STR + "Address family\n" + "Address family modifier\n" + "Address family modifier\n" + "Address family modifier\n" + "Address family modifier\n" + "Display routes matching the communities\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "Exact match of the communities") + +ALIAS (show_bgp_community_exact, + show_bgp_ipv6_safi_community4_exact_cmd, + "show bgp ipv6 (encap|multicast|unicast|vpn) 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", + SHOW_STR + BGP_STR + "Address family\n" + "Address family modifier\n" + "Address family modifier\n" + "Address family modifier\n" + "Address family modifier\n" + "Display routes matching the communities\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "community number\n" + "Do not send outside local AS (well-known community)\n" + "Do not advertise to any peer (well-known community)\n" + "Do not export to next AS (well-known community)\n" + "Exact match of the communities") + +static int +bgp_show_community_list (struct vty *vty, const char *com, int exact, + afi_t afi, safi_t safi) +{ + struct community_list *list; + + list = community_list_lookup (bgp_clist, com, COMMUNITY_LIST_MASTER); + if (list == NULL) + { + vty_out (vty, "%% %s is not a valid community-list name%s", com, + VTY_NEWLINE); + return CMD_WARNING; + } + + return bgp_show (vty, NULL, afi, safi, + (exact ? bgp_show_type_community_list_exact : + bgp_show_type_community_list), list); +} + +DEFUN (show_ip_bgp_community_list, + show_ip_bgp_community_list_cmd, + "show ip bgp community-list (<1-500>|WORD)", + SHOW_STR + IP_STR + BGP_STR + "Display routes matching the community-list\n" + "community-list number\n" + "community-list name\n") +{ + return bgp_show_community_list (vty, argv[0], 0, AFI_IP, SAFI_UNICAST); +} + +DEFUN (show_ip_bgp_ipv4_community_list, + show_ip_bgp_ipv4_community_list_cmd, + "show ip bgp ipv4 (unicast|multicast) community-list (<1-500>|WORD)", + SHOW_STR + IP_STR + BGP_STR + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Display routes matching the community-list\n" + "community-list number\n" + "community-list name\n") +{ + if (strncmp (argv[0], "m", 1) == 0) + return bgp_show_community_list (vty, argv[1], 0, AFI_IP, SAFI_MULTICAST); + + return bgp_show_community_list (vty, argv[1], 0, AFI_IP, SAFI_UNICAST); +} + +DEFUN (show_ip_bgp_community_list_exact, + show_ip_bgp_community_list_exact_cmd, + "show ip bgp community-list (<1-500>|WORD) exact-match", + SHOW_STR + IP_STR + BGP_STR + "Display routes matching the community-list\n" + "community-list number\n" + "community-list name\n" + "Exact match of the communities\n") +{ + return bgp_show_community_list (vty, argv[0], 1, AFI_IP, SAFI_UNICAST); +} + +DEFUN (show_ip_bgp_ipv4_community_list_exact, + show_ip_bgp_ipv4_community_list_exact_cmd, + "show ip bgp ipv4 (unicast|multicast) community-list (<1-500>|WORD) exact-match", + SHOW_STR + IP_STR + BGP_STR + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Display routes matching the community-list\n" + "community-list number\n" + "community-list name\n" + "Exact match of the communities\n") +{ + if (strncmp (argv[0], "m", 1) == 0) + return bgp_show_community_list (vty, argv[1], 1, AFI_IP, SAFI_MULTICAST); + + return bgp_show_community_list (vty, argv[1], 1, AFI_IP, SAFI_UNICAST); +} + +DEFUN (show_bgp_community_list, + show_bgp_community_list_cmd, + "show bgp community-list (<1-500>|WORD)", + SHOW_STR + BGP_STR + "Display routes matching the community-list\n" + "community-list number\n" + "community-list name\n") +{ + return bgp_show_community_list (vty, argv[0], 0, AFI_IP6, SAFI_UNICAST); +} + +ALIAS (show_bgp_community_list, + show_bgp_ipv6_community_list_cmd, + "show bgp ipv6 community-list (<1-500>|WORD)", + SHOW_STR + BGP_STR + "Address family\n" + "Display routes matching the community-list\n" + "community-list number\n" + "community-list name\n") + +/* old command */ +DEFUN (show_ipv6_bgp_community_list, + show_ipv6_bgp_community_list_cmd, + "show ipv6 bgp community-list WORD", + SHOW_STR + IPV6_STR + BGP_STR + "Display routes matching the community-list\n" + "community-list name\n") +{ + return bgp_show_community_list (vty, argv[0], 0, AFI_IP6, SAFI_UNICAST); +} + +/* old command */ +DEFUN (show_ipv6_mbgp_community_list, + show_ipv6_mbgp_community_list_cmd, + "show ipv6 mbgp community-list WORD", + SHOW_STR + IPV6_STR + MBGP_STR + "Display routes matching the community-list\n" + "community-list name\n") +{ + return bgp_show_community_list (vty, argv[0], 0, AFI_IP6, SAFI_MULTICAST); +} + +DEFUN (show_bgp_community_list_exact, + show_bgp_community_list_exact_cmd, + "show bgp community-list (<1-500>|WORD) exact-match", + SHOW_STR + BGP_STR + "Display routes matching the community-list\n" + "community-list number\n" + "community-list name\n" + "Exact match of the communities\n") +{ + return bgp_show_community_list (vty, argv[0], 1, AFI_IP6, SAFI_UNICAST); +} + +ALIAS (show_bgp_community_list_exact, + show_bgp_ipv6_community_list_exact_cmd, + "show bgp ipv6 community-list (<1-500>|WORD) exact-match", + SHOW_STR + BGP_STR + "Address family\n" + "Display routes matching the community-list\n" + "community-list number\n" + "community-list name\n" + "Exact match of the communities\n") + +/* old command */ +DEFUN (show_ipv6_bgp_community_list_exact, + show_ipv6_bgp_community_list_exact_cmd, + "show ipv6 bgp community-list WORD exact-match", + SHOW_STR + IPV6_STR + BGP_STR + "Display routes matching the community-list\n" + "community-list name\n" + "Exact match of the communities\n") +{ + return bgp_show_community_list (vty, argv[0], 1, AFI_IP6, SAFI_UNICAST); +} + +/* old command */ +DEFUN (show_ipv6_mbgp_community_list_exact, + show_ipv6_mbgp_community_list_exact_cmd, + "show ipv6 mbgp community-list WORD exact-match", + SHOW_STR + IPV6_STR + MBGP_STR + "Display routes matching the community-list\n" + "community-list name\n" + "Exact match of the communities\n") +{ + return bgp_show_community_list (vty, argv[0], 1, AFI_IP6, SAFI_MULTICAST); +} + +DEFUN (show_bgp_ipv4_community_list, + show_bgp_ipv4_community_list_cmd, + "show bgp ipv4 community-list (<1-500>|WORD)", + SHOW_STR + BGP_STR + IP_STR + "Display routes matching the community-list\n" + "community-list number\n" + "community-list name\n") +{ + return bgp_show_community_list (vty, argv[0], 0, AFI_IP, SAFI_UNICAST); +} + +DEFUN (show_bgp_ipv4_safi_community_list, + show_bgp_ipv4_safi_community_list_cmd, + "show bgp ipv4 (unicast|multicast) community-list (<1-500>|WORD)", + SHOW_STR + BGP_STR + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Display routes matching the community-list\n" + "community-list number\n" + "community-list name\n") +{ + if (strncmp (argv[0], "m", 1) == 0) + return bgp_show_community_list (vty, argv[1], 0, AFI_IP, SAFI_MULTICAST); + + return bgp_show_community_list (vty, argv[1], 0, AFI_IP, SAFI_UNICAST); +} + +DEFUN (show_bgp_ipv4_community_list_exact, + show_bgp_ipv4_community_list_exact_cmd, + "show bgp ipv4 community-list (<1-500>|WORD) exact-match", + SHOW_STR + BGP_STR + IP_STR + "Display routes matching the community-list\n" + "community-list number\n" + "community-list name\n" + "Exact match of the communities\n") +{ + return bgp_show_community_list (vty, argv[0], 1, AFI_IP, SAFI_UNICAST); +} + +DEFUN (show_bgp_ipv4_safi_community_list_exact, + show_bgp_ipv4_safi_community_list_exact_cmd, + "show bgp ipv4 (unicast|multicast) community-list (<1-500>|WORD) exact-match", + SHOW_STR + BGP_STR + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Display routes matching the community-list\n" + "community-list number\n" + "community-list name\n" + "Exact match of the communities\n") +{ + if (strncmp (argv[0], "m", 1) == 0) + return bgp_show_community_list (vty, argv[1], 1, AFI_IP, SAFI_MULTICAST); + + return bgp_show_community_list (vty, argv[1], 1, AFI_IP, SAFI_UNICAST); +} + +DEFUN (show_bgp_ipv6_safi_community_list, + show_bgp_ipv6_safi_community_list_cmd, + "show bgp ipv6 (encap|multicast|unicast|vpn) community-list (<1-500>|WORD)", + SHOW_STR + BGP_STR + "Address family\n" + "Address family modifier\n" + "Address family modifier\n" + "Address family modifier\n" + "Address family modifier\n" + "Display routes matching the community-list\n" + "community-list number\n" + "community-list name\n") +{ + safi_t safi; + + if (bgp_parse_safi(argv[0], &safi)) { + vty_out (vty, "Error: Bad SAFI: %s%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + return bgp_show_community_list (vty, argv[1], 0, AFI_IP6, safi); +} + +DEFUN (show_bgp_ipv6_safi_community_list_exact, + show_bgp_ipv6_safi_community_list_exact_cmd, + "show bgp ipv6 (encap|multicast|unicast|vpn) community-list (<1-500>|WORD) exact-match", + SHOW_STR + BGP_STR + "Address family\n" + "Address family modifier\n" + "Address family modifier\n" + "Address family modifier\n" + "Address family modifier\n" + "Display routes matching the community-list\n" + "community-list number\n" + "community-list name\n" + "Exact match of the communities\n") +{ + safi_t safi; + + if (bgp_parse_safi(argv[0], &safi)) { + vty_out (vty, "Error: Bad SAFI: %s%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + return bgp_show_community_list (vty, argv[1], 1, AFI_IP6, safi); +} + +/* + * Large Community show commands. + */ + +DEFUN (show_bgp_afi_lcommunity_all, + show_bgp_afi_lcommunity_all_cmd, + "show bgp (ipv4|ipv6) large-community", + SHOW_STR + BGP_STR + "Address family\n" + "Address family\n" + "Display routes matching the large-communities\n") +{ + afi_t afi; + safi_t safi = SAFI_UNICAST; + + if (bgp_parse_afi(argv[0], &afi)) { + vty_out (vty, "Error: Bad AFI: %s%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + return bgp_show (vty, NULL, afi, safi, bgp_show_type_lcommunity_all, NULL); +} + +static int +bgp_show_lcommunity (struct vty *vty, const char *view_name, int argc, + const char **argv, afi_t afi, safi_t safi) +{ + struct lcommunity *lcom; + struct buffer *b; + struct bgp *bgp; + int i; + char *str; + int first = 0; + + /* BGP structure lookup */ + if (view_name) + { + bgp = bgp_lookup_by_name (view_name); + if (bgp == NULL) + { + vty_out (vty, "Can't find BGP view %s%s", view_name, VTY_NEWLINE); + return CMD_WARNING; + } + } + else + { + bgp = bgp_get_default (); + if (bgp == NULL) + { + vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + } + + b = buffer_new (1024); + for (i = 0; i < argc; i++) + { + if (first) + buffer_putc (b, ' '); + else + { + if ((strcmp (argv[i], "unicast") == 0) || (strcmp (argv[i], "multicast") == 0)) + continue; + first = 1; + } + + buffer_putstr (b, argv[i]); + } + buffer_putc (b, '\0'); + + str = buffer_getstr (b); + buffer_free (b); + + lcom = lcommunity_str2com (str); + XFREE (MTYPE_TMP, str); + if (! lcom) + { + vty_out (vty, "%% Large-community malformed: %s", VTY_NEWLINE); + return CMD_WARNING; + } + + return bgp_show (vty, bgp, afi, safi, bgp_show_type_lcommunity, lcom); +} + +DEFUN (show_ip_bgp_lcommunity, + show_ip_bgp_lcommunity_cmd, + "show ip bgp large-community (AA:BB:CC)", + SHOW_STR + IP_STR + BGP_STR + "Display routes matching the large-communities\n" + "large-community number\n") +{ + return bgp_show_lcommunity (vty, NULL, argc, argv, AFI_IP, SAFI_UNICAST); +} + +ALIAS (show_ip_bgp_lcommunity, + show_ip_bgp_lcommunity2_cmd, + "show ip bgp large-community (AA:BB:CC) (AA:BB:CC)", + SHOW_STR + IP_STR + BGP_STR + "Display routes matching the large-communities\n" + "large-community number\n" + "large-community number\n") + +ALIAS (show_ip_bgp_lcommunity, + show_ip_bgp_lcommunity3_cmd, + "show ip bgp large-community (AA:BB:CC) (AA:BB:CC) (AA:BB:CC)", + SHOW_STR + IP_STR + BGP_STR + "Display routes matching the large-communities\n" + "largecommunity number\n" + "largecommunity number\n" + "largecommunity number\n") + +ALIAS (show_ip_bgp_lcommunity, + show_ip_bgp_lcommunity4_cmd, + "show ip bgp large-community (AA:BB:CC) (AA:BB:CC) (AA:BB:CC) (AA:BB:CC)", + SHOW_STR + IP_STR + BGP_STR + "Display routes matching the large-communities\n" + "large-community number\n" + "large-community number\n" + "large-community number\n" + "large-community number\n") + +DEFUN (show_ip_bgp_ipv4_lcommunity, + show_ip_bgp_ipv4_lcommunity_cmd, + "show ip bgp ipv4 (unicast|multicast) large-community (AA:BB:CC)", + SHOW_STR + IP_STR + BGP_STR + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Display routes matching the large-communities\n" + "large-community number\n") +{ + if (strncmp (argv[0], "m", 1) == 0) + return bgp_show_lcommunity (vty, NULL, argc, argv, AFI_IP, SAFI_MULTICAST); + + return bgp_show_lcommunity (vty, NULL, argc, argv, AFI_IP, SAFI_UNICAST); +} + +ALIAS (show_ip_bgp_ipv4_lcommunity, + show_ip_bgp_ipv4_lcommunity2_cmd, + "show ip bgp ipv4 (unicast|multicast) large-community (AA:BB:CC) (AA:BB:CC)", + SHOW_STR + IP_STR + BGP_STR + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Display routes matching the large-communities\n" + "large-community number\n" + "large-community number\n") + +ALIAS (show_ip_bgp_ipv4_lcommunity, + show_ip_bgp_ipv4_lcommunity3_cmd, + "show ip bgp ipv4 (unicast|multicast) large-community (AA:BB:CC) (AA:BB:CC) (AA:BB:CC)", + SHOW_STR + IP_STR + BGP_STR + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Display routes matching the large-communities\n" + "large-community number\n" + "large-community number\n" + "large-community number\n") + +ALIAS (show_ip_bgp_ipv4_lcommunity, + show_ip_bgp_ipv4_lcommunity4_cmd, + "show ip bgp ipv4 (unicast|multicast) large-community (AA:BB:CC) (AA:BB:CC) (AA:BB:CC) (AA:BB:CC)", + SHOW_STR + IP_STR + BGP_STR + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Display routes matching the large-communities\n" + "large-community number\n" + "large-community number\n" + "large-community number\n" + "large-community number\n") + +DEFUN (show_bgp_lcommunity, + show_bgp_lcommunity_cmd, + "show bgp large-community (AA:BB:CC)", + SHOW_STR + BGP_STR + "Display routes matching the large-communities\n" + "large-community number\n") +{ + return bgp_show_lcommunity (vty, NULL, argc, argv, AFI_IP6, SAFI_UNICAST); +} + +ALIAS (show_bgp_lcommunity, + show_bgp_ipv6_lcommunity_cmd, + "show bgp ipv6 large-community (AA:BB:CC)", + SHOW_STR + BGP_STR + "Address family\n" + "Display routes matching the large-communities\n" + "large-community number\n") + +ALIAS (show_bgp_lcommunity, + show_bgp_lcommunity2_cmd, + "show bgp large-community (AA:BB:CC) (AA:BB:CC)", + SHOW_STR + BGP_STR + "Display routes matching the large-communities\n" + "large-community number\n" + "large-community number\n") + +ALIAS (show_bgp_lcommunity, + show_bgp_ipv6_lcommunity2_cmd, + "show bgp ipv6 large-community (AA:BB:CC) (AA:BB:CC)", + SHOW_STR + BGP_STR + "Address family\n" + "Display routes matching the large-communities\n" + "large-community number\n" + "large-community number\n") + +ALIAS (show_bgp_lcommunity, + show_bgp_lcommunity3_cmd, + "show bgp large-community (AA:BB:CC) (AA:BB:CC) (AA:BB:CC)", + SHOW_STR + BGP_STR + "Display routes matching the large-communities\n" + "large-community number\n" + "large-community number\n" + "large-community number\n") + +ALIAS (show_bgp_lcommunity, + show_bgp_ipv6_lcommunity3_cmd, + "show bgp ipv6 large-community (AA:BB:CC) (AA:BB:CC) (AA:BB:CC)", + SHOW_STR + BGP_STR + "Address family\n" + "Display routes matching the large-communities\n" + "large-community number\n" + "large-community number\n" + "large-community number\n") + +ALIAS (show_bgp_lcommunity, + show_bgp_lcommunity4_cmd, + "show bgp large-community (AA:BB:CC) (AA:BB:CC) (AA:BB:CC) (AA:BB:CC)", + SHOW_STR + BGP_STR + "Display routes matching the large-communities\n" + "large-community number\n" + "large-community number\n" + "large-community number\n" + "large-community number\n") + +ALIAS (show_bgp_lcommunity, + show_bgp_ipv6_lcommunity4_cmd, + "show bgp ipv6 large-community (AA:BB:CC) (AA:BB:CC) (AA:BB:CC) (AA:BB:CC)", + SHOW_STR + BGP_STR + "Address family\n" + "Display routes matching the large-communities\n" + "large-community number\n" + "large-community number\n" + "large-community number\n" + "large-community number\n") + +/* old command */ +DEFUN (show_ipv6_bgp_lcommunity, + show_ipv6_bgp_lcommunity_cmd, + "show ipv6 bgp large-community (AA:BB:CC)", + SHOW_STR + IPV6_STR + BGP_STR + "Display routes matching the large-communities\n" + "large-community number\n") +{ + return bgp_show_lcommunity (vty, NULL, argc, argv, AFI_IP6, SAFI_UNICAST); +} + +/* old command */ +ALIAS (show_ipv6_bgp_lcommunity, + show_ipv6_bgp_lcommunity2_cmd, + "show ipv6 bgp large-community (AA:BB:CC) (AA:BB:CC)", + SHOW_STR + IPV6_STR + BGP_STR + "Display routes matching the large-communities\n" + "large-community number\n" + "large-community number\n") + +/* old command */ +ALIAS (show_ipv6_bgp_lcommunity, + show_ipv6_bgp_lcommunity3_cmd, + "show ipv6 bgp large-community (AA:BB:CC) (AA:BB:CC) (AA:BB:CC)", + SHOW_STR + IPV6_STR + BGP_STR + "Display routes matching the large-communities\n" + "large-community number\n" + "large-community number\n" + "large-community number\n") + +/* old command */ +ALIAS (show_ipv6_bgp_lcommunity, + show_ipv6_bgp_lcommunity4_cmd, + "show ipv6 bgp large-community (AA:BB:CC) (AA:BB:CC) (AA:BB:CC) (AA:BB:CC)", + SHOW_STR + IPV6_STR + BGP_STR + "Display routes matching the large-communities\n" + "large-community number\n" + "large-community number\n" + "large-community number\n" + "large-community number\n") + +/* old command */ +DEFUN (show_ipv6_mbgp_lcommunity, + show_ipv6_mbgp_lcommunity_cmd, + "show ipv6 mbgp large-community (AA:BB:CC)", + SHOW_STR + IPV6_STR + MBGP_STR + "Display routes matching the large-communities\n" + "large-community number\n") +{ + return bgp_show_lcommunity (vty, NULL, argc, argv, AFI_IP6, SAFI_MULTICAST); +} + +/* old command */ +ALIAS (show_ipv6_mbgp_lcommunity, + show_ipv6_mbgp_lcommunity2_cmd, + "show ipv6 mbgp large-community (AA:BB:CC) (AA:BB:CC)", + SHOW_STR + IPV6_STR + MBGP_STR + "Display routes matching the large-communities\n" + "large-community number\n" + "large-community number\n") + +/* old command */ +ALIAS (show_ipv6_mbgp_lcommunity, + show_ipv6_mbgp_lcommunity3_cmd, + "show ipv6 mbgp large-community (AA:BB:CC) (AA:BB:CC) (AA:BB:CC)", + SHOW_STR + IPV6_STR + MBGP_STR + "Display routes matching the large-communities\n" + "large-community number\n" + "large-community number\n" + "large-community number\n") + +/* old command */ +ALIAS (show_ipv6_mbgp_lcommunity, + show_ipv6_mbgp_lcommunity4_cmd, + "show ipv6 mbgp laarge-community (AA:BB:CC) (AA:BB:CC) (AA:BB:CC) (AA:BB:CC)", + SHOW_STR + IPV6_STR + MBGP_STR + "Display routes matching the large-communities\n" + "large-community number\n" + "large-community number\n" + "large-community number\n" + "large-community number\n") + +DEFUN (show_bgp_ipv4_lcommunity, + show_bgp_ipv4_lcommunity_cmd, + "show bgp ipv4 large-community (AA:BB:CC)", + SHOW_STR + BGP_STR + IP_STR + "Display routes matching the large-communities\n" + "large-community number\n") +{ + return bgp_show_lcommunity (vty, NULL, argc, argv, AFI_IP, SAFI_UNICAST); +} + +ALIAS (show_bgp_ipv4_lcommunity, + show_bgp_ipv4_lcommunity2_cmd, + "show bgp ipv4 large-community (AA:BB:CC) (AA:BB:CC)", + SHOW_STR + BGP_STR + IP_STR + "Display routes matching the large-communities\n" + "large-community number\n" + "large-community number\n") + +ALIAS (show_bgp_ipv4_lcommunity, + show_bgp_ipv4_lcommunity3_cmd, + "show bgp ipv4 large-community (AA:BB:CC) (AA:BB:CC) (AA:BB:CC)", + SHOW_STR + BGP_STR + IP_STR + "Display routes matching the large-communities\n" + "large-community number\n" + "large-community number\n" + "large-community number\n") + +ALIAS (show_bgp_ipv4_lcommunity, + show_bgp_ipv4_lcommunity4_cmd, + "show bgp ipv4 large-community (AA:BB:CC) (AA:BB:CC) (AA:BB:CC) (AA:BB:CC)", + SHOW_STR + BGP_STR + IP_STR + "Display routes matching the large-communities\n" + "large-community number\n" + "large-community number\n" + "large-community number\n" + "large-community number\n") + +DEFUN (show_bgp_ipv4_safi_lcommunity, + show_bgp_ipv4_safi_lcommunity_cmd, + "show bgp ipv4 (unicast|multicast) large-community (AA:BB:CC)", + SHOW_STR + BGP_STR + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Display routes matching the large-communities\n" + "large-community number\n") +{ + if (strncmp (argv[0], "m", 1) == 0) + return bgp_show_lcommunity (vty, NULL, argc, argv, AFI_IP, SAFI_MULTICAST); + + return bgp_show_lcommunity (vty, NULL, argc, argv, AFI_IP, SAFI_UNICAST); +} + +ALIAS (show_bgp_ipv4_safi_lcommunity, + show_bgp_ipv4_safi_lcommunity2_cmd, + "show bgp ipv4 (unicast|multicast) large-community (AA:BB:CC) (AA:BB:CC)", + SHOW_STR + BGP_STR + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Display routes matching the large-communities\n" + "large-community number\n" + "large-community number\n") + +ALIAS (show_bgp_ipv4_safi_lcommunity, + show_bgp_ipv4_safi_lcommunity3_cmd, + "show bgp ipv4 (unicast|multicast) large-community (AA:BB:CC) (AA:BB:CC) (AA:BB:CC)", + SHOW_STR + BGP_STR + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Display routes matching the large-communities\n" + "large-community number\n" + "large-community number\n" + "large-community number\n") + +ALIAS (show_bgp_ipv4_safi_lcommunity, + show_bgp_ipv4_safi_lcommunity4_cmd, + "show bgp ipv4 (unicast|multicast) large-community (AA:BB:CC) (AA:BB:CC) (AA:BB:CC) (AA:BB:CC)", + SHOW_STR + BGP_STR + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Display routes matching the large-communities\n" + "large-community number\n" + "large-community number\n" + "large-community number\n" + "large-community number\n") + +DEFUN (show_bgp_view_afi_safi_lcommunity_all, + show_bgp_view_afi_safi_lcommunity_all_cmd, + "show bgp view WORD (ipv4|ipv6) (unicast|multicast) large-community", + SHOW_STR + BGP_STR + "BGP view\n" + "View name\n" + "Address family\n" + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Display routes matching the large-communities\n") +{ + int afi; + int safi; + struct bgp *bgp; + + /* BGP structure lookup. */ + bgp = bgp_lookup_by_name (argv[0]); + if (bgp == NULL) + { + vty_out (vty, "Can't find BGP view %s%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + + afi = (strncmp (argv[1], "ipv6", 4) == 0) ? AFI_IP6 : AFI_IP; + safi = (strncmp (argv[2], "m", 1) == 0) ? SAFI_MULTICAST : SAFI_UNICAST; + return bgp_show (vty, bgp, afi, safi, bgp_show_type_lcommunity_all, NULL); +} + +DEFUN (show_bgp_view_afi_safi_lcommunity, + show_bgp_view_afi_safi_lcommunity_cmd, + "show bgp view WORD (ipv4|ipv6) (unicast|multicast) large-community (AA:BB:CC)", + SHOW_STR + BGP_STR + "BGP view\n" + "View name\n" + "Address family\n" + "Address family\n" + "Address family modifier\n" + "Address family modifier\n" + "Display routes matching the large-communities\n" + "large-community number\n") +{ + int afi; + int safi; + + afi = (strncmp (argv[1], "ipv6", 4) == 0) ? AFI_IP6 : AFI_IP; + safi = (strncmp (argv[2], "m", 1) == 0) ? SAFI_MULTICAST : SAFI_UNICAST; + return bgp_show_lcommunity (vty, argv[0], argc-3, &argv[3], afi, safi); +} + +ALIAS (show_bgp_view_afi_safi_lcommunity, + show_bgp_view_afi_safi_lcommunity2_cmd, + "show bgp view WORD (ipv4|ipv6) (unicast|multicast) large-community (AA:BB:CC) (AA:BB:CC)", + SHOW_STR + BGP_STR + "BGP view\n" + "View name\n" + "Address family\n" + "Address family\n" + "Address family modifier\n" + "Address family modifier\n" + "Display routes matching the large-communities\n" + "large-community number\n" + "large-community number\n") + +ALIAS (show_bgp_view_afi_safi_lcommunity, + show_bgp_view_afi_safi_lcommunity3_cmd, + "show bgp view WORD (ipv4|ipv6) (unicast|multicast) large-community (AA:BB:CC) (AA:BB:CC) (AA:BB:CC)", + SHOW_STR + BGP_STR + "BGP view\n" + "View name\n" + "Address family\n" + "Address family\n" + "Address family modifier\n" + "Address family modifier\n" + "Display routes matching the large-communities\n" + "large-community number\n" + "large-community number\n" + "large-community number\n") + +ALIAS (show_bgp_view_afi_safi_lcommunity, + show_bgp_view_afi_safi_lcommunity4_cmd, + "show bgp view WORD (ipv4|ipv6) (unicast|multicast) large-community (AA:BB:CC) (AA:BB:CC) (AA:BB:CC) (AA:BB:CC)", + SHOW_STR + BGP_STR + "BGP view\n" + "View name\n" + "Address family\n" + "Address family\n" + "Address family modifier\n" + "Address family modifier\n" + "Display routes matching the large-communities\n" + "large-community number\n" + "large-community number\n" + "large-community number\n" + "large-community number\n") + +DEFUN (show_bgp_ipv6_safi_lcommunity, + show_bgp_ipv6_safi_lcommunity_cmd, + "show bgp ipv6 (encap|multicast|unicast|vpn) large-community AA:BB:CC", + SHOW_STR + BGP_STR + "Address family\n" + "Address family modifier\n" + "Address family modifier\n" + "Address family modifier\n" + "Address family modifier\n" + "Display routes matching the large-communities\n" + "large-community number\n") +{ + safi_t safi; + + if (bgp_parse_safi(argv[0], &safi)) { + vty_out (vty, "Error: Bad SAFI: %s%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + return bgp_show_lcommunity (vty, NULL, argc-1, argv+1, AFI_IP6, safi); +} + +ALIAS (show_bgp_ipv6_safi_lcommunity, + show_bgp_ipv6_safi_lcommunity2_cmd, + "show bgp ipv6 (encap|multicast|unicast|vpn) large-community (AA:BB:CC) (AA:BB:CC)", + SHOW_STR + BGP_STR + "Address family\n" + "Address family modifier\n" + "Address family modifier\n" + "Address family modifier\n" + "Address family modifier\n" + "Display routes matching the large-communities\n" + "large-community number\n" + "large-community number\n") + +ALIAS (show_bgp_ipv6_safi_lcommunity, + show_bgp_ipv6_safi_lcommunity3_cmd, + "show bgp ipv6 (encap|multicast|unicast|vpn) large-community (AA:BB:CC) (AA:BB:CC) (AA:BB:CC)", + SHOW_STR + BGP_STR + "Address family\n" + "Address family modifier\n" + "Address family modifier\n" + "Address family modifier\n" + "Address family modifier\n" + "Display routes matching the large-communities\n" + "large-community number\n" + "large-community number\n" + "large-community number\n") + +ALIAS (show_bgp_ipv6_safi_lcommunity, + show_bgp_ipv6_safi_lcommunity4_cmd, + "show bgp ipv6 (encap|multicast|unicast|vpn) large-community (AA:BB:CC) (AA:BB:CC) (AA:BB:CC) (AA:BB:CC)", + SHOW_STR + BGP_STR + "Address family\n" + "Address family modifier\n" + "Address family modifier\n" + "Address family modifier\n" + "Address family modifier\n" + "Display routes matching the large-communities\n" + "large-community number\n" + "large-community number\n" + "large-community number\n" + "large-community number\n") + +static int +bgp_show_lcommunity_list (struct vty *vty, const char *lcom, + afi_t afi, safi_t safi) +{ + struct community_list *list; + + list = community_list_lookup (bgp_clist, lcom, LARGE_COMMUNITY_LIST_MASTER); + if (list == NULL) + { + vty_out (vty, "%% %s is not a valid large-community-list name%s", lcom, + VTY_NEWLINE); + return CMD_WARNING; + } + + return bgp_show (vty, NULL, afi, safi, bgp_show_type_lcommunity_list, list); +} + +DEFUN (show_ip_bgp_lcommunity_list, + show_ip_bgp_lcommunity_list_cmd, + "show ip bgp large-community-list (<1-500>|WORD)", + SHOW_STR + IP_STR + BGP_STR + "Display routes matching the large-community-list\n" + "large-community-list number\n" + "large-community-list name\n") +{ + return bgp_show_lcommunity_list (vty, argv[0], AFI_IP, SAFI_UNICAST); +} + +DEFUN (show_ip_bgp_ipv4_lcommunity_list, + show_ip_bgp_ipv4_lcommunity_list_cmd, + "show ip bgp ipv4 (unicast|multicast) large-community-list (<1-500>|WORD)", + SHOW_STR + IP_STR + BGP_STR + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Display routes matching the large-community-list\n" + "large-community-list number\n" + "large-community-list name\n") +{ + if (strncmp (argv[0], "m", 1) == 0) + return bgp_show_lcommunity_list (vty, argv[1], AFI_IP, SAFI_MULTICAST); + + return bgp_show_lcommunity_list (vty, argv[1], AFI_IP, SAFI_UNICAST); +} + +DEFUN (show_bgp_lcommunity_list, + show_bgp_lcommunity_list_cmd, + "show bgp large-community-list (<1-500>|WORD)", + SHOW_STR + BGP_STR + "Display routes matching the large-community-list\n" + "large-community-list number\n" + "large-community-list name\n") +{ + return bgp_show_lcommunity_list (vty, argv[0], AFI_IP6, SAFI_UNICAST); +} + +ALIAS (show_bgp_lcommunity_list, + show_bgp_ipv6_lcommunity_list_cmd, + "show bgp ipv6 large-community-list (<1-500>|WORD)", + SHOW_STR + BGP_STR + "Address family\n" + "Display routes matching the large-community-list\n" + "large-community-list number\n" + "large-community-list name\n") + +/* old command */ +DEFUN (show_ipv6_bgp_lcommunity_list, + show_ipv6_bgp_lcommunity_list_cmd, + "show ipv6 bgp large-community-list WORD", + SHOW_STR + IPV6_STR + BGP_STR + "Display routes matching the large-community-list\n" + "large-community-list name\n") +{ + return bgp_show_lcommunity_list (vty, argv[0], AFI_IP6, SAFI_UNICAST); +} + +/* old command */ +DEFUN (show_ipv6_mbgp_lcommunity_list, + show_ipv6_mbgp_lcommunity_list_cmd, + "show ipv6 mbgp large-community-list WORD", + SHOW_STR + IPV6_STR + MBGP_STR + "Display routes matching the large-community-list\n" + "large-community-list name\n") +{ + return bgp_show_lcommunity_list (vty, argv[0], AFI_IP6, SAFI_MULTICAST); +} + +DEFUN (show_bgp_ipv4_lcommunity_list, + show_bgp_ipv4_lcommunity_list_cmd, + "show bgp ipv4 large-community-list (<1-500>|WORD)", + SHOW_STR + BGP_STR + IP_STR + "Display routes matching the large-community-list\n" + "large-community-list number\n" + "large-community-list name\n") +{ + return bgp_show_lcommunity_list (vty, argv[0], AFI_IP, SAFI_UNICAST); +} + +DEFUN (show_bgp_ipv4_safi_lcommunity_list, + show_bgp_ipv4_safi_lcommunity_list_cmd, + "show bgp ipv4 (unicast|multicast) large-community-list (<1-500>|WORD)", + SHOW_STR + BGP_STR + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Display routes matching the large-community-list\n" + "large-community-list number\n" + "large-community-list name\n") +{ + if (strncmp (argv[0], "m", 1) == 0) + return bgp_show_lcommunity_list (vty, argv[1], AFI_IP, SAFI_MULTICAST); + + return bgp_show_lcommunity_list (vty, argv[1], AFI_IP, SAFI_UNICAST); +} + +DEFUN (show_bgp_ipv6_safi_lcommunity_list, + show_bgp_ipv6_safi_lcommunity_list_cmd, + "show bgp ipv6 (encap|multicast|unicast|vpn) large-community-list (<1-500>|WORD)", + SHOW_STR + BGP_STR + "Address family\n" + "Address family modifier\n" + "Address family modifier\n" + "Address family modifier\n" + "Address family modifier\n" + "Display routes matching the large-community-list\n" + "large-community-list number\n" + "large-community-list name\n") +{ + safi_t safi; + + if (bgp_parse_safi(argv[0], &safi)) { + vty_out (vty, "Error: Bad SAFI: %s%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + return bgp_show_lcommunity_list (vty, argv[1], AFI_IP6, safi); +} + +static int +bgp_show_prefix_longer (struct vty *vty, const char *prefix, afi_t afi, + safi_t safi, enum bgp_show_type type) +{ + int ret; + struct prefix *p; + + p = prefix_new(); + + ret = str2prefix (prefix, p); + if (! ret) + { + vty_out (vty, "%% Malformed Prefix%s", VTY_NEWLINE); + return CMD_WARNING; + } + + ret = bgp_show (vty, NULL, afi, safi, type, p); + prefix_free(p); + return ret; +} + +DEFUN (show_ip_bgp_prefix_longer, + show_ip_bgp_prefix_longer_cmd, + "show ip bgp A.B.C.D/M longer-prefixes", + SHOW_STR + IP_STR + BGP_STR + "IP prefix /, e.g., 35.0.0.0/8\n" + "Display route and more specific routes\n") +{ + return bgp_show_prefix_longer (vty, argv[0], AFI_IP, SAFI_UNICAST, + bgp_show_type_prefix_longer); +} + +DEFUN (show_ip_bgp_flap_prefix_longer, + show_ip_bgp_flap_prefix_longer_cmd, + "show ip bgp flap-statistics A.B.C.D/M longer-prefixes", + SHOW_STR + IP_STR + BGP_STR + "Display flap statistics of routes\n" + "IP prefix /, e.g., 35.0.0.0/8\n" + "Display route and more specific routes\n") +{ + return bgp_show_prefix_longer (vty, argv[0], AFI_IP, SAFI_UNICAST, + bgp_show_type_flap_prefix_longer); +} + +ALIAS (show_ip_bgp_flap_prefix_longer, + show_ip_bgp_damp_flap_prefix_longer_cmd, + "show ip bgp dampening flap-statistics A.B.C.D/M longer-prefixes", + SHOW_STR + IP_STR + BGP_STR + "Display detailed information about dampening\n" + "Display flap statistics of routes\n" + "IP prefix /, e.g., 35.0.0.0/8\n" + "Display route and more specific routes\n") + +DEFUN (show_ip_bgp_ipv4_prefix_longer, + show_ip_bgp_ipv4_prefix_longer_cmd, + "show ip bgp ipv4 (unicast|multicast) A.B.C.D/M longer-prefixes", + SHOW_STR + IP_STR + BGP_STR + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + "IP prefix /, e.g., 35.0.0.0/8\n" + "Display route and more specific routes\n") +{ + if (strncmp (argv[0], "m", 1) == 0) + return bgp_show_prefix_longer (vty, argv[1], AFI_IP, SAFI_MULTICAST, + bgp_show_type_prefix_longer); + + return bgp_show_prefix_longer (vty, argv[1], AFI_IP, SAFI_UNICAST, + bgp_show_type_prefix_longer); +} + +DEFUN (show_ip_bgp_flap_address, + show_ip_bgp_flap_address_cmd, + "show ip bgp flap-statistics A.B.C.D", + SHOW_STR + IP_STR + BGP_STR + "Display flap statistics of routes\n" + "Network in the BGP routing table to display\n") +{ + return bgp_show_prefix_longer (vty, argv[0], AFI_IP, SAFI_UNICAST, + bgp_show_type_flap_address); +} + +ALIAS (show_ip_bgp_flap_address, + show_ip_bgp_damp_flap_address_cmd, + "show ip bgp dampening flap-statistics A.B.C.D", + SHOW_STR + IP_STR + BGP_STR + "Display detailed information about dampening\n" + "Display flap statistics of routes\n" + "Network in the BGP routing table to display\n") + +DEFUN (show_ip_bgp_flap_prefix, + show_ip_bgp_flap_prefix_cmd, + "show ip bgp flap-statistics A.B.C.D/M", + SHOW_STR + IP_STR + BGP_STR + "Display flap statistics of routes\n" + "IP prefix /, e.g., 35.0.0.0/8\n") +{ + return bgp_show_prefix_longer (vty, argv[0], AFI_IP, SAFI_UNICAST, + bgp_show_type_flap_prefix); +} + +ALIAS (show_ip_bgp_flap_prefix, + show_ip_bgp_damp_flap_prefix_cmd, + "show ip bgp dampening flap-statistics A.B.C.D/M", + SHOW_STR + IP_STR + BGP_STR + "Display detailed information about dampening\n" + "Display flap statistics of routes\n" + "IP prefix /, e.g., 35.0.0.0/8\n") + +DEFUN (show_bgp_prefix_longer, + show_bgp_prefix_longer_cmd, + "show bgp X:X::X:X/M longer-prefixes", + SHOW_STR + BGP_STR + "IPv6 prefix /\n" + "Display route and more specific routes\n") +{ + return bgp_show_prefix_longer (vty, argv[0], AFI_IP6, SAFI_UNICAST, + bgp_show_type_prefix_longer); +} + +/* old command */ +DEFUN (show_ipv6_bgp_prefix_longer, + show_ipv6_bgp_prefix_longer_cmd, + "show ipv6 bgp X:X::X:X/M longer-prefixes", + SHOW_STR + IPV6_STR + BGP_STR + "IPv6 prefix /, e.g., 3ffe::/16\n" + "Display route and more specific routes\n") +{ + return bgp_show_prefix_longer (vty, argv[0], AFI_IP6, SAFI_UNICAST, + bgp_show_type_prefix_longer); +} + +/* old command */ +DEFUN (show_ipv6_mbgp_prefix_longer, + show_ipv6_mbgp_prefix_longer_cmd, + "show ipv6 mbgp X:X::X:X/M longer-prefixes", + SHOW_STR + IPV6_STR + MBGP_STR + "IPv6 prefix /, e.g., 3ffe::/16\n" + "Display route and more specific routes\n") +{ + return bgp_show_prefix_longer (vty, argv[0], AFI_IP6, SAFI_MULTICAST, + bgp_show_type_prefix_longer); +} + +DEFUN (show_bgp_ipv4_prefix_longer, + show_bgp_ipv4_prefix_longer_cmd, + "show bgp ipv4 A.B.C.D/M longer-prefixes", + SHOW_STR + BGP_STR + IP_STR + "IP prefix /, e.g., 35.0.0.0/8\n" + "Display route and more specific routes\n") +{ + return bgp_show_prefix_longer (vty, argv[0], AFI_IP, SAFI_UNICAST, + bgp_show_type_prefix_longer); +} + +DEFUN (show_bgp_ipv4_safi_flap_prefix_longer, + show_bgp_ipv4_safi_flap_prefix_longer_cmd, + "show bgp ipv4 (encap|multicast|unicast|vpn) flap-statistics A.B.C.D/M longer-prefixes", + SHOW_STR + BGP_STR + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Display flap statistics of routes\n" + "IP prefix /, e.g., 35.0.0.0/8\n" + "Display route and more specific routes\n") +{ + safi_t safi; + + if (bgp_parse_safi(argv[0], &safi)) { + vty_out (vty, "Error: Bad SAFI: %s%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + return bgp_show_prefix_longer (vty, argv[1], AFI_IP, safi, + bgp_show_type_flap_prefix_longer); +} + +ALIAS (show_bgp_ipv4_safi_flap_prefix_longer, + show_bgp_ipv4_safi_damp_flap_prefix_longer_cmd, + "show bgp ipv4 (encap|multicast|unicast|vpn) dampening flap-statistics A.B.C.D/M longer-prefixes", + SHOW_STR + BGP_STR + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Display detailed information about dampening\n" + "Display flap statistics of routes\n" + "IP prefix /, e.g., 35.0.0.0/8\n" + "Display route and more specific routes\n") + +DEFUN (show_bgp_ipv6_safi_flap_prefix_longer, + show_bgp_ipv6_safi_flap_prefix_longer_cmd, + "show bgp ipv6 (encap|multicast|unicast|vpn) flap-statistics X:X::X:X/M longer-prefixes", + SHOW_STR + BGP_STR + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Display flap statistics of routes\n" + "IP prefix /, e.g., 35.0.0.0/8\n" + "Display route and more specific routes\n") +{ + safi_t safi; + + if (bgp_parse_safi(argv[0], &safi)) { + vty_out (vty, "Error: Bad SAFI: %s%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + return bgp_show_prefix_longer (vty, argv[1], AFI_IP6, safi, + bgp_show_type_flap_prefix_longer); +} +ALIAS (show_bgp_ipv6_safi_flap_prefix_longer, + show_bgp_ipv6_safi_damp_flap_prefix_longer_cmd, + "show bgp ipv6 (encap|multicast|unicast|vpn) dampening flap-statistics X:X::X:X/M longer-prefixes", + SHOW_STR + BGP_STR + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Display detailed information about dampening\n" + "Display flap statistics of routes\n" + "IP prefix /, e.g., 35.0.0.0/8\n" + "Display route and more specific routes\n") + +DEFUN (show_bgp_ipv4_safi_prefix_longer, + show_bgp_ipv4_safi_prefix_longer_cmd, + "show bgp ipv4 (encap|multicast|unicast|vpn) A.B.C.D/M longer-prefixes", + SHOW_STR + BGP_STR + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Address Family modifier\n" + "IP prefix /, e.g., 35.0.0.0/8\n" + "Display route and more specific routes\n") +{ + safi_t safi; + + if (bgp_parse_safi(argv[0], &safi)) { + vty_out (vty, "Error: Bad SAFI: %s%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + + return bgp_show_prefix_longer (vty, argv[1], AFI_IP, safi, + bgp_show_type_prefix_longer); +} + +DEFUN (show_bgp_ipv6_safi_prefix_longer, + show_bgp_ipv6_safi_prefix_longer_cmd, + "show bgp ipv6 (encap|multicast|unicast|vpn) X:X::X:X/M longer-prefixes", + SHOW_STR + BGP_STR + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Address Family modifier\n" + "IP prefix /, e.g., 35.0.0.0/8\n" + "Display route and more specific routes\n") +{ + safi_t safi; + + if (bgp_parse_safi(argv[0], &safi)) { + vty_out (vty, "Error: Bad SAFI: %s%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + + return bgp_show_prefix_longer (vty, argv[1], AFI_IP6, safi, + bgp_show_type_prefix_longer); +} + +DEFUN (show_bgp_ipv4_safi_flap_address, + show_bgp_ipv4_safi_flap_address_cmd, + "show bgp ipv4 (encap|multicast|unicast|vpn) flap-statistics A.B.C.D", + SHOW_STR + BGP_STR + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Display flap statistics of routes\n" + "Network in the BGP routing table to display\n") +{ + safi_t safi; + + if (bgp_parse_safi(argv[0], &safi)) { + vty_out (vty, "Error: Bad SAFI: %s%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + return bgp_show_prefix_longer (vty, argv[1], AFI_IP, safi, + bgp_show_type_flap_address); +} +ALIAS (show_bgp_ipv4_safi_flap_address, + show_bgp_ipv4_safi_damp_flap_address_cmd, + "show bgp ipv4 (encap|multicast|unicast|vpn) dampening flap-statistics A.B.C.D", + SHOW_STR + BGP_STR + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Display detailed information about dampening\n" + "Display flap statistics of routes\n" + "Network in the BGP routing table to display\n") + +DEFUN (show_bgp_ipv6_flap_address, + show_bgp_ipv6_flap_address_cmd, + "show bgp ipv6 (encap|multicast|unicast|vpn) flap-statistics A.B.C.D", + SHOW_STR + BGP_STR + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Display flap statistics of routes\n" + "Network in the BGP routing table to display\n") +{ + safi_t safi; + + if (bgp_parse_safi(argv[0], &safi)) { + vty_out (vty, "Error: Bad SAFI: %s%s", argv[1], VTY_NEWLINE); + return CMD_WARNING; + } + return bgp_show_prefix_longer (vty, argv[1], AFI_IP, safi, + bgp_show_type_flap_address); +} +ALIAS (show_bgp_ipv6_flap_address, + show_bgp_ipv6_damp_flap_address_cmd, + "show bgp ipv6 (encap|multicast|unicast|vpn) dampening flap-statistics A.B.C.D", + SHOW_STR + BGP_STR + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Display detailed information about dampening\n" + "Display flap statistics of routes\n" + "Network in the BGP routing table to display\n") + +DEFUN (show_bgp_ipv4_safi_flap_prefix, + show_bgp_ipv4_safi_flap_prefix_cmd, + "show bgp ipv4 (encap|multicast|unicast|vpn) flap-statistics A.B.C.D/M", + SHOW_STR + BGP_STR + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Display flap statistics of routes\n" + "IP prefix /, e.g., 35.0.0.0/8\n") +{ + safi_t safi; + + if (bgp_parse_safi(argv[0], &safi)) { + vty_out (vty, "Error: Bad SAFI: %s%s", argv[1], VTY_NEWLINE); + return CMD_WARNING; + } + return bgp_show_prefix_longer (vty, argv[0], AFI_IP, safi, + bgp_show_type_flap_prefix); +} + +ALIAS (show_bgp_ipv4_safi_flap_prefix, + show_bgp_ipv4_safi_damp_flap_prefix_cmd, + "show bgp ipv4 (encap|multicast|unicast|vpn) dampening flap-statistics A.B.C.D/M", + SHOW_STR + BGP_STR + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Display detailed information about dampening\n" + "Display flap statistics of routes\n" + "IP prefix /, e.g., 35.0.0.0/8\n") + +DEFUN (show_bgp_ipv6_safi_flap_prefix, + show_bgp_ipv6_safi_flap_prefix_cmd, + "show bgp ipv6 (encap|multicast|unicast|vpn) flap-statistics X:X::X:X/M", + SHOW_STR + BGP_STR + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Display flap statistics of routes\n" + "IP prefix /, e.g., 35.0.0.0/8\n") +{ + safi_t safi; + + if (bgp_parse_safi(argv[0], &safi)) { + vty_out (vty, "Error: Bad SAFI: %s%s", argv[1], VTY_NEWLINE); + return CMD_WARNING; + } + return bgp_show_prefix_longer (vty, argv[0], AFI_IP6, safi, + bgp_show_type_flap_prefix); +} + +ALIAS (show_bgp_ipv6_safi_flap_prefix, + show_bgp_ipv6_safi_damp_flap_prefix_cmd, + "show bgp ipv6 (encap|multicast|unicast|vpn) dampening flap-statistics X:X::X:X/M", + SHOW_STR + BGP_STR + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Display detailed information about dampening\n" + "Display flap statistics of routes\n" + "IP prefix /, e.g., 35.0.0.0/8\n") + +DEFUN (show_bgp_ipv6_prefix_longer, + show_bgp_ipv6_prefix_longer_cmd, + "show bgp ipv6 X:X::X:X/M longer-prefixes", + SHOW_STR + BGP_STR + "Address family\n" + "IPv6 prefix /\n" + "Display route and more specific routes\n") +{ + return bgp_show_prefix_longer (vty, argv[0], AFI_IP6, SAFI_UNICAST, + bgp_show_type_prefix_longer); +} + +static struct peer * +peer_lookup_in_view (struct vty *vty, const char *view_name, + const char *ip_str) +{ + int ret; + struct bgp *bgp; + struct peer *peer; + union sockunion su; + + /* BGP structure lookup. */ + if (view_name) + { + bgp = bgp_lookup_by_name (view_name); + if (! bgp) + { + vty_out (vty, "Can't find BGP view %s%s", view_name, VTY_NEWLINE); + return NULL; + } + } + else + { + bgp = bgp_get_default (); + if (! bgp) + { + vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE); + return NULL; + } + } + + /* Get peer sockunion. */ + ret = str2sockunion (ip_str, &su); + if (ret < 0) + { + vty_out (vty, "Malformed address: %s%s", ip_str, VTY_NEWLINE); + return NULL; + } + + /* Peer structure lookup. */ + peer = peer_lookup (bgp, &su); + if (! peer) + { + vty_out (vty, "No such neighbor%s", VTY_NEWLINE); + return NULL; + } + + return peer; +} + +enum bgp_stats +{ + BGP_STATS_MAXBITLEN = 0, + BGP_STATS_RIB, + BGP_STATS_PREFIXES, + BGP_STATS_TOTPLEN, + BGP_STATS_UNAGGREGATEABLE, + BGP_STATS_MAX_AGGREGATEABLE, + BGP_STATS_AGGREGATES, + BGP_STATS_SPACE, + BGP_STATS_ASPATH_COUNT, + BGP_STATS_ASPATH_MAXHOPS, + BGP_STATS_ASPATH_TOTHOPS, + BGP_STATS_ASPATH_MAXSIZE, + BGP_STATS_ASPATH_TOTSIZE, + BGP_STATS_ASN_HIGHEST, + BGP_STATS_MAX, +}; + +static const char *table_stats_strs[] = +{ + [BGP_STATS_PREFIXES] = "Total Prefixes", + [BGP_STATS_TOTPLEN] = "Average prefix length", + [BGP_STATS_RIB] = "Total Advertisements", + [BGP_STATS_UNAGGREGATEABLE] = "Unaggregateable prefixes", + [BGP_STATS_MAX_AGGREGATEABLE] = "Maximum aggregateable prefixes", + [BGP_STATS_AGGREGATES] = "BGP Aggregate advertisements", + [BGP_STATS_SPACE] = "Address space advertised", + [BGP_STATS_ASPATH_COUNT] = "Advertisements with paths", + [BGP_STATS_ASPATH_MAXHOPS] = "Longest AS-Path (hops)", + [BGP_STATS_ASPATH_MAXSIZE] = "Largest AS-Path (bytes)", + [BGP_STATS_ASPATH_TOTHOPS] = "Average AS-Path length (hops)", + [BGP_STATS_ASPATH_TOTSIZE] = "Average AS-Path size (bytes)", + [BGP_STATS_ASN_HIGHEST] = "Highest public ASN", + [BGP_STATS_MAX] = NULL, +}; + +struct bgp_table_stats +{ + struct bgp_table *table; + unsigned long long counts[BGP_STATS_MAX]; +}; + +#if 0 +#define TALLY_SIGFIG 100000 +static unsigned long +ravg_tally (unsigned long count, unsigned long oldavg, unsigned long newval) +{ + unsigned long newtot = (count-1) * oldavg + (newval * TALLY_SIGFIG); + unsigned long res = (newtot * TALLY_SIGFIG) / count; + unsigned long ret = newtot / count; + + if ((res % TALLY_SIGFIG) > (TALLY_SIGFIG/2)) + return ret + 1; + else + return ret; +} +#endif + +static int +bgp_table_stats_walker (struct thread *t) +{ + struct bgp_node *rn; + struct bgp_node *top; + struct bgp_table_stats *ts = THREAD_ARG (t); + unsigned int space = 0; + + if (!(top = bgp_table_top (ts->table))) + return 0; + + switch (top->p.family) + { + case AF_INET: + space = IPV4_MAX_BITLEN; + break; + case AF_INET6: + space = IPV6_MAX_BITLEN; + break; + } + + ts->counts[BGP_STATS_MAXBITLEN] = space; + + for (rn = top; rn; rn = bgp_route_next (rn)) + { + struct bgp_info *ri; + struct bgp_node *prn = bgp_node_parent_nolock (rn); + unsigned int rinum = 0; + + if (rn == top) + continue; + + if (!rn->info) + continue; + + ts->counts[BGP_STATS_PREFIXES]++; + ts->counts[BGP_STATS_TOTPLEN] += rn->p.prefixlen; + +#if 0 + ts->counts[BGP_STATS_AVGPLEN] + = ravg_tally (ts->counts[BGP_STATS_PREFIXES], + ts->counts[BGP_STATS_AVGPLEN], + rn->p.prefixlen); +#endif + + /* check if the prefix is included by any other announcements */ + while (prn && !prn->info) + prn = bgp_node_parent_nolock (prn); + + if (prn == NULL || prn == top) + { + ts->counts[BGP_STATS_UNAGGREGATEABLE]++; + /* announced address space */ + if (space) + ts->counts[BGP_STATS_SPACE] += 1 << (space - rn->p.prefixlen); + } + else if (prn->info) + ts->counts[BGP_STATS_MAX_AGGREGATEABLE]++; + + for (ri = rn->info; ri; ri = ri->next) + { + rinum++; + ts->counts[BGP_STATS_RIB]++; + + if (ri->attr && + (CHECK_FLAG (ri->attr->flag, + ATTR_FLAG_BIT (BGP_ATTR_ATOMIC_AGGREGATE)))) + ts->counts[BGP_STATS_AGGREGATES]++; + + /* as-path stats */ + if (ri->attr && ri->attr->aspath) + { + unsigned int hops = aspath_count_hops (ri->attr->aspath); + unsigned int size = aspath_size (ri->attr->aspath); + as_t highest = aspath_highest (ri->attr->aspath); + + ts->counts[BGP_STATS_ASPATH_COUNT]++; + + if (hops > ts->counts[BGP_STATS_ASPATH_MAXHOPS]) + ts->counts[BGP_STATS_ASPATH_MAXHOPS] = hops; + + if (size > ts->counts[BGP_STATS_ASPATH_MAXSIZE]) + ts->counts[BGP_STATS_ASPATH_MAXSIZE] = size; + + ts->counts[BGP_STATS_ASPATH_TOTHOPS] += hops; + ts->counts[BGP_STATS_ASPATH_TOTSIZE] += size; +#if 0 + ts->counts[BGP_STATS_ASPATH_AVGHOPS] + = ravg_tally (ts->counts[BGP_STATS_ASPATH_COUNT], + ts->counts[BGP_STATS_ASPATH_AVGHOPS], + hops); + ts->counts[BGP_STATS_ASPATH_AVGSIZE] + = ravg_tally (ts->counts[BGP_STATS_ASPATH_COUNT], + ts->counts[BGP_STATS_ASPATH_AVGSIZE], + size); +#endif + if (highest > ts->counts[BGP_STATS_ASN_HIGHEST]) + ts->counts[BGP_STATS_ASN_HIGHEST] = highest; + } + } + } + return 0; +} + +static int +bgp_table_stats (struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi) +{ + struct bgp_table_stats ts; + unsigned int i; + + if (!bgp->rib[afi][safi]) + { + vty_out (vty, "%% No RIB exists for the specified AFI(%d)/SAFI(%d) %s", + afi, safi, VTY_NEWLINE); + return CMD_WARNING; + } + + memset (&ts, 0, sizeof (ts)); + ts.table = bgp->rib[afi][safi]; + thread_execute (bm->master, bgp_table_stats_walker, &ts, 0); + + vty_out (vty, "BGP %s RIB statistics%s%s", + afi_safi_print (afi, safi), VTY_NEWLINE, VTY_NEWLINE); + + for (i = 0; i < BGP_STATS_MAX; i++) + { + if (!table_stats_strs[i]) + continue; + + switch (i) + { +#if 0 + case BGP_STATS_ASPATH_AVGHOPS: + case BGP_STATS_ASPATH_AVGSIZE: + case BGP_STATS_AVGPLEN: + vty_out (vty, "%-30s: ", table_stats_strs[i]); + vty_out (vty, "%12.2f", + (float)ts.counts[i] / (float)TALLY_SIGFIG); + break; +#endif + case BGP_STATS_ASPATH_TOTHOPS: + case BGP_STATS_ASPATH_TOTSIZE: + vty_out (vty, "%-30s: ", table_stats_strs[i]); + vty_out (vty, "%12.2f", + ts.counts[i] ? + (float)ts.counts[i] / + (float)ts.counts[BGP_STATS_ASPATH_COUNT] + : 0); + break; + case BGP_STATS_TOTPLEN: + vty_out (vty, "%-30s: ", table_stats_strs[i]); + vty_out (vty, "%12.2f", + ts.counts[i] ? + (float)ts.counts[i] / + (float)ts.counts[BGP_STATS_PREFIXES] + : 0); + break; + case BGP_STATS_SPACE: + vty_out (vty, "%-30s: ", table_stats_strs[i]); + vty_out (vty, "%12llu%s", ts.counts[i], VTY_NEWLINE); + if (ts.counts[BGP_STATS_MAXBITLEN] < 9) + break; + vty_out (vty, "%30s: ", "%% announced "); + vty_out (vty, "%12.2f%s", + 100 * (float)ts.counts[BGP_STATS_SPACE] / + (float)((uint64_t)1UL << ts.counts[BGP_STATS_MAXBITLEN]), + VTY_NEWLINE); + vty_out (vty, "%30s: ", "/8 equivalent "); + vty_out (vty, "%12.2f%s", + (float)ts.counts[BGP_STATS_SPACE] / + (float)(1UL << (ts.counts[BGP_STATS_MAXBITLEN] - 8)), + VTY_NEWLINE); + if (ts.counts[BGP_STATS_MAXBITLEN] < 25) + break; + vty_out (vty, "%30s: ", "/24 equivalent "); + vty_out (vty, "%12.2f", + (float)ts.counts[BGP_STATS_SPACE] / + (float)(1UL << (ts.counts[BGP_STATS_MAXBITLEN] - 24))); + break; + default: + vty_out (vty, "%-30s: ", table_stats_strs[i]); + vty_out (vty, "%12llu", ts.counts[i]); + } + + vty_out (vty, "%s", VTY_NEWLINE); + } + return CMD_SUCCESS; +} + +static int +bgp_table_stats_vty (struct vty *vty, const char *name, + const char *afi_str, const char *safi_str) +{ + struct bgp *bgp; + afi_t afi; + safi_t safi; + + if (name) + bgp = bgp_lookup_by_name (name); + else + bgp = bgp_get_default (); + + if (!bgp) + { + vty_out (vty, "%% No such BGP instance exists%s", VTY_NEWLINE); + return CMD_WARNING; + } + if (strncmp (afi_str, "ipv", 3) == 0) + { + if (strncmp (afi_str, "ipv4", 4) == 0) + afi = AFI_IP; + else if (strncmp (afi_str, "ipv6", 4) == 0) + afi = AFI_IP6; + else + { + vty_out (vty, "%% Invalid address family %s%s", + afi_str, VTY_NEWLINE); + return CMD_WARNING; + } + switch (safi_str[0]) { + case 'm': + safi = SAFI_MULTICAST; + break; + case 'u': + safi = SAFI_UNICAST; + break; + case 'v': + safi = SAFI_MPLS_VPN; + break; + case 'e': + safi = SAFI_ENCAP; + break; + default: + vty_out (vty, "%% Invalid subsequent address family %s%s", + safi_str, VTY_NEWLINE); + return CMD_WARNING; + } + } + else + { + vty_out (vty, "%% Invalid address family \"%s\"%s", + afi_str, VTY_NEWLINE); + return CMD_WARNING; + } + + return bgp_table_stats (vty, bgp, afi, safi); +} + +DEFUN (show_bgp_statistics, + show_bgp_statistics_cmd, + "show bgp (ipv4|ipv6) (encap|multicast|unicast|vpn) statistics", + SHOW_STR + BGP_STR + "Address family\n" + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Address Family modifier\n" + "BGP RIB advertisement statistics\n") +{ + return bgp_table_stats_vty (vty, NULL, argv[0], argv[1]); +} + +ALIAS (show_bgp_statistics, + show_bgp_statistics_vpnv4_cmd, + "show bgp (ipv4) (vpnv4) statistics", + SHOW_STR + BGP_STR + "Address family\n" + "Address Family modifier\n" + "BGP RIB advertisement statistics\n") + +DEFUN (show_bgp_statistics_view, + show_bgp_statistics_view_cmd, + "show bgp view WORD (ipv4|ipv6) (encap|multicast|unicast|vpn) statistics", + SHOW_STR + BGP_STR + "BGP view\n" + "Address family\n" + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Address Family modifier\n" + "BGP RIB advertisement statistics\n") +{ + return bgp_table_stats_vty (vty, NULL, argv[0], argv[1]); +} + +ALIAS (show_bgp_statistics_view, + show_bgp_statistics_view_vpnv4_cmd, + "show bgp view WORD (ipv4) (vpnv4) statistics", + SHOW_STR + BGP_STR + "BGP view\n" + "Address family\n" + "Address Family modifier\n" + "BGP RIB advertisement statistics\n") + +enum bgp_pcounts +{ + PCOUNT_ADJ_IN = 0, + PCOUNT_DAMPED, + PCOUNT_REMOVED, + PCOUNT_HISTORY, + PCOUNT_STALE, + PCOUNT_VALID, + PCOUNT_ALL, + PCOUNT_COUNTED, + PCOUNT_PFCNT, /* the figure we display to users */ + PCOUNT_MAX, +}; + +static const char *pcount_strs[] = +{ + [PCOUNT_ADJ_IN] = "Adj-in", + [PCOUNT_DAMPED] = "Damped", + [PCOUNT_REMOVED] = "Removed", + [PCOUNT_HISTORY] = "History", + [PCOUNT_STALE] = "Stale", + [PCOUNT_VALID] = "Valid", + [PCOUNT_ALL] = "All RIB", + [PCOUNT_COUNTED] = "PfxCt counted", + [PCOUNT_PFCNT] = "Useable", + [PCOUNT_MAX] = NULL, +}; + +struct peer_pcounts +{ + unsigned int count[PCOUNT_MAX]; + const struct peer *peer; + const struct bgp_table *table; +}; + +static int +bgp_peer_count_walker (struct thread *t) +{ + struct bgp_node *rn; + struct peer_pcounts *pc = THREAD_ARG (t); + const struct peer *peer = pc->peer; + + for (rn = bgp_table_top (pc->table); rn; rn = bgp_route_next (rn)) + { + struct bgp_adj_in *ain; + struct bgp_info *ri; + + for (ain = rn->adj_in; ain; ain = ain->next) + if (ain->peer == peer) + pc->count[PCOUNT_ADJ_IN]++; + + for (ri = rn->info; ri; ri = ri->next) + { + char buf[SU_ADDRSTRLEN]; + + if (ri->peer != peer) + continue; + + pc->count[PCOUNT_ALL]++; + + if (CHECK_FLAG (ri->flags, BGP_INFO_DAMPED)) + pc->count[PCOUNT_DAMPED]++; + if (CHECK_FLAG (ri->flags, BGP_INFO_HISTORY)) + pc->count[PCOUNT_HISTORY]++; + if (CHECK_FLAG (ri->flags, BGP_INFO_REMOVED)) + pc->count[PCOUNT_REMOVED]++; + if (CHECK_FLAG (ri->flags, BGP_INFO_STALE)) + pc->count[PCOUNT_STALE]++; + if (CHECK_FLAG (ri->flags, BGP_INFO_VALID)) + pc->count[PCOUNT_VALID]++; + if (!CHECK_FLAG (ri->flags, BGP_INFO_UNUSEABLE)) + pc->count[PCOUNT_PFCNT]++; + + if (CHECK_FLAG (ri->flags, BGP_INFO_COUNTED)) + { + pc->count[PCOUNT_COUNTED]++; + if (CHECK_FLAG (ri->flags, BGP_INFO_UNUSEABLE)) + plog_warn (peer->log, + "%s [pcount] %s/%d is counted but flags 0x%x", + peer->host, + inet_ntop(rn->p.family, &rn->p.u.prefix, + buf, SU_ADDRSTRLEN), + rn->p.prefixlen, + ri->flags); + } + else + { + if (!CHECK_FLAG (ri->flags, BGP_INFO_UNUSEABLE)) + plog_warn (peer->log, + "%s [pcount] %s/%d not counted but flags 0x%x", + peer->host, + inet_ntop(rn->p.family, &rn->p.u.prefix, + buf, SU_ADDRSTRLEN), + rn->p.prefixlen, + ri->flags); + } + } + } + return 0; +} + +static int +bgp_peer_counts (struct vty *vty, struct peer *peer, afi_t afi, safi_t safi) +{ + struct peer_pcounts pcounts = { .peer = peer }; + unsigned int i; + + if (!peer || !peer->bgp || !peer->afc[afi][safi] + || !peer->bgp->rib[afi][safi]) + { + vty_out (vty, "%% No such neighbor or address family%s", VTY_NEWLINE); + return CMD_WARNING; + } + + memset (&pcounts, 0, sizeof(pcounts)); + pcounts.peer = peer; + pcounts.table = peer->bgp->rib[afi][safi]; + + /* in-place call via thread subsystem so as to record execution time + * stats for the thread-walk (i.e. ensure this can't be blamed on + * on just vty_read()). + */ + thread_execute (bm->master, bgp_peer_count_walker, &pcounts, 0); + + vty_out (vty, "Prefix counts for %s, %s%s", + peer->host, afi_safi_print (afi, safi), VTY_NEWLINE); + vty_out (vty, "PfxCt: %ld%s", peer->pcount[afi][safi], VTY_NEWLINE); + vty_out (vty, "%sCounts from RIB table walk:%s%s", + VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE); + + for (i = 0; i < PCOUNT_MAX; i++) + vty_out (vty, "%20s: %-10d%s", + pcount_strs[i], pcounts.count[i], VTY_NEWLINE); + + if (pcounts.count[PCOUNT_PFCNT] != peer->pcount[afi][safi]) + { + vty_out (vty, "%s [pcount] PfxCt drift!%s", + peer->host, VTY_NEWLINE); + vty_out (vty, "Please report this bug, with the above command output%s", + VTY_NEWLINE); + } + + return CMD_SUCCESS; +} + +DEFUN (show_ip_bgp_neighbor_prefix_counts, + show_ip_bgp_neighbor_prefix_counts_cmd, + "show ip bgp neighbors (A.B.C.D|X:X::X:X) prefix-counts", + SHOW_STR + IP_STR + BGP_STR + "Detailed information on TCP and BGP neighbor connections\n" + "Neighbor to display information about\n" + "Neighbor to display information about\n" + "Display detailed prefix count information\n") +{ + struct peer *peer; + + peer = peer_lookup_in_view (vty, NULL, argv[0]); + if (! peer) + return CMD_WARNING; + + return bgp_peer_counts (vty, peer, AFI_IP, SAFI_UNICAST); +} + +DEFUN (show_bgp_ipv6_neighbor_prefix_counts, + show_bgp_ipv6_neighbor_prefix_counts_cmd, + "show bgp ipv6 neighbors (A.B.C.D|X:X::X:X) prefix-counts", + SHOW_STR + BGP_STR + "Address family\n" + "Detailed information on TCP and BGP neighbor connections\n" + "Neighbor to display information about\n" + "Neighbor to display information about\n" + "Display detailed prefix count information\n") +{ + struct peer *peer; + + peer = peer_lookup_in_view (vty, NULL, argv[0]); + if (! peer) + return CMD_WARNING; + + return bgp_peer_counts (vty, peer, AFI_IP6, SAFI_UNICAST); +} + +DEFUN (show_ip_bgp_ipv4_neighbor_prefix_counts, + show_ip_bgp_ipv4_neighbor_prefix_counts_cmd, + "show ip bgp ipv4 (unicast|multicast) neighbors (A.B.C.D|X:X::X:X) prefix-counts", + SHOW_STR + IP_STR + BGP_STR + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Detailed information on TCP and BGP neighbor connections\n" + "Neighbor to display information about\n" + "Neighbor to display information about\n" + "Display detailed prefix count information\n") +{ + struct peer *peer; + + peer = peer_lookup_in_view (vty, NULL, argv[1]); + if (! peer) + return CMD_WARNING; + + if (strncmp (argv[0], "m", 1) == 0) + return bgp_peer_counts (vty, peer, AFI_IP, SAFI_MULTICAST); - /* Peer structure lookup. */ - peer = peer_lookup (bgp, &su); + return bgp_peer_counts (vty, peer, AFI_IP, SAFI_UNICAST); +} + +DEFUN (show_ip_bgp_vpnv4_neighbor_prefix_counts, + show_ip_bgp_vpnv4_neighbor_prefix_counts_cmd, + "show ip bgp vpnv4 all neighbors (A.B.C.D|X:X::X:X) prefix-counts", + SHOW_STR + IP_STR + BGP_STR + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Detailed information on TCP and BGP neighbor connections\n" + "Neighbor to display information about\n" + "Neighbor to display information about\n" + "Display detailed prefix count information\n") +{ + struct peer *peer; + + peer = peer_lookup_in_view (vty, NULL, argv[0]); if (! peer) - { - vty_out (vty, "No such neighbor%s", VTY_NEWLINE); - return NULL; - } + return CMD_WARNING; - return peer; + return bgp_peer_counts (vty, peer, AFI_IP, SAFI_MPLS_VPN); } - -enum bgp_stats -{ - BGP_STATS_MAXBITLEN = 0, - BGP_STATS_RIB, - BGP_STATS_PREFIXES, - BGP_STATS_TOTPLEN, - BGP_STATS_UNAGGREGATEABLE, - BGP_STATS_MAX_AGGREGATEABLE, - BGP_STATS_AGGREGATES, - BGP_STATS_SPACE, - BGP_STATS_ASPATH_COUNT, - BGP_STATS_ASPATH_MAXHOPS, - BGP_STATS_ASPATH_TOTHOPS, - BGP_STATS_ASPATH_MAXSIZE, - BGP_STATS_ASPATH_TOTSIZE, - BGP_STATS_ASN_HIGHEST, - BGP_STATS_MAX, -}; -static const char *table_stats_strs[] = +DEFUN (show_bgp_ipv4_safi_neighbor_prefix_counts, + show_bgp_ipv4_safi_neighbor_prefix_counts_cmd, + "show bgp ipv4 (unicast|multicast) neighbors (A.B.C.D|X:X::X:X) prefix-counts", + SHOW_STR + BGP_STR + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Detailed information on TCP and BGP neighbor connections\n" + "Neighbor to display information about\n" + "Neighbor to display information about\n" + "Display detailed prefix count information\n") { - [BGP_STATS_PREFIXES] = "Total Prefixes", - [BGP_STATS_TOTPLEN] = "Average prefix length", - [BGP_STATS_RIB] = "Total Advertisements", - [BGP_STATS_UNAGGREGATEABLE] = "Unaggregateable prefixes", - [BGP_STATS_MAX_AGGREGATEABLE] = "Maximum aggregateable prefixes", - [BGP_STATS_AGGREGATES] = "BGP Aggregate advertisements", - [BGP_STATS_SPACE] = "Address space advertised", - [BGP_STATS_ASPATH_COUNT] = "Advertisements with paths", - [BGP_STATS_ASPATH_MAXHOPS] = "Longest AS-Path (hops)", - [BGP_STATS_ASPATH_MAXSIZE] = "Largest AS-Path (bytes)", - [BGP_STATS_ASPATH_TOTHOPS] = "Average AS-Path length (hops)", - [BGP_STATS_ASPATH_TOTSIZE] = "Average AS-Path size (bytes)", - [BGP_STATS_ASN_HIGHEST] = "Highest public ASN", - [BGP_STATS_MAX] = NULL, -}; + struct peer *peer; + safi_t safi; -struct bgp_table_stats + if (bgp_parse_safi(argv[0], &safi)) { + vty_out (vty, "Error: Bad SAFI: %s%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + + peer = peer_lookup_in_view (vty, NULL, argv[1]); + if (! peer) + return CMD_WARNING; + + return bgp_peer_counts (vty, peer, AFI_IP, safi); +} + +DEFUN (show_bgp_ipv6_safi_neighbor_prefix_counts, + show_bgp_ipv6_safi_neighbor_prefix_counts_cmd, + "show bgp ipv6 (unicast|multicast) neighbors (A.B.C.D|X:X::X:X) prefix-counts", + SHOW_STR + BGP_STR + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Detailed information on TCP and BGP neighbor connections\n" + "Neighbor to display information about\n" + "Neighbor to display information about\n" + "Display detailed prefix count information\n") { - struct bgp_table *table; - unsigned long long counts[BGP_STATS_MAX]; -}; + struct peer *peer; + safi_t safi; -#if 0 -#define TALLY_SIGFIG 100000 -static unsigned long -ravg_tally (unsigned long count, unsigned long oldavg, unsigned long newval) + if (bgp_parse_safi(argv[0], &safi)) { + vty_out (vty, "Error: Bad SAFI: %s%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + + peer = peer_lookup_in_view (vty, NULL, argv[1]); + if (! peer) + return CMD_WARNING; + + return bgp_peer_counts (vty, peer, AFI_IP6, safi); +} + +DEFUN (show_ip_bgp_encap_neighbor_prefix_counts, + show_ip_bgp_encap_neighbor_prefix_counts_cmd, + "show ip bgp encap all neighbors (A.B.C.D|X:X::X:X) prefix-counts", + SHOW_STR + IP_STR + BGP_STR + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Detailed information on TCP and BGP neighbor connections\n" + "Neighbor to display information about\n" + "Neighbor to display information about\n" + "Display detailed prefix count information\n") { - unsigned long newtot = (count-1) * oldavg + (newval * TALLY_SIGFIG); - unsigned long res = (newtot * TALLY_SIGFIG) / count; - unsigned long ret = newtot / count; + struct peer *peer; + + peer = peer_lookup_in_view (vty, NULL, argv[0]); + if (! peer) + return CMD_WARNING; - if ((res % TALLY_SIGFIG) > (TALLY_SIGFIG/2)) - return ret + 1; - else - return ret; + return bgp_peer_counts (vty, peer, AFI_IP, SAFI_ENCAP); } -#endif -static int -bgp_table_stats_walker (struct thread *t) + +static void +show_adj_route (struct vty *vty, struct peer *peer, afi_t afi, safi_t safi, + int in) { + struct bgp_table *table; + struct bgp_adj_in *ain; + struct bgp_adj_out *adj; + unsigned long output_count; struct bgp_node *rn; - struct bgp_node *top; - struct bgp_table_stats *ts = THREAD_ARG (t); - unsigned int space = 0; - - if (!(top = bgp_table_top (ts->table))) - return 0; + int header1 = 1; + struct bgp *bgp; + int header2 = 1; - switch (top->p.family) - { - case AF_INET: - space = IPV4_MAX_BITLEN; - break; - case AF_INET6: - space = IPV6_MAX_BITLEN; - break; - } - - ts->counts[BGP_STATS_MAXBITLEN] = space; + bgp = peer->bgp; - for (rn = top; rn; rn = bgp_route_next (rn)) - { - struct bgp_info *ri; - struct bgp_node *prn = rn->parent; - unsigned int rinum = 0; - - if (rn == top) - continue; - - if (!rn->info) - continue; - - ts->counts[BGP_STATS_PREFIXES]++; - ts->counts[BGP_STATS_TOTPLEN] += rn->p.prefixlen; + if (! bgp) + return; -#if 0 - ts->counts[BGP_STATS_AVGPLEN] - = ravg_tally (ts->counts[BGP_STATS_PREFIXES], - ts->counts[BGP_STATS_AVGPLEN], - rn->p.prefixlen); -#endif - - /* check if the prefix is included by any other announcements */ - while (prn && !prn->info) - prn = prn->parent; - - if (prn == NULL || prn == top) - { - ts->counts[BGP_STATS_UNAGGREGATEABLE]++; - /* announced address space */ - if (space) - ts->counts[BGP_STATS_SPACE] += 1 << (space - rn->p.prefixlen); - } - else if (prn->info) - ts->counts[BGP_STATS_MAX_AGGREGATEABLE]++; - - for (ri = rn->info; ri; ri = ri->next) - { - rinum++; - ts->counts[BGP_STATS_RIB]++; - - if (ri->attr && - (CHECK_FLAG (ri->attr->flag, - ATTR_FLAG_BIT (BGP_ATTR_ATOMIC_AGGREGATE)))) - ts->counts[BGP_STATS_AGGREGATES]++; - - /* as-path stats */ - if (ri->attr && ri->attr->aspath) - { - unsigned int hops = aspath_count_hops (ri->attr->aspath); - unsigned int size = aspath_size (ri->attr->aspath); - as_t highest = aspath_highest (ri->attr->aspath); - - ts->counts[BGP_STATS_ASPATH_COUNT]++; - - if (hops > ts->counts[BGP_STATS_ASPATH_MAXHOPS]) - ts->counts[BGP_STATS_ASPATH_MAXHOPS] = hops; - - if (size > ts->counts[BGP_STATS_ASPATH_MAXSIZE]) - ts->counts[BGP_STATS_ASPATH_MAXSIZE] = size; - - ts->counts[BGP_STATS_ASPATH_TOTHOPS] += hops; - ts->counts[BGP_STATS_ASPATH_TOTSIZE] += size; -#if 0 - ts->counts[BGP_STATS_ASPATH_AVGHOPS] - = ravg_tally (ts->counts[BGP_STATS_ASPATH_COUNT], - ts->counts[BGP_STATS_ASPATH_AVGHOPS], - hops); - ts->counts[BGP_STATS_ASPATH_AVGSIZE] - = ravg_tally (ts->counts[BGP_STATS_ASPATH_COUNT], - ts->counts[BGP_STATS_ASPATH_AVGSIZE], - size); -#endif - if (highest > ts->counts[BGP_STATS_ASN_HIGHEST]) - ts->counts[BGP_STATS_ASN_HIGHEST] = highest; - } - } - } - return 0; -} + table = bgp->rib[afi][safi]; -static int -bgp_table_stats (struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi) -{ - struct bgp_table_stats ts; - unsigned int i; - - if (!bgp->rib[afi][safi]) + output_count = 0; + + if (! in && CHECK_FLAG (peer->af_sflags[afi][safi], + PEER_STATUS_DEFAULT_ORIGINATE)) { - vty_out (vty, "%% No RIB exist for the AFI/SAFI%s", VTY_NEWLINE); - return CMD_WARNING; + vty_out (vty, "BGP table version is 0, local router ID is %s%s", inet_ntoa (bgp->router_id), VTY_NEWLINE); + vty_out (vty, BGP_SHOW_SCODE_HEADER, VTY_NEWLINE, VTY_NEWLINE); + vty_out (vty, BGP_SHOW_OCODE_HEADER, VTY_NEWLINE, VTY_NEWLINE); + + vty_out (vty, "Originating default network 0.0.0.0%s%s", + VTY_NEWLINE, VTY_NEWLINE); + header1 = 0; } - - memset (&ts, 0, sizeof (ts)); - ts.table = bgp->rib[afi][safi]; - thread_execute (bm->master, bgp_table_stats_walker, &ts, 0); - vty_out (vty, "BGP %s RIB statistics%s%s", - afi_safi_print (afi, safi), VTY_NEWLINE, VTY_NEWLINE); + for (rn = bgp_table_top (table); rn; rn = bgp_route_next (rn)) + if (in) + { + for (ain = rn->adj_in; ain; ain = ain->next) + if (ain->peer == peer) + { + if (header1) + { + vty_out (vty, "BGP table version is 0, local router ID is %s%s", inet_ntoa (bgp->router_id), VTY_NEWLINE); + vty_out (vty, BGP_SHOW_SCODE_HEADER, VTY_NEWLINE, VTY_NEWLINE); + vty_out (vty, BGP_SHOW_OCODE_HEADER, VTY_NEWLINE, VTY_NEWLINE); + header1 = 0; + } + if (header2) + { + vty_out (vty, BGP_SHOW_HEADER, VTY_NEWLINE); + header2 = 0; + } + if (ain->attr) + { + route_vty_out_tmp (vty, &rn->p, ain->attr, safi); + output_count++; + } + } + } + else + { + for (adj = rn->adj_out; adj; adj = adj->next) + if (adj->peer == peer) + { + if (header1) + { + vty_out (vty, "BGP table version is 0, local router ID is %s%s", inet_ntoa (bgp->router_id), VTY_NEWLINE); + vty_out (vty, BGP_SHOW_SCODE_HEADER, VTY_NEWLINE, VTY_NEWLINE); + vty_out (vty, BGP_SHOW_OCODE_HEADER, VTY_NEWLINE, VTY_NEWLINE); + header1 = 0; + } + if (header2) + { + vty_out (vty, BGP_SHOW_HEADER, VTY_NEWLINE); + header2 = 0; + } + if (adj->attr) + { + route_vty_out_tmp (vty, &rn->p, adj->attr, safi); + output_count++; + } + } + } - for (i = 0; i < BGP_STATS_MAX; i++) - { - if (!table_stats_strs[i]) - continue; - - switch (i) - { -#if 0 - case BGP_STATS_ASPATH_AVGHOPS: - case BGP_STATS_ASPATH_AVGSIZE: - case BGP_STATS_AVGPLEN: - vty_out (vty, "%-30s: ", table_stats_strs[i]); - vty_out (vty, "%12.2f", - (float)ts.counts[i] / (float)TALLY_SIGFIG); - break; -#endif - case BGP_STATS_ASPATH_TOTHOPS: - case BGP_STATS_ASPATH_TOTSIZE: - vty_out (vty, "%-30s: ", table_stats_strs[i]); - vty_out (vty, "%12.2f", - ts.counts[i] ? - (float)ts.counts[i] / - (float)ts.counts[BGP_STATS_ASPATH_COUNT] - : 0); - break; - case BGP_STATS_TOTPLEN: - vty_out (vty, "%-30s: ", table_stats_strs[i]); - vty_out (vty, "%12.2f", - ts.counts[i] ? - (float)ts.counts[i] / - (float)ts.counts[BGP_STATS_PREFIXES] - : 0); - break; - case BGP_STATS_SPACE: - vty_out (vty, "%-30s: ", table_stats_strs[i]); - vty_out (vty, "%12llu%s", ts.counts[i], VTY_NEWLINE); - if (ts.counts[BGP_STATS_MAXBITLEN] < 9) - break; - vty_out (vty, "%30s: ", "%% announced "); - vty_out (vty, "%12.2f%s", - 100 * (float)ts.counts[BGP_STATS_SPACE] / - (float)((uint64_t)1UL << ts.counts[BGP_STATS_MAXBITLEN]), - VTY_NEWLINE); - vty_out (vty, "%30s: ", "/8 equivalent "); - vty_out (vty, "%12.2f%s", - (float)ts.counts[BGP_STATS_SPACE] / - (float)(1UL << (ts.counts[BGP_STATS_MAXBITLEN] - 8)), - VTY_NEWLINE); - if (ts.counts[BGP_STATS_MAXBITLEN] < 25) - break; - vty_out (vty, "%30s: ", "/24 equivalent "); - vty_out (vty, "%12.2f", - (float)ts.counts[BGP_STATS_SPACE] / - (float)(1UL << (ts.counts[BGP_STATS_MAXBITLEN] - 24))); - break; - default: - vty_out (vty, "%-30s: ", table_stats_strs[i]); - vty_out (vty, "%12llu", ts.counts[i]); - } - - vty_out (vty, "%s", VTY_NEWLINE); - } - return CMD_SUCCESS; + if (output_count != 0) + vty_out (vty, "%sTotal number of prefixes %ld%s", + VTY_NEWLINE, output_count, VTY_NEWLINE); } static int -bgp_table_stats_vty (struct vty *vty, const char *name, - const char *afi_str, const char *safi_str) -{ - struct bgp *bgp; - afi_t afi; - safi_t safi; - - if (name) - bgp = bgp_lookup_by_name (name); - else - bgp = bgp_get_default (); - - if (!bgp) +peer_adj_routes (struct vty *vty, struct peer *peer, afi_t afi, safi_t safi, int in) +{ + if (! peer || ! peer->afc[afi][safi]) { - vty_out (vty, "%% No such BGP instance exist%s", VTY_NEWLINE); + vty_out (vty, "%% No such neighbor or address family%s", VTY_NEWLINE); return CMD_WARNING; } - if (strncmp (afi_str, "ipv", 3) == 0) - { - if (strncmp (afi_str, "ipv4", 4) == 0) - afi = AFI_IP; - else if (strncmp (afi_str, "ipv6", 4) == 0) - afi = AFI_IP6; - else - { - vty_out (vty, "%% Invalid address family %s%s", - afi_str, VTY_NEWLINE); - return CMD_WARNING; - } - if (strncmp (safi_str, "m", 1) == 0) - safi = SAFI_MULTICAST; - else if (strncmp (safi_str, "u", 1) == 0) - safi = SAFI_UNICAST; - else if (strncmp (safi_str, "vpnv4", 5) == 0 || strncmp (safi_str, "vpnv6", 5) == 0) - safi = SAFI_MPLS_LABELED_VPN; - else - { - vty_out (vty, "%% Invalid subsequent address family %s%s", - safi_str, VTY_NEWLINE); - return CMD_WARNING; - } - } - else + + if (in && ! CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_SOFT_RECONFIG)) { - vty_out (vty, "%% Invalid address family %s%s", - afi_str, VTY_NEWLINE); + vty_out (vty, "%% Inbound soft reconfiguration not enabled%s", + VTY_NEWLINE); return CMD_WARNING; } - return bgp_table_stats (vty, bgp, afi, safi); + show_adj_route (vty, peer, afi, safi, in); + + return CMD_SUCCESS; } -DEFUN (show_bgp_statistics, - show_bgp_statistics_cmd, - "show bgp (ipv4|ipv6) (unicast|multicast) statistics", +DEFUN (show_ip_bgp_view_neighbor_advertised_route, + show_ip_bgp_view_neighbor_advertised_route_cmd, + "show ip bgp view WORD neighbors (A.B.C.D|X:X::X:X) advertised-routes", SHOW_STR + IP_STR BGP_STR - "Address family\n" - "Address family\n" - "Address Family modifier\n" - "Address Family modifier\n" - "BGP RIB advertisement statistics\n") + "BGP view\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" + "Display the routes advertised to a BGP neighbor\n") { - return bgp_table_stats_vty (vty, NULL, argv[0], argv[1]); + struct peer *peer; + + if (argc == 2) + peer = peer_lookup_in_view (vty, argv[0], argv[1]); + else + peer = peer_lookup_in_view (vty, NULL, argv[0]); + + if (! peer) + return CMD_WARNING; + + return peer_adj_routes (vty, peer, AFI_IP, SAFI_UNICAST, 0); } -ALIAS (show_bgp_statistics, - show_bgp_statistics_vpnv4_cmd, - "show bgp (ipv4) (vpnv4) statistics", +ALIAS (show_ip_bgp_view_neighbor_advertised_route, + show_ip_bgp_neighbor_advertised_route_cmd, + "show ip bgp neighbors (A.B.C.D|X:X::X:X) advertised-routes", + SHOW_STR + IP_STR + BGP_STR + "Detailed information on TCP and BGP neighbor connections\n" + "Neighbor to display information about\n" + "Neighbor to display information about\n" + "Display the routes advertised to a BGP neighbor\n") + +DEFUN (show_ip_bgp_ipv4_neighbor_advertised_route, + show_ip_bgp_ipv4_neighbor_advertised_route_cmd, + "show ip bgp ipv4 (unicast|multicast) neighbors (A.B.C.D|X:X::X:X) advertised-routes", SHOW_STR + IP_STR BGP_STR "Address family\n" "Address Family modifier\n" - "BGP RIB advertisement statistics\n") + "Address Family modifier\n" + "Detailed information on TCP and BGP neighbor connections\n" + "Neighbor to display information about\n" + "Neighbor to display information about\n" + "Display the routes advertised to a BGP neighbor\n") +{ + struct peer *peer; -DEFUN (show_bgp_statistics_view, - show_bgp_statistics_view_cmd, - "show bgp view WORD (ipv4|ipv6) (unicast|multicast) statistics", + peer = peer_lookup_in_view (vty, NULL, argv[1]); + if (! peer) + return CMD_WARNING; + + if (strncmp (argv[0], "m", 1) == 0) + return peer_adj_routes (vty, peer, AFI_IP, SAFI_MULTICAST, 0); + + return peer_adj_routes (vty, peer, AFI_IP, SAFI_UNICAST, 0); +} + +DEFUN (show_bgp_view_neighbor_advertised_route, + show_bgp_view_neighbor_advertised_route_cmd, + "show bgp view WORD neighbors (A.B.C.D|X:X::X:X) advertised-routes", SHOW_STR BGP_STR "BGP view\n" - "Address family\n" - "Address family\n" - "Address Family modifier\n" - "Address Family modifier\n" - "BGP RIB advertisement statistics\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" + "Display the routes advertised to a BGP neighbor\n") { - return bgp_table_stats_vty (vty, NULL, argv[0], argv[1]); + struct peer *peer; + + if (argc == 2) + peer = peer_lookup_in_view (vty, argv[0], argv[1]); + else + peer = peer_lookup_in_view (vty, NULL, argv[0]); + + if (! peer) + return CMD_WARNING; + + return peer_adj_routes (vty, peer, AFI_IP6, SAFI_UNICAST, 0); } -ALIAS (show_bgp_statistics_view, - show_bgp_statistics_view_vpnv4_cmd, - "show bgp view WORD (ipv4) (vpnv4) statistics", +DEFUN (show_bgp_view_neighbor_received_routes, + show_bgp_view_neighbor_received_routes_cmd, + "show bgp view WORD neighbors (A.B.C.D|X:X::X:X) received-routes", SHOW_STR BGP_STR "BGP view\n" - "Address family\n" - "Address Family modifier\n" - "BGP RIB advertisement statistics\n") - -enum bgp_pcounts -{ - PCOUNT_ADJ_IN = 0, - PCOUNT_DAMPED, - PCOUNT_REMOVED, - PCOUNT_HISTORY, - PCOUNT_STALE, - PCOUNT_VALID, - PCOUNT_ALL, - PCOUNT_COUNTED, - PCOUNT_PFCNT, /* the figure we display to users */ - PCOUNT_MAX, -}; - -static const char *pcount_strs[] = + "View name\n" + "Detailed information on TCP and BGP neighbor connections\n" + "Neighbor to display information about\n" + "Neighbor to display information about\n" + "Display the received routes from neighbor\n") { - [PCOUNT_ADJ_IN] = "Adj-in", - [PCOUNT_DAMPED] = "Damped", - [PCOUNT_REMOVED] = "Removed", - [PCOUNT_HISTORY] = "History", - [PCOUNT_STALE] = "Stale", - [PCOUNT_VALID] = "Valid", - [PCOUNT_ALL] = "All RIB", - [PCOUNT_COUNTED] = "PfxCt counted", - [PCOUNT_PFCNT] = "Useable", - [PCOUNT_MAX] = NULL, -}; + struct peer *peer; -struct peer_pcounts -{ - unsigned int count[PCOUNT_MAX]; - const struct peer *peer; - const struct bgp_table *table; -}; + if (argc == 2) + peer = peer_lookup_in_view (vty, argv[0], argv[1]); + else + peer = peer_lookup_in_view (vty, NULL, argv[0]); -static int -bgp_peer_count_walker (struct thread *t) -{ - struct bgp_node *rn; - struct peer_pcounts *pc = THREAD_ARG (t); - const struct peer *peer = pc->peer; - - for (rn = bgp_table_top (pc->table); rn; rn = bgp_route_next (rn)) - { - struct bgp_adj_in *ain; - struct bgp_info *ri; - - for (ain = rn->adj_in; ain; ain = ain->next) - if (ain->peer == peer) - pc->count[PCOUNT_ADJ_IN]++; + if (! peer) + return CMD_WARNING; - for (ri = rn->info; ri; ri = ri->next) - { - char buf[SU_ADDRSTRLEN]; - - if (ri->peer != peer) - continue; - - pc->count[PCOUNT_ALL]++; - - if (CHECK_FLAG (ri->flags, BGP_INFO_DAMPED)) - pc->count[PCOUNT_DAMPED]++; - if (CHECK_FLAG (ri->flags, BGP_INFO_HISTORY)) - pc->count[PCOUNT_HISTORY]++; - if (CHECK_FLAG (ri->flags, BGP_INFO_REMOVED)) - pc->count[PCOUNT_REMOVED]++; - if (CHECK_FLAG (ri->flags, BGP_INFO_STALE)) - pc->count[PCOUNT_STALE]++; - if (CHECK_FLAG (ri->flags, BGP_INFO_VALID)) - pc->count[PCOUNT_VALID]++; - if (!CHECK_FLAG (ri->flags, BGP_INFO_UNUSEABLE)) - pc->count[PCOUNT_PFCNT]++; - - if (CHECK_FLAG (ri->flags, BGP_INFO_COUNTED)) - { - pc->count[PCOUNT_COUNTED]++; - if (CHECK_FLAG (ri->flags, BGP_INFO_UNUSEABLE)) - plog_warn (peer->log, - "%s [pcount] %s/%d is counted but flags 0x%x", - peer->host, - inet_ntop(rn->p.family, &rn->p.u.prefix, - buf, SU_ADDRSTRLEN), - rn->p.prefixlen, - ri->flags); - } - else - { - if (!CHECK_FLAG (ri->flags, BGP_INFO_UNUSEABLE)) - plog_warn (peer->log, - "%s [pcount] %s/%d not counted but flags 0x%x", - peer->host, - inet_ntop(rn->p.family, &rn->p.u.prefix, - buf, SU_ADDRSTRLEN), - rn->p.prefixlen, - ri->flags); - } - } - } - return 0; + return peer_adj_routes (vty, peer, AFI_IP6, SAFI_UNICAST, 1); } -static int -bgp_peer_counts (struct vty *vty, struct peer *peer, afi_t afi, safi_t safi) -{ - struct peer_pcounts pcounts = { .peer = peer }; - unsigned int i; - - if (!peer || !peer->bgp || !peer->afc[afi][safi] - || !peer->bgp->rib[afi][safi]) - { - vty_out (vty, "%% No such neighbor or address family%s", VTY_NEWLINE); - return CMD_WARNING; - } - - memset (&pcounts, 0, sizeof(pcounts)); - pcounts.peer = peer; - pcounts.table = peer->bgp->rib[afi][safi]; - - /* in-place call via thread subsystem so as to record execution time - * stats for the thread-walk (i.e. ensure this can't be blamed on - * on just vty_read()). - */ - thread_execute (bm->master, bgp_peer_count_walker, &pcounts, 0); +ALIAS (show_bgp_view_neighbor_advertised_route, + show_bgp_neighbor_advertised_route_cmd, + "show bgp neighbors (A.B.C.D|X:X::X:X) advertised-routes", + SHOW_STR + BGP_STR + "Detailed information on TCP and BGP neighbor connections\n" + "Neighbor to display information about\n" + "Neighbor to display information about\n" + "Display the routes advertised to a BGP neighbor\n") + +ALIAS (show_bgp_view_neighbor_advertised_route, + show_bgp_ipv6_neighbor_advertised_route_cmd, + "show bgp ipv6 neighbors (A.B.C.D|X:X::X:X) advertised-routes", + SHOW_STR + BGP_STR + "Address family\n" + "Detailed information on TCP and BGP neighbor connections\n" + "Neighbor to display information about\n" + "Neighbor to display information about\n" + "Display the routes advertised to a BGP neighbor\n") + +/* old command */ +ALIAS (show_bgp_view_neighbor_advertised_route, + ipv6_bgp_neighbor_advertised_route_cmd, + "show ipv6 bgp neighbors (A.B.C.D|X:X::X:X) advertised-routes", + SHOW_STR + IPV6_STR + BGP_STR + "Detailed information on TCP and BGP neighbor connections\n" + "Neighbor to display information about\n" + "Neighbor to display information about\n" + "Display the routes advertised to a BGP neighbor\n") - vty_out (vty, "Prefix counts for %s, %s%s", - peer->host, afi_safi_print (afi, safi), VTY_NEWLINE); - vty_out (vty, "PfxCt: %ld%s", peer->pcount[afi][safi], VTY_NEWLINE); - vty_out (vty, "%sCounts from RIB table walk:%s%s", - VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE); +/* old command */ +DEFUN (ipv6_mbgp_neighbor_advertised_route, + ipv6_mbgp_neighbor_advertised_route_cmd, + "show ipv6 mbgp neighbors (A.B.C.D|X:X::X:X) advertised-routes", + SHOW_STR + IPV6_STR + MBGP_STR + "Detailed information on TCP and BGP neighbor connections\n" + "Neighbor to display information about\n" + "Neighbor to display information about\n" + "Display the routes advertised to a BGP neighbor\n") +{ + struct peer *peer; - for (i = 0; i < PCOUNT_MAX; i++) - vty_out (vty, "%20s: %-10d%s", - pcount_strs[i], pcounts.count[i], VTY_NEWLINE); + peer = peer_lookup_in_view (vty, NULL, argv[0]); + if (! peer) + return CMD_WARNING; - if (pcounts.count[PCOUNT_PFCNT] != peer->pcount[afi][safi]) - { - vty_out (vty, "%s [pcount] PfxCt drift!%s", - peer->host, VTY_NEWLINE); - vty_out (vty, "Please report this bug, with the above command output%s", - VTY_NEWLINE); - } - - return CMD_SUCCESS; + return peer_adj_routes (vty, peer, AFI_IP6, SAFI_MULTICAST, 0); } -DEFUN (show_ip_bgp_neighbor_prefix_counts, - show_ip_bgp_neighbor_prefix_counts_cmd, - "show ip bgp neighbors (A.B.C.D|X:X::X:X) prefix-counts", +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", SHOW_STR IP_STR BGP_STR + "BGP view\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" - "Display detailed prefix count information\n") + "Display the received routes from neighbor\n") { struct peer *peer; - peer = peer_lookup_in_view (vty, NULL, argv[0]); - if (! peer) + if (argc == 2) + peer = peer_lookup_in_view (vty, argv[0], argv[1]); + else + peer = peer_lookup_in_view (vty, NULL, argv[0]); + + if (! peer) return CMD_WARNING; - - return bgp_peer_counts (vty, peer, AFI_IP, SAFI_UNICAST); + + return peer_adj_routes (vty, peer, AFI_IP, SAFI_UNICAST, 1); } -DEFUN (show_bgp_ipv6_neighbor_prefix_counts, - show_bgp_ipv6_neighbor_prefix_counts_cmd, - "show bgp ipv6 neighbors (A.B.C.D|X:X::X:X) prefix-counts", +ALIAS (show_ip_bgp_view_neighbor_received_routes, + show_ip_bgp_neighbor_received_routes_cmd, + "show ip bgp neighbors (A.B.C.D|X:X::X:X) received-routes", + SHOW_STR + IP_STR + BGP_STR + "Detailed information on TCP and BGP neighbor connections\n" + "Neighbor to display information about\n" + "Neighbor to display information about\n" + "Display the received routes from neighbor\n") + +ALIAS (show_bgp_view_neighbor_received_routes, + show_bgp_ipv6_neighbor_received_routes_cmd, + "show bgp ipv6 neighbors (A.B.C.D|X:X::X:X) received-routes", SHOW_STR BGP_STR "Address family\n" "Detailed information on TCP and BGP neighbor connections\n" "Neighbor to display information about\n" "Neighbor to display information about\n" - "Display detailed prefix count information\n") -{ - struct peer *peer; - - peer = peer_lookup_in_view (vty, NULL, argv[0]); - if (! peer) - return CMD_WARNING; - - return bgp_peer_counts (vty, peer, AFI_IP6, SAFI_UNICAST); -} + "Display the received routes from neighbor\n") -DEFUN (show_ip_bgp_ipv4_neighbor_prefix_counts, - show_ip_bgp_ipv4_neighbor_prefix_counts_cmd, - "show ip bgp ipv4 (unicast|multicast) neighbors (A.B.C.D|X:X::X:X) prefix-counts", +DEFUN (show_bgp_neighbor_received_prefix_filter, + show_bgp_neighbor_received_prefix_filter_cmd, + "show bgp neighbors (A.B.C.D|X:X::X:X) received prefix-filter", SHOW_STR - IP_STR BGP_STR - "Address family\n" - "Address Family modifier\n" - "Address Family modifier\n" "Detailed information on TCP and BGP neighbor connections\n" "Neighbor to display information about\n" "Neighbor to display information about\n" - "Display detailed prefix count information\n") + "Display information received from a BGP neighbor\n" + "Display the prefixlist filter\n") { + char name[BUFSIZ]; + union sockunion su; struct peer *peer; + int count, ret; - peer = peer_lookup_in_view (vty, NULL, argv[1]); + ret = str2sockunion (argv[0], &su); + if (ret < 0) + { + vty_out (vty, "Malformed address: %s%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + + peer = peer_lookup (NULL, &su); if (! peer) return CMD_WARNING; - if (strncmp (argv[0], "m", 1) == 0) - return bgp_peer_counts (vty, peer, AFI_IP, SAFI_MULTICAST); + sprintf (name, "%s.%d.%d", peer->host, AFI_IP6, SAFI_UNICAST); + count = prefix_bgp_show_prefix_list (NULL, AFI_IP6, name); + if (count) + { + vty_out (vty, "Address family: IPv6 Unicast%s", VTY_NEWLINE); + prefix_bgp_show_prefix_list (vty, AFI_IP6, name); + } - return bgp_peer_counts (vty, peer, AFI_IP, SAFI_UNICAST); + return CMD_SUCCESS; } -DEFUN (show_ip_bgp_vpnv4_neighbor_prefix_counts, - show_ip_bgp_vpnv4_neighbor_prefix_counts_cmd, - "show ip bgp vpnv4 all neighbors (A.B.C.D|X:X::X:X) prefix-counts", +/* old command */ +ALIAS (show_bgp_view_neighbor_received_routes, + ipv6_bgp_neighbor_received_routes_cmd, + "show ipv6 bgp neighbors (A.B.C.D|X:X::X:X) received-routes", SHOW_STR - IP_STR + IPV6_STR BGP_STR - "Address family\n" - "Address Family modifier\n" - "Address Family modifier\n" "Detailed information on TCP and BGP neighbor connections\n" "Neighbor to display information about\n" "Neighbor to display information about\n" - "Display detailed prefix count information\n") + "Display the received routes from neighbor\n") + +/* old command */ +DEFUN (ipv6_mbgp_neighbor_received_routes, + ipv6_mbgp_neighbor_received_routes_cmd, + "show ipv6 mbgp neighbors (A.B.C.D|X:X::X:X) received-routes", + SHOW_STR + IPV6_STR + MBGP_STR + "Detailed information on TCP and BGP neighbor connections\n" + "Neighbor to display information about\n" + "Neighbor to display information about\n" + "Display the received routes from neighbor\n") { struct peer *peer; peer = peer_lookup_in_view (vty, NULL, argv[0]); if (! peer) return CMD_WARNING; - - return bgp_peer_counts (vty, peer, AFI_IP, SAFI_MPLS_VPN); -} + return peer_adj_routes (vty, peer, AFI_IP6, SAFI_MULTICAST, 1); +} -static void -show_adj_route (struct vty *vty, struct peer *peer, afi_t afi, safi_t safi, - int in) +DEFUN (show_bgp_view_neighbor_received_prefix_filter, + show_bgp_view_neighbor_received_prefix_filter_cmd, + "show bgp view WORD neighbors (A.B.C.D|X:X::X:X) received prefix-filter", + SHOW_STR + BGP_STR + "BGP view\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" + "Display information received from a BGP neighbor\n" + "Display the prefixlist filter\n") { - struct bgp_table *table; - struct bgp_adj_in *ain; - struct bgp_adj_out *adj; - unsigned long output_count; - struct bgp_node *rn; - int header1 = 1; + char name[BUFSIZ]; + union sockunion su; + struct peer *peer; struct bgp *bgp; - int header2 = 1; - - bgp = peer->bgp; - - if (! bgp) - return; - - table = bgp->rib[afi][safi]; - - output_count = 0; - - if (! in && CHECK_FLAG (peer->af_sflags[afi][safi], - PEER_STATUS_DEFAULT_ORIGINATE)) - { - vty_out (vty, "BGP table version is 0, local router ID is %s%s", inet_ntoa (bgp->router_id), VTY_NEWLINE); - vty_out (vty, BGP_SHOW_SCODE_HEADER, VTY_NEWLINE, VTY_NEWLINE); - vty_out (vty, BGP_SHOW_OCODE_HEADER, VTY_NEWLINE, VTY_NEWLINE); - - vty_out (vty, "Originating default network 0.0.0.0%s%s", - VTY_NEWLINE, VTY_NEWLINE); - header1 = 0; - } + int count, ret; - for (rn = bgp_table_top (table); rn; rn = bgp_route_next (rn)) - if (in) - { - for (ain = rn->adj_in; ain; ain = ain->next) - if (ain->peer == peer) - { - if (header1) - { - vty_out (vty, "BGP table version is 0, local router ID is %s%s", inet_ntoa (bgp->router_id), VTY_NEWLINE); - vty_out (vty, BGP_SHOW_SCODE_HEADER, VTY_NEWLINE, VTY_NEWLINE); - vty_out (vty, BGP_SHOW_OCODE_HEADER, VTY_NEWLINE, VTY_NEWLINE); - header1 = 0; - } - if (header2) - { - vty_out (vty, BGP_SHOW_HEADER, VTY_NEWLINE); - header2 = 0; - } - if (ain->attr) - { - route_vty_out_tmp (vty, &rn->p, ain->attr, safi); - output_count++; - } - } - } - else - { - for (adj = rn->adj_out; adj; adj = adj->next) - if (adj->peer == peer) - { - if (header1) - { - vty_out (vty, "BGP table version is 0, local router ID is %s%s", inet_ntoa (bgp->router_id), VTY_NEWLINE); - vty_out (vty, BGP_SHOW_SCODE_HEADER, VTY_NEWLINE, VTY_NEWLINE); - vty_out (vty, BGP_SHOW_OCODE_HEADER, VTY_NEWLINE, VTY_NEWLINE); - header1 = 0; - } - if (header2) - { - vty_out (vty, BGP_SHOW_HEADER, VTY_NEWLINE); - header2 = 0; - } - if (adj->attr) - { - route_vty_out_tmp (vty, &rn->p, adj->attr, safi); - output_count++; - } - } - } + /* BGP structure lookup. */ + bgp = bgp_lookup_by_name (argv[0]); + if (bgp == NULL) + { + vty_out (vty, "Can't find BGP view %s%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } - if (output_count != 0) - vty_out (vty, "%sTotal number of prefixes %ld%s", - VTY_NEWLINE, output_count, VTY_NEWLINE); -} - -static int -peer_adj_routes (struct vty *vty, struct peer *peer, afi_t afi, safi_t safi, int in) -{ - if (! peer || ! peer->afc[afi][safi]) + ret = str2sockunion (argv[1], &su); + if (ret < 0) { - vty_out (vty, "%% No such neighbor or address family%s", VTY_NEWLINE); + vty_out (vty, "Malformed address: %s%s", argv[1], VTY_NEWLINE); return CMD_WARNING; } - if (in && ! CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_SOFT_RECONFIG)) + peer = peer_lookup (bgp, &su); + if (! peer) + return CMD_WARNING; + + sprintf (name, "%s.%d.%d", peer->host, AFI_IP6, SAFI_UNICAST); + count = prefix_bgp_show_prefix_list (NULL, AFI_IP6, name); + if (count) { - vty_out (vty, "%% Inbound soft reconfiguration not enabled%s", - VTY_NEWLINE); - return CMD_WARNING; + vty_out (vty, "Address family: IPv6 Unicast%s", VTY_NEWLINE); + prefix_bgp_show_prefix_list (vty, AFI_IP6, name); } - show_adj_route (vty, peer, afi, safi, in); - return CMD_SUCCESS; } -DEFUN (show_ip_bgp_view_neighbor_advertised_route, - show_ip_bgp_view_neighbor_advertised_route_cmd, - "show ip bgp view WORD neighbors (A.B.C.D|X:X::X:X) advertised-routes", + +DEFUN (show_ip_bgp_ipv4_neighbor_received_routes, + show_ip_bgp_ipv4_neighbor_received_routes_cmd, + "show ip bgp ipv4 (unicast|multicast) neighbors (A.B.C.D|X:X::X:X) received-routes", SHOW_STR IP_STR BGP_STR - "BGP view\n" - "View name\n" + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" "Detailed information on TCP and BGP neighbor connections\n" "Neighbor to display information about\n" "Neighbor to display information about\n" - "Display the routes advertised to a BGP neighbor\n") + "Display the received routes from neighbor\n") { struct peer *peer; - if (argc == 2) - peer = peer_lookup_in_view (vty, argv[0], argv[1]); - else - peer = peer_lookup_in_view (vty, NULL, argv[0]); - - if (! peer) + peer = peer_lookup_in_view (vty, NULL, argv[1]); + if (! peer) return CMD_WARNING; - - return peer_adj_routes (vty, peer, AFI_IP, SAFI_UNICAST, 0); -} + + if (strncmp (argv[0], "m", 1) == 0) + return peer_adj_routes (vty, peer, AFI_IP, SAFI_MULTICAST, 1); -ALIAS (show_ip_bgp_view_neighbor_advertised_route, - show_ip_bgp_neighbor_advertised_route_cmd, - "show ip bgp neighbors (A.B.C.D|X:X::X:X) advertised-routes", - SHOW_STR - IP_STR - BGP_STR - "Detailed information on TCP and BGP neighbor connections\n" - "Neighbor to display information about\n" - "Neighbor to display information about\n" - "Display the routes advertised to a BGP neighbor\n") + return peer_adj_routes (vty, peer, AFI_IP, SAFI_UNICAST, 1); +} -DEFUN (show_ip_bgp_ipv4_neighbor_advertised_route, - show_ip_bgp_ipv4_neighbor_advertised_route_cmd, - "show ip bgp ipv4 (unicast|multicast) neighbors (A.B.C.D|X:X::X:X) advertised-routes", +DEFUN (show_bgp_ipv4_safi_neighbor_advertised_route, + show_bgp_ipv4_safi_neighbor_advertised_route_cmd, + "show bgp ipv4 (multicast|unicast) neighbors (A.B.C.D|X:X::X:X) advertised-routes", SHOW_STR - IP_STR BGP_STR - "Address family\n" "Address Family modifier\n" "Address Family modifier\n" "Detailed information on TCP and BGP neighbor connections\n" @@ -9851,44 +13973,49 @@ DEFUN (show_ip_bgp_ipv4_neighbor_advertised_route, "Display the routes advertised to a BGP neighbor\n") { struct peer *peer; + safi_t safi; + + if (bgp_parse_safi(argv[0], &safi)) { + vty_out (vty, "Error: Bad SAFI: %s%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } peer = peer_lookup_in_view (vty, NULL, argv[1]); if (! peer) return CMD_WARNING; - if (strncmp (argv[0], "m", 1) == 0) - return peer_adj_routes (vty, peer, AFI_IP, SAFI_MULTICAST, 0); - - return peer_adj_routes (vty, peer, AFI_IP, SAFI_UNICAST, 0); + return peer_adj_routes (vty, peer, AFI_IP, safi, 0); } -#ifdef HAVE_IPV6 -DEFUN (show_bgp_view_neighbor_advertised_route, - show_bgp_view_neighbor_advertised_route_cmd, - "show bgp view WORD neighbors (A.B.C.D|X:X::X:X) advertised-routes", +DEFUN (show_bgp_ipv6_safi_neighbor_advertised_route, + show_bgp_ipv6_safi_neighbor_advertised_route_cmd, + "show bgp ipv6 (multicast|unicast) neighbors (A.B.C.D|X:X::X:X) advertised-routes", SHOW_STR BGP_STR - "BGP view\n" - "View name\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Address Family modifier\n" "Detailed information on TCP and BGP neighbor connections\n" "Neighbor to display information about\n" "Neighbor to display information about\n" "Display the routes advertised to a BGP neighbor\n") { struct peer *peer; + safi_t safi; - if (argc == 2) - peer = peer_lookup_in_view (vty, argv[0], argv[1]); - else - peer = peer_lookup_in_view (vty, NULL, argv[0]); + if (bgp_parse_safi(argv[0], &safi)) { + vty_out (vty, "Error: Bad SAFI: %s%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + peer = peer_lookup_in_view (vty, NULL, argv[1]); if (! peer) - return CMD_WARNING; + return CMD_WARNING; - return peer_adj_routes (vty, peer, AFI_IP6, SAFI_UNICAST, 0); + return peer_adj_routes (vty, peer, AFI_IP6, safi, 0); } -ALIAS (show_bgp_view_neighbor_advertised_route, +DEFUN (show_bgp_view_ipv6_neighbor_advertised_route, show_bgp_view_ipv6_neighbor_advertised_route_cmd, "show bgp view WORD ipv6 neighbors (A.B.C.D|X:X::X:X) advertised-routes", SHOW_STR @@ -9900,18 +14027,6 @@ ALIAS (show_bgp_view_neighbor_advertised_route, "Neighbor to display information about\n" "Neighbor to display information about\n" "Display the routes advertised to a BGP neighbor\n") - -DEFUN (show_bgp_view_neighbor_received_routes, - show_bgp_view_neighbor_received_routes_cmd, - "show bgp view WORD neighbors (A.B.C.D|X:X::X:X) received-routes", - SHOW_STR - BGP_STR - "BGP view\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" - "Display the received routes from neighbor\n") { struct peer *peer; @@ -9921,12 +14036,12 @@ DEFUN (show_bgp_view_neighbor_received_routes, peer = peer_lookup_in_view (vty, NULL, argv[0]); if (! peer) - return CMD_WARNING; + return CMD_WARNING; - return peer_adj_routes (vty, peer, AFI_IP6, SAFI_UNICAST, 1); + return peer_adj_routes (vty, peer, AFI_IP6, SAFI_UNICAST, 0); } -ALIAS (show_bgp_view_neighbor_received_routes, +DEFUN (show_bgp_view_ipv6_neighbor_received_routes, show_bgp_view_ipv6_neighbor_received_routes_cmd, "show bgp view WORD ipv6 neighbors (A.B.C.D|X:X::X:X) received-routes", SHOW_STR @@ -9938,74 +14053,6 @@ ALIAS (show_bgp_view_neighbor_received_routes, "Neighbor to display information about\n" "Neighbor to display information about\n" "Display the received routes from neighbor\n") - -ALIAS (show_bgp_view_neighbor_advertised_route, - show_bgp_neighbor_advertised_route_cmd, - "show bgp neighbors (A.B.C.D|X:X::X:X) advertised-routes", - SHOW_STR - BGP_STR - "Detailed information on TCP and BGP neighbor connections\n" - "Neighbor to display information about\n" - "Neighbor to display information about\n" - "Display the routes advertised to a BGP neighbor\n") - -ALIAS (show_bgp_view_neighbor_advertised_route, - show_bgp_ipv6_neighbor_advertised_route_cmd, - "show bgp ipv6 neighbors (A.B.C.D|X:X::X:X) advertised-routes", - SHOW_STR - BGP_STR - "Address family\n" - "Detailed information on TCP and BGP neighbor connections\n" - "Neighbor to display information about\n" - "Neighbor to display information about\n" - "Display the routes advertised to a BGP neighbor\n") - -/* old command */ -ALIAS (show_bgp_view_neighbor_advertised_route, - ipv6_bgp_neighbor_advertised_route_cmd, - "show ipv6 bgp neighbors (A.B.C.D|X:X::X:X) advertised-routes", - SHOW_STR - IPV6_STR - BGP_STR - "Detailed information on TCP and BGP neighbor connections\n" - "Neighbor to display information about\n" - "Neighbor to display information about\n" - "Display the routes advertised to a BGP neighbor\n") - -/* old command */ -DEFUN (ipv6_mbgp_neighbor_advertised_route, - ipv6_mbgp_neighbor_advertised_route_cmd, - "show ipv6 mbgp neighbors (A.B.C.D|X:X::X:X) advertised-routes", - SHOW_STR - IPV6_STR - MBGP_STR - "Detailed information on TCP and BGP neighbor connections\n" - "Neighbor to display information about\n" - "Neighbor to display information about\n" - "Display the routes advertised to a BGP neighbor\n") -{ - struct peer *peer; - - peer = peer_lookup_in_view (vty, NULL, argv[0]); - if (! peer) - return CMD_WARNING; - - 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", - SHOW_STR - IP_STR - BGP_STR - "BGP view\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" - "Display the received routes from neighbor\n") { struct peer *peer; @@ -10017,61 +14064,78 @@ DEFUN (show_ip_bgp_view_neighbor_received_routes, if (! peer) return CMD_WARNING; - return peer_adj_routes (vty, peer, AFI_IP, SAFI_UNICAST, 1); + return peer_adj_routes (vty, peer, AFI_IP6, SAFI_UNICAST, 1); } -ALIAS (show_ip_bgp_view_neighbor_received_routes, - show_ip_bgp_neighbor_received_routes_cmd, - "show ip bgp neighbors (A.B.C.D|X:X::X:X) received-routes", +DEFUN (show_bgp_ipv4_safi_neighbor_received_routes, + show_bgp_ipv4_safi_neighbor_received_routes_cmd, + "show bgp ipv4 (encap|multicast|unicast|vpn) neighbors (A.B.C.D|X:X::X:X) received-routes", SHOW_STR - IP_STR BGP_STR + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Address Family modifier\n" "Detailed information on TCP and BGP neighbor connections\n" "Neighbor to display information about\n" "Neighbor to display information about\n" "Display the received routes from neighbor\n") +{ + struct peer *peer; + safi_t safi; -DEFUN (show_ip_bgp_ipv4_neighbor_received_routes, - show_ip_bgp_ipv4_neighbor_received_routes_cmd, - "show ip bgp ipv4 (unicast|multicast) neighbors (A.B.C.D|X:X::X:X) received-routes", + if (bgp_parse_safi(argv[0], &safi)) { + vty_out (vty, "Error: Bad SAFI: %s%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + + peer = peer_lookup_in_view (vty, NULL, argv[1]); + if (! peer) + return CMD_WARNING; + + return peer_adj_routes (vty, peer, AFI_IP, safi, 1); +} + +DEFUN (show_bgp_ipv6_safi_neighbor_received_routes, + show_bgp_ipv6_safi_neighbor_received_routes_cmd, + "show bgp ipv6 (encap|multicast|unicast|vpn) neighbors (A.B.C.D|X:X::X:X) received-routes", SHOW_STR - IP_STR BGP_STR "Address family\n" "Address Family modifier\n" "Address Family modifier\n" + "Address Family modifier\n" + "Address Family modifier\n" "Detailed information on TCP and BGP neighbor connections\n" "Neighbor to display information about\n" "Neighbor to display information about\n" "Display the received routes from neighbor\n") { struct peer *peer; + safi_t safi; + + if (bgp_parse_safi(argv[0], &safi)) { + vty_out (vty, "Error: Bad SAFI: %s%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } peer = peer_lookup_in_view (vty, NULL, argv[1]); if (! peer) return CMD_WARNING; - if (strncmp (argv[0], "m", 1) == 0) - return peer_adj_routes (vty, peer, AFI_IP, SAFI_MULTICAST, 1); - - return peer_adj_routes (vty, peer, AFI_IP, SAFI_UNICAST, 1); + return peer_adj_routes (vty, peer, AFI_IP6, safi, 1); } DEFUN (show_bgp_view_afi_safi_neighbor_adv_recd_routes, show_bgp_view_afi_safi_neighbor_adv_recd_routes_cmd, -#ifdef HAVE_IPV6 "show bgp view WORD (ipv4|ipv6) (unicast|multicast) neighbors (A.B.C.D|X:X::X:X) (advertised-routes|received-routes)", -#else - "show bgp view WORD ipv4 (unicast|multicast) neighbors (A.B.C.D|X:X::X:X) (advertised-routes|received-routes)", -#endif 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" "Detailed information on TCP and BGP neighbor connections\n" @@ -10085,24 +14149,14 @@ DEFUN (show_bgp_view_afi_safi_neighbor_adv_recd_routes, int in; struct peer *peer; -#ifdef HAVE_IPV6 - peer = peer_lookup_in_view (vty, argv[0], argv[3]); -#else - peer = peer_lookup_in_view (vty, argv[0], argv[2]); -#endif + peer = peer_lookup_in_view (vty, argv[0], argv[3]); if (! peer) return CMD_WARNING; -#ifdef HAVE_IPV6 afi = (strncmp (argv[1], "ipv6", 4) == 0) ? AFI_IP6 : AFI_IP; safi = (strncmp (argv[2], "m", 1) == 0) ? SAFI_MULTICAST : SAFI_UNICAST; in = (strncmp (argv[4], "r", 1) == 0) ? 1 : 0; -#else - afi = AFI_IP; - safi = (strncmp (argv[1], "m", 1) == 0) ? SAFI_MULTICAST : SAFI_UNICAST; - in = (strncmp (argv[3], "r", 1) == 0) ? 1 : 0; -#endif return peer_adj_routes (vty, peer, afi, safi, in); } @@ -10120,15 +14174,18 @@ DEFUN (show_ip_bgp_neighbor_received_prefix_filter, "Display the prefixlist filter\n") { char name[BUFSIZ]; - union sockunion *su; + union sockunion su; struct peer *peer; - int count; + int count, ret; - su = sockunion_str2su (argv[0]); - if (su == NULL) - return CMD_WARNING; + ret = str2sockunion (argv[0], &su); + if (ret < 0) + { + vty_out (vty, "Malformed address: %s%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } - peer = peer_lookup (NULL, su); + peer = peer_lookup (NULL, &su); if (! peer) return CMD_WARNING; @@ -10159,15 +14216,18 @@ DEFUN (show_ip_bgp_ipv4_neighbor_received_prefix_filter, "Display the prefixlist filter\n") { char name[BUFSIZ]; - union sockunion *su; + union sockunion su; struct peer *peer; - int count; + int count, ret; - su = sockunion_str2su (argv[1]); - if (su == NULL) - return CMD_WARNING; + ret = str2sockunion (argv[1], &su); + if (ret < 0) + { + vty_out (vty, "Malformed address: %s%s", argv[1], VTY_NEWLINE); + return CMD_WARNING; + } - peer = peer_lookup (NULL, su); + peer = peer_lookup (NULL, &su); if (! peer) return CMD_WARNING; @@ -10195,8 +14255,6 @@ DEFUN (show_ip_bgp_ipv4_neighbor_received_prefix_filter, return CMD_SUCCESS; } - -#ifdef HAVE_IPV6 ALIAS (show_bgp_view_neighbor_received_routes, show_bgp_neighbor_received_routes_cmd, "show bgp neighbors (A.B.C.D|X:X::X:X) received-routes", @@ -10207,22 +14265,16 @@ ALIAS (show_bgp_view_neighbor_received_routes, "Neighbor to display information about\n" "Display the received routes from neighbor\n") -ALIAS (show_bgp_view_neighbor_received_routes, - show_bgp_ipv6_neighbor_received_routes_cmd, - "show bgp ipv6 neighbors (A.B.C.D|X:X::X:X) received-routes", - SHOW_STR - BGP_STR - "Address family\n" - "Detailed information on TCP and BGP neighbor connections\n" - "Neighbor to display information about\n" - "Neighbor to display information about\n" - "Display the received routes from neighbor\n") - -DEFUN (show_bgp_neighbor_received_prefix_filter, - show_bgp_neighbor_received_prefix_filter_cmd, - "show bgp neighbors (A.B.C.D|X:X::X:X) received prefix-filter", +DEFUN (show_bgp_ipv4_safi_neighbor_received_prefix_filter, + show_bgp_ipv4_safi_neighbor_received_prefix_filter_cmd, + "show bgp ipv4 (encap|multicast|unicast|vpn) neighbors (A.B.C.D|X:X::X:X) received prefix-filter", SHOW_STR BGP_STR + IP_STR + "Address Family modifier\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Address Family modifier\n" "Detailed information on TCP and BGP neighbor connections\n" "Neighbor to display information about\n" "Neighbor to display information about\n" @@ -10230,81 +14282,132 @@ DEFUN (show_bgp_neighbor_received_prefix_filter, "Display the prefixlist filter\n") { char name[BUFSIZ]; - union sockunion *su; + union sockunion su; struct peer *peer; - int count; + int count, ret; + safi_t safi; - su = sockunion_str2su (argv[0]); - if (su == NULL) + if (bgp_parse_safi(argv[0], &safi)) { + vty_out (vty, "Error: Bad SAFI: %s%s", argv[0], VTY_NEWLINE); return CMD_WARNING; + } + + ret = str2sockunion (argv[1], &su); + if (ret < 0) + { + vty_out (vty, "Malformed address: %s%s", argv[1], VTY_NEWLINE); + return CMD_WARNING; + } - peer = peer_lookup (NULL, su); + peer = peer_lookup (NULL, &su); if (! peer) return CMD_WARNING; - sprintf (name, "%s.%d.%d", peer->host, AFI_IP6, SAFI_UNICAST); - count = prefix_bgp_show_prefix_list (NULL, AFI_IP6, name); - if (count) - { - vty_out (vty, "Address family: IPv6 Unicast%s", VTY_NEWLINE); - prefix_bgp_show_prefix_list (vty, AFI_IP6, name); - } + sprintf (name, "%s.%d.%d", peer->host, AFI_IP, safi); + count = prefix_bgp_show_prefix_list (NULL, AFI_IP, name); + if (count) { + vty_out (vty, "Address family: IPv4 %s%s", safi2str(safi), VTY_NEWLINE); + prefix_bgp_show_prefix_list (vty, AFI_IP, name); + } return CMD_SUCCESS; } -ALIAS (show_bgp_neighbor_received_prefix_filter, - show_bgp_ipv6_neighbor_received_prefix_filter_cmd, - "show bgp ipv6 neighbors (A.B.C.D|X:X::X:X) received prefix-filter", +DEFUN (show_bgp_ipv6_safi_neighbor_received_prefix_filter, + show_bgp_ipv6_safi_neighbor_received_prefix_filter_cmd, + "show bgp ipv6 (encap|multicast|unicast|vpn) neighbors (A.B.C.D|X:X::X:X) received prefix-filter", SHOW_STR BGP_STR - "Address family\n" + IP_STR + "Address Family modifier\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Address Family modifier\n" "Detailed information on TCP and BGP neighbor connections\n" "Neighbor to display information about\n" "Neighbor to display information about\n" "Display information received from a BGP neighbor\n" "Display the prefixlist filter\n") +{ + char name[BUFSIZ]; + union sockunion su; + struct peer *peer; + int count, ret; + safi_t safi; -/* old command */ -ALIAS (show_bgp_view_neighbor_received_routes, - ipv6_bgp_neighbor_received_routes_cmd, - "show ipv6 bgp neighbors (A.B.C.D|X:X::X:X) received-routes", - SHOW_STR - IPV6_STR - BGP_STR - "Detailed information on TCP and BGP neighbor connections\n" - "Neighbor to display information about\n" - "Neighbor to display information about\n" - "Display the received routes from neighbor\n") + if (bgp_parse_safi(argv[0], &safi)) { + vty_out (vty, "Error: Bad SAFI: %s%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } -/* old command */ -DEFUN (ipv6_mbgp_neighbor_received_routes, - ipv6_mbgp_neighbor_received_routes_cmd, - "show ipv6 mbgp neighbors (A.B.C.D|X:X::X:X) received-routes", + ret = str2sockunion (argv[1], &su); + if (ret < 0) + { + vty_out (vty, "Malformed address: %s%s", argv[1], VTY_NEWLINE); + return CMD_WARNING; + } + + peer = peer_lookup (NULL, &su); + if (! peer) + return CMD_WARNING; + + sprintf (name, "%s.%d.%d", peer->host, AFI_IP6, safi); + count = prefix_bgp_show_prefix_list (NULL, AFI_IP6, name); + if (count) { + vty_out (vty, "Address family: IPv6 %s%s", safi2str(safi), VTY_NEWLINE); + prefix_bgp_show_prefix_list (vty, AFI_IP6, name); + } + + return CMD_SUCCESS; +} + +DEFUN (show_bgp_ipv6_neighbor_received_prefix_filter, + show_bgp_ipv6_neighbor_received_prefix_filter_cmd, + "show bgp ipv6 neighbors (A.B.C.D|X:X::X:X) received prefix-filter", SHOW_STR - IPV6_STR - MBGP_STR + BGP_STR + "Address family\n" "Detailed information on TCP and BGP neighbor connections\n" "Neighbor to display information about\n" "Neighbor to display information about\n" - "Display the received routes from neighbor\n") + "Display information received from a BGP neighbor\n" + "Display the prefixlist filter\n") { + char name[BUFSIZ]; + union sockunion su; struct peer *peer; + int count, ret; - peer = peer_lookup_in_view (vty, NULL, argv[0]); + ret = str2sockunion (argv[0], &su); + if (ret < 0) + { + vty_out (vty, "Malformed address: %s%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + + peer = peer_lookup (NULL, &su); if (! peer) return CMD_WARNING; - return peer_adj_routes (vty, peer, AFI_IP6, SAFI_MULTICAST, 1); + sprintf (name, "%s.%d.%d", peer->host, AFI_IP6, SAFI_UNICAST); + count = prefix_bgp_show_prefix_list (NULL, AFI_IP6, name); + if (count) + { + vty_out (vty, "Address family: IPv6 Unicast%s", VTY_NEWLINE); + prefix_bgp_show_prefix_list (vty, AFI_IP6, name); + } + + return CMD_SUCCESS; } -DEFUN (show_bgp_view_neighbor_received_prefix_filter, - show_bgp_view_neighbor_received_prefix_filter_cmd, - "show bgp view WORD neighbors (A.B.C.D|X:X::X:X) received prefix-filter", +DEFUN (show_bgp_view_ipv6_neighbor_received_prefix_filter, + show_bgp_view_ipv6_neighbor_received_prefix_filter_cmd, + "show bgp view WORD ipv6 neighbors (A.B.C.D|X:X::X:X) received prefix-filter", SHOW_STR BGP_STR "BGP view\n" "View name\n" + "Address family\n" "Detailed information on TCP and BGP neighbor connections\n" "Neighbor to display information about\n" "Neighbor to display information about\n" @@ -10312,10 +14415,10 @@ DEFUN (show_bgp_view_neighbor_received_prefix_filter, "Display the prefixlist filter\n") { char name[BUFSIZ]; - union sockunion *su; + union sockunion su; struct peer *peer; struct bgp *bgp; - int count; + int count, ret; /* BGP structure lookup. */ bgp = bgp_lookup_by_name (argv[0]); @@ -10325,11 +14428,14 @@ DEFUN (show_bgp_view_neighbor_received_prefix_filter, return CMD_WARNING; } - su = sockunion_str2su (argv[1]); - if (su == NULL) - return CMD_WARNING; + ret = str2sockunion (argv[1], &su); + if (ret < 0) + { + vty_out (vty, "Malformed address: %s%s", argv[1], VTY_NEWLINE); + return CMD_WARNING; + } - peer = peer_lookup (bgp, su); + peer = peer_lookup (bgp, &su); if (! peer) return CMD_WARNING; @@ -10344,21 +14450,6 @@ DEFUN (show_bgp_view_neighbor_received_prefix_filter, return CMD_SUCCESS; } -ALIAS (show_bgp_view_neighbor_received_prefix_filter, - show_bgp_view_ipv6_neighbor_received_prefix_filter_cmd, - "show bgp view WORD ipv6 neighbors (A.B.C.D|X:X::X:X) received prefix-filter", - SHOW_STR - BGP_STR - "BGP view\n" - "View name\n" - "Address family\n" - "Detailed information on TCP and BGP neighbor connections\n" - "Neighbor to display information about\n" - "Neighbor to display information about\n" - "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) @@ -10371,7 +14462,6 @@ bgp_show_neighbor_route (struct vty *vty, struct peer *peer, afi_t afi, return bgp_show (vty, peer->bgp, afi, safi, type, &peer->su); } - DEFUN (show_ip_bgp_neighbor_routes, show_ip_bgp_neighbor_routes_cmd, "show ip bgp neighbors (A.B.C.D|X:X::X:X) routes", @@ -10470,7 +14560,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) { @@ -10520,7 +14610,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" @@ -10562,34 +14652,288 @@ DEFUN (show_bgp_view_ipv4_safi_rsclient, return bgp_show_table (vty, table, &peer->remote_id, bgp_show_type_normal, NULL); } -ALIAS (show_bgp_view_ipv4_safi_rsclient, - show_bgp_ipv4_safi_rsclient_cmd, - "show bgp ipv4 (unicast|multicast) rsclient (A.B.C.D|X:X::X:X)", +ALIAS (show_bgp_view_ipv4_safi_rsclient, + show_bgp_ipv4_safi_rsclient_cmd, + "show bgp ipv4 (unicast|multicast) rsclient (A.B.C.D|X:X::X:X)", + SHOW_STR + BGP_STR + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Information about Route Server Client\n" + NEIGHBOR_ADDR_STR) + +DEFUN (show_ip_bgp_view_rsclient_route, + show_ip_bgp_view_rsclient_route_cmd, + "show ip bgp view WORD rsclient (A.B.C.D|X:X::X:X) A.B.C.D", + SHOW_STR + IP_STR + BGP_STR + "BGP view\n" + "View name\n" + "Information about Route Server Client\n" + NEIGHBOR_ADDR_STR + "Network in the BGP routing table to display\n") +{ + struct bgp *bgp; + struct peer *peer; + + /* BGP structure lookup. */ + if (argc == 3) + { + bgp = bgp_lookup_by_name (argv[0]); + if (bgp == NULL) + { + vty_out (vty, "Can't find BGP view %s%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + } + else + { + bgp = bgp_get_default (); + if (bgp == NULL) + { + vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + } + + if (argc == 3) + peer = peer_lookup_in_view (vty, argv[0], argv[1]); + else + peer = peer_lookup_in_view (vty, NULL, argv[0]); + + if (! peer) + return CMD_WARNING; + + if (! peer->afc[AFI_IP][SAFI_UNICAST]) + { + vty_out (vty, "%% Activate the neighbor for the address family first%s", + VTY_NEWLINE); + return CMD_WARNING; +} + + if ( ! CHECK_FLAG (peer->af_flags[AFI_IP][SAFI_UNICAST], + PEER_FLAG_RSERVER_CLIENT)) + { + vty_out (vty, "%% Neighbor is not a Route-Server client%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + return bgp_show_route_in_table (vty, bgp, peer->rib[AFI_IP][SAFI_UNICAST], + (argc == 3) ? argv[2] : argv[1], + AFI_IP, SAFI_UNICAST, NULL, 0, BGP_PATH_ALL); +} + +ALIAS (show_ip_bgp_view_rsclient_route, + show_ip_bgp_rsclient_route_cmd, + "show ip bgp rsclient (A.B.C.D|X:X::X:X) A.B.C.D", + SHOW_STR + IP_STR + BGP_STR + "Information about Route Server Client\n" + NEIGHBOR_ADDR_STR + "Network in the BGP routing table to display\n") + +DEFUN (show_bgp_ipv4_safi_neighbor_flap, + show_bgp_ipv4_safi_neighbor_flap_cmd, + "show bgp ipv4 (encap|multicast|unicast|vpn) neighbors (A.B.C.D|X:X::X:X) flap-statistics", + SHOW_STR + BGP_STR + "Address Family Modifier\n" + "Address Family Modifier\n" + "Address Family Modifier\n" + "Address Family Modifier\n" + "Detailed information on TCP and BGP neighbor connections\n" + "Neighbor to display information about\n" + "Neighbor to display information about\n" + "Display flap statistics of the routes learned from neighbor\n") +{ + struct peer *peer; + safi_t safi; + + if (bgp_parse_safi(argv[0], &safi)) { + vty_out (vty, "Error: Bad SAFI: %s%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + + peer = peer_lookup_in_view (vty, NULL, argv[1]); + if (! peer) + return CMD_WARNING; + + return bgp_show_neighbor_route (vty, peer, AFI_IP, safi, + bgp_show_type_flap_neighbor); +} + +DEFUN (show_bgp_ipv6_safi_neighbor_flap, + show_bgp_ipv6_safi_neighbor_flap_cmd, + "show bgp ipv6 (encap|multicast|unicast|vpn) neighbors (A.B.C.D|X:X::X:X) flap-statistics", + SHOW_STR + BGP_STR + "Address Family Modifier\n" + "Address Family Modifier\n" + "Address Family Modifier\n" + "Address Family Modifier\n" + "Detailed information on TCP and BGP neighbor connections\n" + "Neighbor to display information about\n" + "Neighbor to display information about\n" + "Display flap statistics of the routes learned from neighbor\n") +{ + struct peer *peer; + safi_t safi; + + if (bgp_parse_safi(argv[0], &safi)) { + vty_out (vty, "Error: Bad SAFI: %s%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + + peer = peer_lookup_in_view (vty, NULL, argv[1]); + if (! peer) + return CMD_WARNING; + + return bgp_show_neighbor_route (vty, peer, AFI_IP6, safi, + bgp_show_type_flap_neighbor); +} + +DEFUN (show_bgp_ipv4_safi_neighbor_damp, + show_bgp_ipv4_safi_neighbor_damp_cmd, + "show bgp ipv4 (encap|multicast|unicast|vpn) neighbors (A.B.C.D|X:X::X:X) dampened-routes", + SHOW_STR + BGP_STR + "Address Family Modifier\n" + "Address Family Modifier\n" + "Address Family Modifier\n" + "Address Family Modifier\n" + "Detailed information on TCP and BGP neighbor connections\n" + "Neighbor to display information about\n" + "Neighbor to display information about\n" + "Display the dampened routes received from neighbor\n") +{ + struct peer *peer; + safi_t safi; + + if (bgp_parse_safi(argv[0], &safi)) { + vty_out (vty, "Error: Bad SAFI: %s%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + + peer = peer_lookup_in_view (vty, NULL, argv[1]); + if (! peer) + return CMD_WARNING; + + return bgp_show_neighbor_route (vty, peer, AFI_IP, safi, + bgp_show_type_damp_neighbor); +} + +DEFUN (show_bgp_ipv6_safi_neighbor_damp, + show_bgp_ipv6_safi_neighbor_damp_cmd, + "show bgp ipv6 (encap|multicast|unicast|vpn) neighbors (A.B.C.D|X:X::X:X) dampened-routes", + SHOW_STR + BGP_STR + "Address Family Modifier\n" + "Address Family Modifier\n" + "Address Family Modifier\n" + "Address Family Modifier\n" + "Detailed information on TCP and BGP neighbor connections\n" + "Neighbor to display information about\n" + "Neighbor to display information about\n" + "Display the dampened routes received from neighbor\n") +{ + struct peer *peer; + safi_t safi; + + if (bgp_parse_safi(argv[0], &safi)) { + vty_out (vty, "Error: Bad SAFI: %s%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + + peer = peer_lookup_in_view (vty, NULL, argv[1]); + if (! peer) + return CMD_WARNING; + + return bgp_show_neighbor_route (vty, peer, AFI_IP6, safi, + bgp_show_type_damp_neighbor); +} + +DEFUN (show_bgp_ipv4_safi_neighbor_routes, + show_bgp_ipv4_safi_neighbor_routes_cmd, + "show bgp ipv4 (multicast|unicast) neighbors (A.B.C.D|X:X::X:X) routes", + SHOW_STR + BGP_STR + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Detailed information on TCP and BGP neighbor connections\n" + "Neighbor to display information about\n" + "Neighbor to display information about\n" + "Display routes learned from neighbor\n") +{ + struct peer *peer; + safi_t safi; + + if (bgp_parse_safi(argv[0], &safi)) { + vty_out (vty, "Error: Bad SAFI: %s%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + + peer = peer_lookup_in_view (vty, NULL, argv[1]); + if (! peer) + return CMD_WARNING; + + return bgp_show_neighbor_route (vty, peer, AFI_IP, safi, + bgp_show_type_neighbor); +} + +DEFUN (show_bgp_ipv6_safi_neighbor_routes, + show_bgp_ipv6_safi_neighbor_routes_cmd, + "show bgp ipv6 (multicast|unicast) neighbors (A.B.C.D|X:X::X:X) routes", + SHOW_STR + BGP_STR + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Detailed information on TCP and BGP neighbor connections\n" + NEIGHBOR_ADDR_STR + NEIGHBOR_ADDR_STR + "Display routes learned from neighbor\n") +{ + struct peer *peer; + safi_t safi; + + if (bgp_parse_safi(argv[0], &safi)) { + vty_out (vty, "Error: Bad SAFI: %s%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + + peer = peer_lookup_in_view (vty, NULL, argv[1]); + if (! peer) + return CMD_WARNING; + + return bgp_show_neighbor_route (vty, peer, AFI_IP6, safi, + bgp_show_type_neighbor); +} + +DEFUN (show_bgp_view_ipv4_safi_rsclient_route, + show_bgp_view_ipv4_safi_rsclient_route_cmd, + "show bgp view WORD ipv4 (unicast|multicast) rsclient (A.B.C.D|X:X::X:X) A.B.C.D", SHOW_STR BGP_STR + "BGP view\n" + "View name\n" "Address family\n" "Address Family modifier\n" "Address Family modifier\n" "Information about Route Server Client\n" - NEIGHBOR_ADDR_STR) - -DEFUN (show_ip_bgp_view_rsclient_route, - show_ip_bgp_view_rsclient_route_cmd, - "show ip bgp view WORD rsclient (A.B.C.D|X:X::X:X) A.B.C.D", - SHOW_STR - IP_STR - BGP_STR - "BGP view\n" - "BGP view name\n" - "Information about Route Server Client\n" NEIGHBOR_ADDR_STR "Network in the BGP routing table to display\n") { struct bgp *bgp; struct peer *peer; + safi_t safi; /* BGP structure lookup. */ - if (argc == 3) + if (argc == 4) { bgp = bgp_lookup_by_name (argv[0]); if (bgp == NULL) @@ -10608,57 +14952,63 @@ DEFUN (show_ip_bgp_view_rsclient_route, } } - if (argc == 3) - peer = peer_lookup_in_view (vty, argv[0], argv[1]); - else - peer = peer_lookup_in_view (vty, NULL, argv[0]); + if (argc == 4) { + peer = peer_lookup_in_view (vty, argv[0], argv[2]); + safi = (strncmp (argv[1], "m", 1) == 0) ? SAFI_MULTICAST : SAFI_UNICAST; + } else { + peer = peer_lookup_in_view (vty, NULL, argv[1]); + safi = (strncmp (argv[0], "m", 1) == 0) ? SAFI_MULTICAST : SAFI_UNICAST; + } if (! peer) return CMD_WARNING; - if (! peer->afc[AFI_IP][SAFI_UNICAST]) + if (! peer->afc[AFI_IP][safi]) { vty_out (vty, "%% Activate the neighbor for the address family first%s", VTY_NEWLINE); return CMD_WARNING; } - if ( ! CHECK_FLAG (peer->af_flags[AFI_IP][SAFI_UNICAST], + if ( ! CHECK_FLAG (peer->af_flags[AFI_IP][safi], PEER_FLAG_RSERVER_CLIENT)) { vty_out (vty, "%% Neighbor is not a Route-Server client%s", VTY_NEWLINE); return CMD_WARNING; } - - return bgp_show_route_in_table (vty, bgp, peer->rib[AFI_IP][SAFI_UNICAST], - (argc == 3) ? argv[2] : argv[1], - AFI_IP, SAFI_UNICAST, NULL, 0); + + return bgp_show_route_in_table (vty, bgp, peer->rib[AFI_IP][safi], + (argc == 4) ? argv[3] : argv[2], + AFI_IP, safi, NULL, 0, BGP_PATH_ALL); } -ALIAS (show_ip_bgp_view_rsclient_route, - show_ip_bgp_rsclient_route_cmd, - "show ip bgp rsclient (A.B.C.D|X:X::X:X) A.B.C.D", +ALIAS (show_bgp_view_ipv4_safi_rsclient_route, + show_bgp_ipv4_safi_rsclient_route_cmd, + "show bgp ipv4 (unicast|multicast) rsclient (A.B.C.D|X:X::X:X) A.B.C.D", SHOW_STR - IP_STR BGP_STR + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" "Information about Route Server Client\n" NEIGHBOR_ADDR_STR "Network in the BGP routing table to display\n") -DEFUN (show_bgp_view_ipv4_safi_rsclient_route, - show_bgp_view_ipv4_safi_rsclient_route_cmd, - "show bgp view WORD ipv4 (unicast|multicast) rsclient (A.B.C.D|X:X::X:X) A.B.C.D", + +DEFUN (show_bgp_view_ipv4_safi_rsclient_prefix, + show_bgp_view_ipv4_safi_rsclient_prefix_cmd, + "show bgp view WORD ipv4 (unicast|multicast) rsclient (A.B.C.D|X:X::X:X) A.B.C.D/M", 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" "Information about Route Server Client\n" NEIGHBOR_ADDR_STR - "Network in the BGP routing table to display\n") + "IP prefix /, e.g., 35.0.0.0/8\n") { struct bgp *bgp; struct peer *peer; @@ -10704,29 +15054,17 @@ DEFUN (show_bgp_view_ipv4_safi_rsclient_route, if ( ! CHECK_FLAG (peer->af_flags[AFI_IP][safi], PEER_FLAG_RSERVER_CLIENT)) - { +{ vty_out (vty, "%% Neighbor is not a Route-Server client%s", VTY_NEWLINE); - return CMD_WARNING; + return CMD_WARNING; } return bgp_show_route_in_table (vty, bgp, peer->rib[AFI_IP][safi], (argc == 4) ? argv[3] : argv[2], - AFI_IP, safi, NULL, 0); + AFI_IP, safi, NULL, 1, BGP_PATH_ALL); } -ALIAS (show_bgp_view_ipv4_safi_rsclient_route, - show_bgp_ipv4_safi_rsclient_route_cmd, - "show bgp ipv4 (unicast|multicast) rsclient (A.B.C.D|X:X::X:X) A.B.C.D", - SHOW_STR - BGP_STR - "Address family\n" - "Address Family modifier\n" - "Address Family modifier\n" - "Information about Route Server Client\n" - NEIGHBOR_ADDR_STR - "Network in the BGP routing table to display\n") - DEFUN (show_ip_bgp_view_rsclient_prefix, show_ip_bgp_view_rsclient_prefix_cmd, "show ip bgp view WORD rsclient (A.B.C.D|X:X::X:X) A.B.C.D/M", @@ -10734,7 +15072,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") @@ -10787,7 +15125,7 @@ DEFUN (show_ip_bgp_view_rsclient_prefix, return bgp_show_route_in_table (vty, bgp, peer->rib[AFI_IP][SAFI_UNICAST], (argc == 3) ? argv[2] : argv[1], - AFI_IP, SAFI_UNICAST, NULL, 1); + AFI_IP, SAFI_UNICAST, NULL, 1, BGP_PATH_ALL); } ALIAS (show_ip_bgp_view_rsclient_prefix, @@ -10800,99 +15138,83 @@ ALIAS (show_ip_bgp_view_rsclient_prefix, NEIGHBOR_ADDR_STR "IP prefix /, e.g., 35.0.0.0/8\n") -DEFUN (show_bgp_view_ipv4_safi_rsclient_prefix, - show_bgp_view_ipv4_safi_rsclient_prefix_cmd, - "show bgp view WORD ipv4 (unicast|multicast) rsclient (A.B.C.D|X:X::X:X) A.B.C.D/M", +ALIAS (show_bgp_view_ipv4_safi_rsclient_prefix, + show_bgp_ipv4_safi_rsclient_prefix_cmd, + "show bgp ipv4 (unicast|multicast) rsclient (A.B.C.D|X:X::X:X) A.B.C.D/M", SHOW_STR BGP_STR - "BGP view\n" - "BGP view name\n" "Address family\n" "Address Family modifier\n" "Address Family modifier\n" "Information about Route Server Client\n" NEIGHBOR_ADDR_STR "IP prefix /, e.g., 35.0.0.0/8\n") + +DEFUN (show_bgp_view_ipv6_neighbor_routes, + show_bgp_view_ipv6_neighbor_routes_cmd, + "show bgp view WORD ipv6 neighbors (A.B.C.D|X:X::X:X) routes", + SHOW_STR + BGP_STR + "BGP view\n" + "View name\n" + "Address family\n" + "Detailed information on TCP and BGP neighbor connections\n" + "Neighbor to display information about\n" + "Neighbor to display information about\n" + "Display routes learned from neighbor\n") { - struct bgp *bgp; struct peer *peer; - safi_t safi; - /* BGP structure lookup. */ - if (argc == 4) - { - bgp = bgp_lookup_by_name (argv[0]); - if (bgp == NULL) - { - vty_out (vty, "Can't find BGP view %s%s", argv[0], VTY_NEWLINE); - return CMD_WARNING; - } - } + if (argc == 2) + peer = peer_lookup_in_view (vty, argv[0], argv[1]); else - { - bgp = bgp_get_default (); - if (bgp == NULL) - { - vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE); - return CMD_WARNING; - } - } - - if (argc == 4) { - peer = peer_lookup_in_view (vty, argv[0], argv[2]); - safi = (strncmp (argv[1], "m", 1) == 0) ? SAFI_MULTICAST : SAFI_UNICAST; - } else { - peer = peer_lookup_in_view (vty, NULL, argv[1]); - safi = (strncmp (argv[0], "m", 1) == 0) ? SAFI_MULTICAST : SAFI_UNICAST; - } - + peer = peer_lookup_in_view (vty, NULL, argv[0]); + if (! peer) return CMD_WARNING; - if (! peer->afc[AFI_IP][safi]) - { - vty_out (vty, "%% Activate the neighbor for the address family first%s", - VTY_NEWLINE); - return CMD_WARNING; + return bgp_show_neighbor_route (vty, peer, AFI_IP6, SAFI_UNICAST, + bgp_show_type_neighbor); } - if ( ! CHECK_FLAG (peer->af_flags[AFI_IP][safi], - PEER_FLAG_RSERVER_CLIENT)) +DEFUN (show_bgp_view_neighbor_damp, + show_bgp_view_neighbor_damp_cmd, + "show bgp view WORD neighbors (A.B.C.D|X:X::X:X) dampened-routes", + SHOW_STR + BGP_STR + "BGP view\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" + "Display the dampened routes received from neighbor\n") { - vty_out (vty, "%% Neighbor is not a Route-Server client%s", - VTY_NEWLINE); + struct peer *peer; + + if (argc == 2) + peer = peer_lookup_in_view (vty, argv[0], argv[1]); + else + peer = peer_lookup_in_view (vty, NULL, argv[0]); + + if (! peer) return CMD_WARNING; - } - return bgp_show_route_in_table (vty, bgp, peer->rib[AFI_IP][safi], - (argc == 4) ? argv[3] : argv[2], - AFI_IP, safi, NULL, 1); + return bgp_show_neighbor_route (vty, peer, AFI_IP6, SAFI_UNICAST, + bgp_show_type_damp_neighbor); } -ALIAS (show_bgp_view_ipv4_safi_rsclient_prefix, - show_bgp_ipv4_safi_rsclient_prefix_cmd, - "show bgp ipv4 (unicast|multicast) rsclient (A.B.C.D|X:X::X:X) A.B.C.D/M", - SHOW_STR - BGP_STR - "Address family\n" - "Address Family modifier\n" - "Address Family modifier\n" - "Information about Route Server Client\n" - NEIGHBOR_ADDR_STR - "IP prefix /, e.g., 35.0.0.0/8\n") - -#ifdef HAVE_IPV6 -DEFUN (show_bgp_view_neighbor_routes, - show_bgp_view_neighbor_routes_cmd, - "show bgp view WORD neighbors (A.B.C.D|X:X::X:X) routes", +DEFUN (show_bgp_view_ipv6_neighbor_damp, + show_bgp_view_ipv6_neighbor_damp_cmd, + "show bgp view WORD ipv6 neighbors (A.B.C.D|X:X::X:X) dampened-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" "Neighbor to display information about\n" - "Display routes learned from neighbor\n") + "Display the dampened routes received from neighbor\n") { struct peer *peer; @@ -10900,38 +15222,52 @@ DEFUN (show_bgp_view_neighbor_routes, peer = peer_lookup_in_view (vty, argv[0], argv[1]); else peer = peer_lookup_in_view (vty, NULL, argv[0]); - + if (! peer) return CMD_WARNING; return bgp_show_neighbor_route (vty, peer, AFI_IP6, SAFI_UNICAST, - bgp_show_type_neighbor); + bgp_show_type_damp_neighbor); } -ALIAS (show_bgp_view_neighbor_routes, - show_bgp_view_ipv6_neighbor_routes_cmd, - "show bgp view WORD ipv6 neighbors (A.B.C.D|X:X::X:X) routes", +DEFUN (show_bgp_view_ipv6_neighbor_flap, + show_bgp_view_ipv6_neighbor_flap_cmd, + "show bgp view WORD ipv6 neighbors (A.B.C.D|X:X::X:X) flap-statistics", 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" "Neighbor to display information about\n" - "Display routes learned from neighbor\n") + "Display the dampened routes received from neighbor\n") +{ + struct peer *peer; -DEFUN (show_bgp_view_neighbor_damp, - show_bgp_view_neighbor_damp_cmd, - "show bgp view WORD neighbors (A.B.C.D|X:X::X:X) dampened-routes", + if (argc == 2) + peer = peer_lookup_in_view (vty, argv[0], argv[1]); + else + peer = peer_lookup_in_view (vty, NULL, argv[0]); + + if (! peer) + return CMD_WARNING; + + return bgp_show_neighbor_route (vty, peer, AFI_IP6, SAFI_UNICAST, + bgp_show_type_flap_neighbor); +} + +DEFUN (show_bgp_view_neighbor_flap, + show_bgp_view_neighbor_flap_cmd, + "show bgp view WORD neighbors (A.B.C.D|X:X::X:X) flap-statistics", 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" - "Display the dampened routes received from neighbor\n") + "Display flap statistics of the routes learned from neighbor\n") { struct peer *peer; @@ -10944,33 +15280,40 @@ DEFUN (show_bgp_view_neighbor_damp, return CMD_WARNING; return bgp_show_neighbor_route (vty, peer, AFI_IP6, SAFI_UNICAST, - bgp_show_type_damp_neighbor); + bgp_show_type_flap_neighbor); } +ALIAS (show_bgp_view_neighbor_flap, + show_bgp_neighbor_flap_cmd, + "show bgp neighbors (A.B.C.D|X:X::X:X) flap-statistics", + SHOW_STR + BGP_STR + "Detailed information on TCP and BGP neighbor connections\n" + "Neighbor to display information about\n" + "Neighbor to display information about\n" + "Display flap statistics of the routes learned from neighbor\n") + ALIAS (show_bgp_view_neighbor_damp, - show_bgp_view_ipv6_neighbor_damp_cmd, - "show bgp view WORD ipv6 neighbors (A.B.C.D|X:X::X:X) dampened-routes", + show_bgp_neighbor_damp_cmd, + "show bgp neighbors (A.B.C.D|X:X::X:X) dampened-routes", SHOW_STR BGP_STR - "BGP view\n" - "BGP view name\n" - "Address family\n" "Detailed information on TCP and BGP neighbor connections\n" "Neighbor to display information about\n" "Neighbor to display information about\n" "Display the dampened routes received from neighbor\n") -DEFUN (show_bgp_view_neighbor_flap, - show_bgp_view_neighbor_flap_cmd, - "show bgp view WORD neighbors (A.B.C.D|X:X::X:X) flap-statistics", +DEFUN (show_bgp_view_neighbor_routes, + show_bgp_view_neighbor_routes_cmd, + "show bgp view WORD neighbors (A.B.C.D|X:X::X:X) 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" - "Display flap statistics of the routes learned from neighbor\n") + "Display routes learned from neighbor\n") { struct peer *peer; @@ -10978,27 +15321,14 @@ DEFUN (show_bgp_view_neighbor_flap, peer = peer_lookup_in_view (vty, argv[0], argv[1]); else peer = peer_lookup_in_view (vty, NULL, argv[0]); - + if (! peer) return CMD_WARNING; return bgp_show_neighbor_route (vty, peer, AFI_IP6, SAFI_UNICAST, - bgp_show_type_flap_neighbor); + bgp_show_type_neighbor); } -ALIAS (show_bgp_view_neighbor_flap, - show_bgp_view_ipv6_neighbor_flap_cmd, - "show bgp view WORD ipv6 neighbors (A.B.C.D|X:X::X:X) flap-statistics", - SHOW_STR - BGP_STR - "BGP view\n" - "BGP view name\n" - "Address family\n" - "Detailed information on TCP and BGP neighbor connections\n" - "Neighbor to display information about\n" - "Neighbor to display information about\n" - "Display flap statistics of the routes learned from neighbor\n") - ALIAS (show_bgp_view_neighbor_routes, show_bgp_neighbor_routes_cmd, "show bgp neighbors (A.B.C.D|X:X::X:X) routes", @@ -11009,7 +15339,6 @@ ALIAS (show_bgp_view_neighbor_routes, "Neighbor to display information about\n" "Display routes learned from neighbor\n") - ALIAS (show_bgp_view_neighbor_routes, show_bgp_ipv6_neighbor_routes_cmd, "show bgp ipv6 neighbors (A.B.C.D|X:X::X:X) routes", @@ -11055,16 +15384,6 @@ DEFUN (ipv6_mbgp_neighbor_routes, bgp_show_type_neighbor); } -ALIAS (show_bgp_view_neighbor_flap, - show_bgp_neighbor_flap_cmd, - "show bgp neighbors (A.B.C.D|X:X::X:X) flap-statistics", - SHOW_STR - BGP_STR - "Detailed information on TCP and BGP neighbor connections\n" - "Neighbor to display information about\n" - "Neighbor to display information about\n" - "Display flap statistics of the routes learned from neighbor\n") - ALIAS (show_bgp_view_neighbor_flap, show_bgp_ipv6_neighbor_flap_cmd, "show bgp ipv6 neighbors (A.B.C.D|X:X::X:X) flap-statistics", @@ -11076,16 +15395,6 @@ ALIAS (show_bgp_view_neighbor_flap, "Neighbor to display information about\n" "Display flap statistics of the routes learned from neighbor\n") -ALIAS (show_bgp_view_neighbor_damp, - show_bgp_neighbor_damp_cmd, - "show bgp neighbors (A.B.C.D|X:X::X:X) dampened-routes", - SHOW_STR - BGP_STR - "Detailed information on TCP and BGP neighbor connections\n" - "Neighbor to display information about\n" - "Neighbor to display information about\n" - "Display the dampened routes received from neighbor\n") - ALIAS (show_bgp_view_neighbor_damp, show_bgp_ipv6_neighbor_damp_cmd, "show bgp ipv6 neighbors (A.B.C.D|X:X::X:X) dampened-routes", @@ -11103,7 +15412,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) { @@ -11146,13 +15455,114 @@ ALIAS (show_bgp_view_rsclient, "Information about Route Server Client\n" NEIGHBOR_ADDR_STR) +DEFUN (show_bgp_view_ipv4_rsclient, + show_bgp_view_ipv4_rsclient_cmd, + "show bgp view WORD ipv4 rsclient (A.B.C.D|X:X::X:X)", + SHOW_STR + BGP_STR + "BGP view\n" + "View name\n" + "Address Family\n" + "Information about Route Server Client\n" + NEIGHBOR_ADDR_STR2) +{ + struct bgp_table *table; + struct peer *peer; + + if (argc == 2) + peer = peer_lookup_in_view (vty, argv[0], argv[1]); + else + peer = peer_lookup_in_view (vty, NULL, argv[0]); + + if (! peer) + return CMD_WARNING; + + if (! peer->afc[AFI_IP][SAFI_UNICAST]) + { + vty_out (vty, "%% Activate the neighbor for the address family first%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + if ( ! CHECK_FLAG (peer->af_flags[AFI_IP][SAFI_UNICAST], + PEER_FLAG_RSERVER_CLIENT)) + { + vty_out (vty, "%% Neighbor is not a Route-Server client%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + table = peer->rib[AFI_IP][SAFI_UNICAST]; + + return bgp_show_table (vty, table, &peer->remote_id, bgp_show_type_normal, NULL); +} +DEFUN (show_bgp_view_ipv6_rsclient, + show_bgp_view_ipv6_rsclient_cmd, + "show bgp view WORD ipv6 rsclient (A.B.C.D|X:X::X:X)", + SHOW_STR + BGP_STR + "BGP view\n" + "BGP view name\n" + "Address Family\n" + "Information about Route Server Client\n" + NEIGHBOR_ADDR_STR2) +{ + struct bgp_table *table; + struct peer *peer; + + if (argc == 2) + peer = peer_lookup_in_view (vty, argv[0], argv[1]); + else + peer = peer_lookup_in_view (vty, NULL, argv[0]); + + if (! peer) + return CMD_WARNING; + + if (! peer->afc[AFI_IP6][SAFI_UNICAST]) + { + vty_out (vty, "%% Activate the neighbor for the address family first%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + if ( ! CHECK_FLAG (peer->af_flags[AFI_IP6][SAFI_UNICAST], + PEER_FLAG_RSERVER_CLIENT)) + { + vty_out (vty, "%% Neighbor is not a Route-Server client%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + table = peer->rib[AFI_IP6][SAFI_UNICAST]; + + return bgp_show_table (vty, table, &peer->remote_id, bgp_show_type_normal, NULL); +} + +ALIAS (show_bgp_view_ipv4_rsclient, + show_bgp_ipv4_rsclient_cmd, + "show bgp ipv4 rsclient (A.B.C.D|X:X::X:X)", + SHOW_STR + BGP_STR + "Address Family\n" + "Information about Route Server Client\n" + NEIGHBOR_ADDR_STR2) + +ALIAS (show_bgp_view_ipv6_rsclient, + show_bgp_ipv6_rsclient_cmd, + "show bgp ipv6 rsclient (A.B.C.D|X:X::X:X)", + SHOW_STR + BGP_STR + "Address Family\n" + "Information about Route Server Client\n" + NEIGHBOR_ADDR_STR2) + DEFUN (show_bgp_view_ipv6_safi_rsclient, show_bgp_view_ipv6_safi_rsclient_cmd, "show bgp view WORD ipv6 (unicast|multicast) rsclient (A.B.C.D|X:X::X:X)", 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" @@ -11211,7 +15621,70 @@ DEFUN (show_bgp_view_rsclient_route, SHOW_STR BGP_STR "BGP view\n" + "View name\n" + "Information about Route Server Client\n" + NEIGHBOR_ADDR_STR + "Network in the BGP routing table to display\n") +{ + struct bgp *bgp; + struct peer *peer; + + /* BGP structure lookup. */ + if (argc == 3) + { + bgp = bgp_lookup_by_name (argv[0]); + if (bgp == NULL) + { + vty_out (vty, "Can't find BGP view %s%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + } + else + { + bgp = bgp_get_default (); + if (bgp == NULL) + { + vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + } + + if (argc == 3) + peer = peer_lookup_in_view (vty, argv[0], argv[1]); + else + peer = peer_lookup_in_view (vty, NULL, argv[0]); + + if (! peer) + return CMD_WARNING; + + if (! peer->afc[AFI_IP6][SAFI_UNICAST]) + { + vty_out (vty, "%% Activate the neighbor for the address family first%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + if ( ! CHECK_FLAG (peer->af_flags[AFI_IP6][SAFI_UNICAST], + PEER_FLAG_RSERVER_CLIENT)) + { + vty_out (vty, "%% Neighbor is not a Route-Server client%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + return bgp_show_route_in_table (vty, bgp, peer->rib[AFI_IP6][SAFI_UNICAST], + (argc == 3) ? argv[2] : argv[1], + AFI_IP6, SAFI_UNICAST, NULL, 0, BGP_PATH_ALL); +} + +DEFUN (show_bgp_view_ipv6_rsclient_route, + show_bgp_view_ipv6_rsclient_route_cmd, + "show bgp view WORD ipv6 rsclient (A.B.C.D|X:X::X:X) X:X::X:X", + SHOW_STR + BGP_STR + "BGP view\n" "BGP view name\n" + "IP6_STR" "Information about Route Server Client\n" NEIGHBOR_ADDR_STR "Network in the BGP routing table to display\n") @@ -11264,10 +15737,10 @@ DEFUN (show_bgp_view_rsclient_route, return bgp_show_route_in_table (vty, bgp, peer->rib[AFI_IP6][SAFI_UNICAST], (argc == 3) ? argv[2] : argv[1], - AFI_IP6, SAFI_UNICAST, NULL, 0); + AFI_IP6, SAFI_UNICAST, NULL, 0, BGP_PATH_ALL); } -ALIAS (show_bgp_view_rsclient_route, +ALIAS (show_bgp_view_ipv6_rsclient_route, show_bgp_rsclient_route_cmd, "show bgp rsclient (A.B.C.D|X:X::X:X) X:X::X:X", SHOW_STR @@ -11276,13 +15749,23 @@ ALIAS (show_bgp_view_rsclient_route, NEIGHBOR_ADDR_STR "Network in the BGP routing table to display\n") +ALIAS (show_bgp_view_ipv6_rsclient_route, + show_bgp_ipv6_rsclient_route_cmd, + "show bgp ipv6 rsclient (A.B.C.D|X:X::X:X) X:X::X:X", + SHOW_STR + BGP_STR + IP6_STR + "Information about Route Server Client\n" + NEIGHBOR_ADDR_STR + "Network in the BGP routing table to display\n") + DEFUN (show_bgp_view_ipv6_safi_rsclient_route, show_bgp_view_ipv6_safi_rsclient_route_cmd, "show bgp view WORD ipv6 (unicast|multicast) rsclient (A.B.C.D|X:X::X:X) X:X::X:X", 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" @@ -11325,14 +15808,89 @@ DEFUN (show_bgp_view_ipv6_safi_rsclient_route, if (! peer) return CMD_WARNING; - if (! peer->afc[AFI_IP6][safi]) + if (! peer->afc[AFI_IP6][safi]) + { + vty_out (vty, "%% Activate the neighbor for the address family first%s", + VTY_NEWLINE); + return CMD_WARNING; +} + + if ( ! CHECK_FLAG (peer->af_flags[AFI_IP6][safi], + PEER_FLAG_RSERVER_CLIENT)) + { + vty_out (vty, "%% Neighbor is not a Route-Server client%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + return bgp_show_route_in_table (vty, bgp, peer->rib[AFI_IP6][safi], + (argc == 4) ? argv[3] : argv[2], + AFI_IP6, safi, NULL, 0, BGP_PATH_ALL); +} + +ALIAS (show_bgp_view_ipv6_safi_rsclient_route, + show_bgp_ipv6_safi_rsclient_route_cmd, + "show bgp ipv6 (unicast|multicast) rsclient (A.B.C.D|X:X::X:X) X:X::X:X", + SHOW_STR + BGP_STR + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Information about Route Server Client\n" + NEIGHBOR_ADDR_STR + "Network in the BGP routing table to display\n") + + +DEFUN (show_bgp_view_rsclient_prefix, + show_bgp_view_rsclient_prefix_cmd, + "show bgp view WORD rsclient (A.B.C.D|X:X::X:X) X:X::X:X/M", + SHOW_STR + BGP_STR + "BGP view\n" + "View name\n" + "Information about Route Server Client\n" + NEIGHBOR_ADDR_STR + "IPv6 prefix /, e.g., 3ffe::/16\n") +{ + struct bgp *bgp; + struct peer *peer; + + /* BGP structure lookup. */ + if (argc == 3) + { + bgp = bgp_lookup_by_name (argv[0]); + if (bgp == NULL) + { + vty_out (vty, "Can't find BGP view %s%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + } + else + { + bgp = bgp_get_default (); + if (bgp == NULL) + { + vty_out (vty, "No BGP process is configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + } + + if (argc == 3) + peer = peer_lookup_in_view (vty, argv[0], argv[1]); + else + peer = peer_lookup_in_view (vty, NULL, argv[0]); + + if (! peer) + return CMD_WARNING; + + if (! peer->afc[AFI_IP6][SAFI_UNICAST]) { vty_out (vty, "%% Activate the neighbor for the address family first%s", VTY_NEWLINE); return CMD_WARNING; -} + } - if ( ! CHECK_FLAG (peer->af_flags[AFI_IP6][safi], + if ( ! CHECK_FLAG (peer->af_flags[AFI_IP6][SAFI_UNICAST], PEER_FLAG_RSERVER_CLIENT)) { vty_out (vty, "%% Neighbor is not a Route-Server client%s", @@ -11340,30 +15898,19 @@ DEFUN (show_bgp_view_ipv6_safi_rsclient_route, return CMD_WARNING; } - return bgp_show_route_in_table (vty, bgp, peer->rib[AFI_IP6][safi], - (argc == 4) ? argv[3] : argv[2], - AFI_IP6, safi, NULL, 0); + return bgp_show_route_in_table (vty, bgp, peer->rib[AFI_IP6][SAFI_UNICAST], + (argc == 3) ? argv[2] : argv[1], + AFI_IP6, SAFI_UNICAST, NULL, 1, BGP_PATH_ALL); } -ALIAS (show_bgp_view_ipv6_safi_rsclient_route, - show_bgp_ipv6_safi_rsclient_route_cmd, - "show bgp ipv6 (unicast|multicast) rsclient (A.B.C.D|X:X::X:X) X:X::X:X", - SHOW_STR - BGP_STR - "Address family\n" - "Address Family modifier\n" - "Address Family modifier\n" - "Information about Route Server Client\n" - NEIGHBOR_ADDR_STR - "Network in the BGP routing table to display\n") - -DEFUN (show_bgp_view_rsclient_prefix, - show_bgp_view_rsclient_prefix_cmd, - "show bgp view WORD rsclient (A.B.C.D|X:X::X:X) X:X::X:X/M", +DEFUN (show_bgp_view_ipv6_rsclient_prefix, + show_bgp_view_ipv6_rsclient_prefix_cmd, + "show bgp view WORD ipv6 rsclient (A.B.C.D|X:X::X:X) X:X::X:X/M", SHOW_STR BGP_STR "BGP view\n" - "BGP view name\n" + "View name\n" + IP6_STR "Information about Route Server Client\n" NEIGHBOR_ADDR_STR "IPv6 prefix /, e.g., 3ffe::/16\n") @@ -11416,10 +15963,10 @@ DEFUN (show_bgp_view_rsclient_prefix, return bgp_show_route_in_table (vty, bgp, peer->rib[AFI_IP6][SAFI_UNICAST], (argc == 3) ? argv[2] : argv[1], - AFI_IP6, SAFI_UNICAST, NULL, 1); + AFI_IP6, SAFI_UNICAST, NULL, 1, BGP_PATH_ALL); } -ALIAS (show_bgp_view_rsclient_prefix, +ALIAS (show_bgp_view_ipv6_rsclient_prefix, show_bgp_rsclient_prefix_cmd, "show bgp rsclient (A.B.C.D|X:X::X:X) X:X::X:X/M", SHOW_STR @@ -11428,13 +15975,22 @@ ALIAS (show_bgp_view_rsclient_prefix, NEIGHBOR_ADDR_STR "IPv6 prefix /, e.g., 3ffe::/16\n") +ALIAS (show_bgp_view_ipv6_rsclient_prefix, + show_bgp_ipv6_rsclient_prefix_cmd, + "show bgp ipv6 rsclient (A.B.C.D|X:X::X:X) X:X::X:X/M", + SHOW_STR + BGP_STR + "Information about Route Server Client\n" + NEIGHBOR_ADDR_STR + "IPv6 prefix /, e.g., 3ffe::/16\n") + DEFUN (show_bgp_view_ipv6_safi_rsclient_prefix, show_bgp_view_ipv6_safi_rsclient_prefix_cmd, "show bgp view WORD ipv6 (unicast|multicast) rsclient (A.B.C.D|X:X::X:X) X:X::X:X/M", 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" @@ -11494,7 +16050,7 @@ DEFUN (show_bgp_view_ipv6_safi_rsclient_prefix, return bgp_show_route_in_table (vty, bgp, peer->rib[AFI_IP6][safi], (argc == 4) ? argv[3] : argv[2], - AFI_IP6, safi, NULL, 1); + AFI_IP6, safi, NULL, 1, BGP_PATH_ALL); } ALIAS (show_bgp_view_ipv6_safi_rsclient_prefix, @@ -11509,8 +16065,6 @@ ALIAS (show_bgp_view_ipv6_safi_rsclient_prefix, NEIGHBOR_ADDR_STR "IP prefix /, e.g., 3ffe::/16\n") -#endif /* HAVE_IPV6 */ - struct bgp_table *bgp_distance_table; struct bgp_distance @@ -11539,12 +16093,12 @@ bgp_distance_set (struct vty *vty, const char *distance_str, const char *ip_str, const char *access_list_str) { int ret; - struct prefix_ipv4 p; + struct prefix p; u_char distance; struct bgp_node *rn; struct bgp_distance *bdistance; - ret = str2prefix_ipv4 (ip_str, &p); + ret = str2prefix (ip_str, &p); if (ret == 0) { vty_out (vty, "Malformed prefix%s", VTY_NEWLINE); @@ -11586,12 +16140,12 @@ bgp_distance_unset (struct vty *vty, const char *distance_str, const char *ip_str, const char *access_list_str) { int ret; - struct prefix_ipv4 p; + struct prefix p; u_char distance; struct bgp_node *rn; struct bgp_distance *bdistance; - ret = str2prefix_ipv4 (ip_str, &p); + ret = str2prefix (ip_str, &p); if (ret == 0) { vty_out (vty, "Malformed prefix%s", VTY_NEWLINE); @@ -11608,7 +16162,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); @@ -11680,7 +16240,7 @@ bgp_distance_apply (struct prefix *p, struct bgp_info *rinfo, struct bgp *bgp) } } - if (peer_sort (peer) == BGP_PEER_EBGP) + if (peer->sort == BGP_PEER_EBGP) { if (bgp->distance_ebgp) return bgp->distance_ebgp; @@ -11694,6 +16254,81 @@ bgp_distance_apply (struct prefix *p, struct bgp_info *rinfo, struct bgp *bgp) } } +#ifdef HAVE_IPV6 +/* Apply BGP information to ipv6 distance method. */ +u_char +ipv6_bgp_distance_apply (struct prefix *p, struct bgp_info *rinfo, struct bgp *bgp) +{ + struct bgp_node *rn; + struct prefix_ipv6 q; + struct peer *peer; + struct bgp_distance *bdistance; + struct access_list *alist; + struct bgp_static *bgp_static; + + if (! bgp) + return 0; + + if (p->family != AF_INET6) + return 0; + + peer = rinfo->peer; + + if (peer->su.sa.sa_family != AF_INET6) + return 0; + + memset (&q, 0, sizeof (struct prefix_ipv6)); + q.family = AF_INET; + q.prefix = peer->su.sin6.sin6_addr; + q.prefixlen = IPV6_MAX_BITLEN; + + /* Check source address. */ + rn = bgp_node_match (bgp_distance_table, (struct prefix *) &q); + if (rn) + { + bdistance = rn->info; + bgp_unlock_node (rn); + + if (bdistance->access_list) + { + alist = access_list_lookup (AFI_IP6, bdistance->access_list); + if (alist && access_list_apply (alist, p) == FILTER_PERMIT) + return bdistance->distance; + } + else + return bdistance->distance; + } + /* Backdoor check. */ + rn = bgp_node_lookup (bgp->route[AFI_IP6][SAFI_UNICAST], p); + if (rn) + { + bgp_static = rn->info; + bgp_unlock_node (rn); + + if (bgp_static->backdoor) + { + if (bgp->ipv6_distance_local) + return bgp->ipv6_distance_local; + else + return ZEBRA_IBGP_DISTANCE_DEFAULT; + } + } + + if (peer_sort (peer) == BGP_PEER_EBGP) + { + if (bgp->ipv6_distance_ebgp) + return bgp->ipv6_distance_ebgp; + return ZEBRA_EBGP_DISTANCE_DEFAULT; + } + else + { + if (bgp->ipv6_distance_ibgp) + return bgp->ipv6_distance_ibgp; + return ZEBRA_IBGP_DISTANCE_DEFAULT; + } +} +#endif /* HAVE_IPV6 */ + DEFUN (bgp_distance, bgp_distance_cmd, "distance bgp <1-255> <1-255> <1-255>", @@ -11787,7 +16422,103 @@ DEFUN (no_bgp_distance_source_access_list, bgp_distance_unset (vty, argv[0], argv[1], argv[2]); return CMD_SUCCESS; } - + +#ifdef HAVE_IPV6 +DEFUN (ipv6_bgp_distance, + ipv6_bgp_distance_cmd, + "distance bgp <1-255> <1-255> <1-255>", + "Define an administrative distance\n" + "BGP distance\n" + "Distance for routes external to the AS\n" + "Distance for routes internal to the AS\n" + "Distance for local routes\n") +{ + struct bgp *bgp; + + bgp = vty->index; + + bgp->ipv6_distance_ebgp = atoi (argv[0]); + bgp->ipv6_distance_ibgp = atoi (argv[1]); + bgp->ipv6_distance_local = atoi (argv[2]); + return CMD_SUCCESS; +} + +DEFUN (no_ipv6_bgp_distance, + no_ipv6_bgp_distance_cmd, + "no distance bgp <1-255> <1-255> <1-255>", + NO_STR + "Define an administrative distance\n" + "BGP distance\n" + "Distance for routes external to the AS\n" + "Distance for routes internal to the AS\n" + "Distance for local routes\n") +{ + struct bgp *bgp; + + bgp = vty->index; + + bgp->ipv6_distance_ebgp= 0; + bgp->ipv6_distance_ibgp = 0; + bgp->ipv6_distance_local = 0; + return CMD_SUCCESS; +} + +ALIAS (no_ipv6_bgp_distance, + no_ipv6_bgp_distance2_cmd, + "no distance bgp", + NO_STR + "Define an administrative distance\n" + "BGP distance\n") + +DEFUN (ipv6_bgp_distance_source, + ipv6_bgp_distance_source_cmd, + "distance <1-255> X:X::X:X/M", + "Define an administrative distance\n" + "Administrative distance\n" + "IP source prefix\n") +{ + bgp_distance_set (vty, argv[0], argv[1], NULL); + return CMD_SUCCESS; +} + +DEFUN (no_ipv6_bgp_distance_source, + no_ipv6_bgp_distance_source_cmd, + "no distance <1-255> X:X::X:X/M", + NO_STR + "Define an administrative distance\n" + "Administrative distance\n" + "IP source prefix\n") +{ + bgp_distance_unset (vty, argv[0], argv[1], NULL); + return CMD_SUCCESS; +} + +DEFUN (ipv6_bgp_distance_source_access_list, + ipv6_bgp_distance_source_access_list_cmd, + "distance <1-255> X:X::X:X/M WORD", + "Define an administrative distance\n" + "Administrative distance\n" + "IP source prefix\n" + "Access list name\n") +{ + bgp_distance_set (vty, argv[0], argv[1], argv[2]); + return CMD_SUCCESS; +} + +DEFUN (no_ipv6_bgp_distance_source_access_list, + no_ipv6_bgp_distance_source_access_list_cmd, + "no distance <1-255> X:X::X:X/M WORD", + NO_STR + "Define an administrative distance\n" + "Administrative distance\n" + "IP source prefix\n" + "Access list name\n") +{ + bgp_distance_unset (vty, argv[0], argv[1], argv[2]); + return CMD_SUCCESS; +} +#endif + DEFUN (bgp_damp_set, bgp_damp_set_cmd, "bgp dampening <1-45> <1-20000> <1-20000> <1-255>", @@ -11818,6 +16549,14 @@ DEFUN (bgp_damp_set, } bgp = vty->index; + + if (suppress < reuse) + { + vty_out (vty, "Suppress value cannot be less than reuse value %s", + VTY_NEWLINE); + return 0; + } + return bgp_damp_enable (bgp, bgp_node_afi (vty), bgp_node_safi (vty), half, reuse, suppress, max); } @@ -11844,45 +16583,199 @@ DEFUN (bgp_damp_unset, { struct bgp *bgp; - bgp = vty->index; - return bgp_damp_disable (bgp, bgp_node_afi (vty), bgp_node_safi (vty)); -} + bgp = vty->index; + return bgp_damp_disable (bgp, bgp_node_afi (vty), bgp_node_safi (vty)); +} + +ALIAS (bgp_damp_unset, + bgp_damp_unset2_cmd, + "no bgp dampening <1-45> <1-20000> <1-20000> <1-255>", + NO_STR + "BGP Specific commands\n" + "Enable route-flap dampening\n" + "Half-life time for the penalty\n" + "Value to start reusing a route\n" + "Value to start suppressing a route\n" + "Maximum duration to suppress a stable route\n") + +DEFUN (show_ip_bgp_dampened_paths, + show_ip_bgp_dampened_paths_cmd, + "show ip bgp dampened-paths", + SHOW_STR + IP_STR + BGP_STR + "Display paths suppressed due to dampening\n") +{ + return bgp_show (vty, NULL, AFI_IP, SAFI_UNICAST, bgp_show_type_dampend_paths, + NULL); +} + +ALIAS (show_ip_bgp_dampened_paths, + show_ip_bgp_damp_dampened_paths_cmd, + "show ip bgp dampening dampened-paths", + SHOW_STR + IP_STR + BGP_STR + "Display detailed information about dampening\n" + "Display paths suppressed due to dampening\n") + +DEFUN (show_ip_bgp_flap_statistics, + show_ip_bgp_flap_statistics_cmd, + "show ip bgp flap-statistics", + SHOW_STR + IP_STR + BGP_STR + "Display flap statistics of routes\n") +{ + return bgp_show (vty, NULL, AFI_IP, SAFI_UNICAST, + bgp_show_type_flap_statistics, NULL); +} + +ALIAS (show_ip_bgp_flap_statistics, + show_ip_bgp_damp_flap_statistics_cmd, + "show ip bgp dampening flap-statistics", + SHOW_STR + IP_STR + BGP_STR + "Display detailed information about dampening\n" + "Display flap statistics of routes\n") + +DEFUN (show_bgp_ipv4_safi_dampened_paths, + show_bgp_ipv4_safi_dampened_paths_cmd, + "show bgp ipv4 (encap|multicast|unicast|vpn) dampened-paths", + SHOW_STR + BGP_STR + IP_STR + "Address Family modifier\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Display paths suppressed due to dampening\n") +{ + safi_t safi; + + if (bgp_parse_safi(argv[0], &safi)) { + vty_out (vty, "Error: Bad SAFI: %s%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + + return bgp_show (vty, NULL, AFI_IP, safi, bgp_show_type_dampend_paths, NULL); +} +ALIAS (show_bgp_ipv4_safi_dampened_paths, + show_bgp_ipv4_safi_damp_dampened_paths_cmd, + "show bgp ipv4 (encap|multicast|unicast|vpn) dampening dampened-paths", + SHOW_STR + BGP_STR + IP_STR + "Address Family modifier\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Display detailed information about dampening\n" + "Display paths suppressed due to dampening\n") + +DEFUN (show_bgp_ipv6_safi_dampened_paths, + show_bgp_ipv6_safi_dampened_paths_cmd, + "show bgp ipv6 (encap|multicast|unicast|vpn) dampened-paths", + SHOW_STR + BGP_STR + IPV6_STR + "Address Family modifier\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Display paths suppressed due to dampening\n") +{ + safi_t safi; -ALIAS (bgp_damp_unset, - bgp_damp_unset2_cmd, - "no bgp dampening <1-45> <1-20000> <1-20000> <1-255>", - NO_STR - "BGP Specific commands\n" - "Enable route-flap dampening\n" - "Half-life time for the penalty\n" - "Value to start reusing a route\n" - "Value to start suppressing a route\n" - "Maximum duration to suppress a stable route\n") + if (bgp_parse_safi(argv[0], &safi)) { + vty_out (vty, "Error: Bad SAFI: %s%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } -DEFUN (show_ip_bgp_dampened_paths, - show_ip_bgp_dampened_paths_cmd, - "show ip bgp dampened-paths", + return bgp_show (vty, NULL, AFI_IP6, safi, bgp_show_type_dampend_paths, NULL); +} +ALIAS (show_bgp_ipv6_safi_dampened_paths, + show_bgp_ipv6_safi_damp_dampened_paths_cmd, + "show bgp ipv6 (encap|multicast|unicast|vpn) dampening dampened-paths", SHOW_STR - IP_STR BGP_STR + IPV6_STR + "Address Family modifier\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Display detailed information about dampening\n" "Display paths suppressed due to dampening\n") + +DEFUN (show_bgp_ipv4_safi_flap_statistics, + show_bgp_ipv4_safi_flap_statistics_cmd, + "show bgp ipv4 (encap|multicast|unicast|vpn) flap-statistics", + SHOW_STR + BGP_STR + "Address Family\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Display flap statistics of routes\n") { - return bgp_show (vty, NULL, AFI_IP, SAFI_UNICAST, bgp_show_type_dampend_paths, - NULL); + safi_t safi; + + if (bgp_parse_safi(argv[0], &safi)) { + vty_out (vty, "Error: Bad SAFI: %s%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + + return bgp_show (vty, NULL, AFI_IP, safi, bgp_show_type_flap_statistics, NULL); } +ALIAS (show_bgp_ipv4_safi_flap_statistics, + show_bgp_ipv4_safi_damp_flap_statistics_cmd, + "show bgp ipv4 (encap|multicast|unicast|vpn) dampening flap-statistics", + SHOW_STR + BGP_STR + "Address Family\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Display detailed information about dampening\n" + "Display flap statistics of routes\n") -DEFUN (show_ip_bgp_flap_statistics, - show_ip_bgp_flap_statistics_cmd, - "show ip bgp flap-statistics", +DEFUN (show_bgp_ipv6_safi_flap_statistics, + show_bgp_ipv6_safi_flap_statistics_cmd, + "show bgp ipv6 (encap|multicast|unicast|vpn) flap-statistics", SHOW_STR - IP_STR BGP_STR + "Address Family\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Address Family modifier\n" "Display flap statistics of routes\n") { - return bgp_show (vty, NULL, AFI_IP, SAFI_UNICAST, - bgp_show_type_flap_statistics, NULL); + safi_t safi; + + if (bgp_parse_safi(argv[0], &safi)) { + vty_out (vty, "Error: Bad SAFI: %s%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + + return bgp_show (vty, NULL, AFI_IP6, safi, bgp_show_type_flap_statistics, NULL); } - +ALIAS (show_bgp_ipv6_safi_flap_statistics, + show_bgp_ipv6_safi_damp_flap_statistics_cmd, + "show bgp ipv6 (encap|multicast|unicast|vpn) dampening flap-statistics", + SHOW_STR + BGP_STR + "Address Family\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Display detailed information about dampening\n" + "Display flap statistics of routes\n") + /* Display specified route of BGP table. */ static int bgp_clear_damp_route (struct vty *vty, const char *view_name, @@ -11928,9 +16821,9 @@ bgp_clear_damp_route (struct vty *vty, const char *view_name, match.family = afi2family (afi); - if (safi == SAFI_MPLS_VPN) + if ((safi == SAFI_MPLS_VPN) || (safi == SAFI_ENCAP)) { - for (rn = bgp_table_top (bgp->rib[AFI_IP][SAFI_MPLS_VPN]); rn; rn = bgp_route_next (rn)) + for (rn = bgp_table_top (bgp->rib[AFI_IP][safi]); rn; rn = bgp_route_next (rn)) { if (prd && memcmp (rn->p.u.val, prd->val, 8) != 0) continue; @@ -12046,7 +16939,8 @@ DEFUN (clear_ip_bgp_dampening_address_mask, return bgp_clear_damp_route (vty, NULL, prefix_str, AFI_IP, SAFI_UNICAST, NULL, 0); } - + +/* also used for encap safi */ static int bgp_config_write_network_vpnv4 (struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi, int *write) @@ -12098,7 +16992,7 @@ bgp_config_write_network (struct vty *vty, struct bgp *bgp, struct bgp_aggregate *bgp_aggregate; char buf[SU_ADDRSTRLEN]; - if (afi == AFI_IP && safi == SAFI_MPLS_VPN) + if (afi == AFI_IP && ((safi == SAFI_MPLS_VPN) || (safi == SAFI_ENCAP))) return bgp_config_write_network_vpnv4 (vty, bgp, afi, safi, write); /* Network configuration. */ @@ -12187,30 +17081,59 @@ bgp_config_write_network (struct vty *vty, struct bgp *bgp, } int -bgp_config_write_distance (struct vty *vty, struct bgp *bgp) +bgp_config_write_distance (struct vty *vty, struct bgp *bgp, + afi_t afi, safi_t safi, int *write) { struct bgp_node *rn; struct bgp_distance *bdistance; - /* Distance configuration. */ - if (bgp->distance_ebgp - && bgp->distance_ibgp - && bgp->distance_local - && (bgp->distance_ebgp != ZEBRA_EBGP_DISTANCE_DEFAULT - || bgp->distance_ibgp != ZEBRA_IBGP_DISTANCE_DEFAULT - || bgp->distance_local != ZEBRA_IBGP_DISTANCE_DEFAULT)) - vty_out (vty, " distance bgp %d %d %d%s", - bgp->distance_ebgp, bgp->distance_ibgp, bgp->distance_local, - VTY_NEWLINE); - - for (rn = bgp_table_top (bgp_distance_table); rn; rn = bgp_route_next (rn)) - if ((bdistance = rn->info) != NULL) - { - vty_out (vty, " distance %d %s/%d %s%s", bdistance->distance, - inet_ntoa (rn->p.u.prefix4), rn->p.prefixlen, - bdistance->access_list ? bdistance->access_list : "", - VTY_NEWLINE); - } + if (afi == AFI_IP && safi == SAFI_UNICAST) + { + /* Distance configuration. */ + if (bgp->distance_ebgp + && bgp->distance_ibgp + && bgp->distance_local + && (bgp->distance_ebgp != ZEBRA_EBGP_DISTANCE_DEFAULT + || bgp->distance_ibgp != ZEBRA_IBGP_DISTANCE_DEFAULT + || bgp->distance_local != ZEBRA_IBGP_DISTANCE_DEFAULT)) + vty_out (vty, " distance bgp %d %d %d%s", + bgp->distance_ebgp, bgp->distance_ibgp, bgp->distance_local, + VTY_NEWLINE); + + for (rn = bgp_table_top (bgp_distance_table); rn; rn = bgp_route_next (rn)) + if ((bdistance = rn->info) != NULL) + { + vty_out (vty, " distance %d %s/%d %s%s", bdistance->distance, + inet_ntoa (rn->p.u.prefix4), rn->p.prefixlen, + bdistance->access_list ? bdistance->access_list : "", + VTY_NEWLINE); + } + } + +#ifdef HAVE_IPV6 + else if (afi == AFI_IP6 && safi == SAFI_UNICAST) + { + bgp_config_write_family_header (vty, afi, safi, write); + if (bgp->ipv6_distance_ebgp + && bgp->ipv6_distance_ibgp + && bgp->ipv6_distance_local + && (bgp->ipv6_distance_ebgp != ZEBRA_EBGP_DISTANCE_DEFAULT + || bgp->ipv6_distance_ibgp != ZEBRA_IBGP_DISTANCE_DEFAULT + || bgp->ipv6_distance_local != ZEBRA_IBGP_DISTANCE_DEFAULT)) + vty_out (vty, " distance bgp %d %d %d%s", + bgp->ipv6_distance_ebgp, bgp->ipv6_distance_ibgp, bgp->ipv6_distance_local, + VTY_NEWLINE); + + for (rn = bgp_table_top (bgp_distance_table); rn; rn = bgp_route_next (rn)) + if ((bdistance = rn->info) != NULL) + { + vty_out (vty, " distance %d %s/%d %s%s", bdistance->distance, + inet6_ntoa (rn->p.u.prefix6), rn->p.prefixlen, + bdistance->access_list ? bdistance->access_list : "", + VTY_NEWLINE); + } + } +#endif /* HAVE_IPV6 */ return 0; } @@ -12332,17 +17255,417 @@ bgp_route_init (void) install_element (BGP_IPV4M_NODE, &no_aggregate_address_mask_as_set_summary_cmd); install_element (BGP_IPV4M_NODE, &no_aggregate_address_mask_summary_as_set_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_safi_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_safi_route_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_vpn_route_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_vpn_route_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_vpn_rd_route_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_vpn_rd_route_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_encap_route_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_encap_route_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_safi_rd_route_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_safi_rd_route_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_prefix_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_safi_prefix_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_vpn_prefix_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_vpn_prefix_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_encap_prefix_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_encap_prefix_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_safi_rd_prefix_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_safi_rd_prefix_cmd); + install_element (VIEW_NODE, &show_bgp_afi_safi_view_cmd); + install_element (VIEW_NODE, &show_bgp_view_afi_safi_route_cmd); + install_element (VIEW_NODE, &show_bgp_view_afi_safi_prefix_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_safi_regexp_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_safi_regexp_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_prefix_list_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_safi_prefix_list_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_safi_prefix_list_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_filter_list_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_safi_filter_list_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_safi_filter_list_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_route_map_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_cidr_only_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_safi_cidr_only_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_community_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_community2_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_community3_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_community4_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_safi_community_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_safi_community2_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_safi_community3_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_safi_community4_cmd); + install_element (VIEW_NODE, &show_bgp_view_afi_safi_community_all_cmd); + install_element (VIEW_NODE, &show_bgp_view_afi_safi_community_cmd); + install_element (VIEW_NODE, &show_bgp_view_afi_safi_community2_cmd); + install_element (VIEW_NODE, &show_bgp_view_afi_safi_community3_cmd); + install_element (VIEW_NODE, &show_bgp_view_afi_safi_community4_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_community_exact_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_community2_exact_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_community3_exact_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_community4_exact_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_safi_community_exact_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_safi_community2_exact_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_safi_community3_exact_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_safi_community4_exact_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_community_list_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_safi_community_list_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_community_list_exact_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_safi_community_list_exact_cmd); + + /* large-communities */ + install_element (VIEW_NODE, &show_bgp_ipv4_lcommunity_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_lcommunity2_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_lcommunity3_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_lcommunity4_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_safi_lcommunity_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_safi_lcommunity2_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_safi_lcommunity3_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_safi_lcommunity4_cmd); + install_element (VIEW_NODE, &show_bgp_view_afi_safi_lcommunity_all_cmd); + install_element (VIEW_NODE, &show_bgp_view_afi_safi_lcommunity_cmd); + install_element (VIEW_NODE, &show_bgp_view_afi_safi_lcommunity2_cmd); + install_element (VIEW_NODE, &show_bgp_view_afi_safi_lcommunity3_cmd); + install_element (VIEW_NODE, &show_bgp_view_afi_safi_lcommunity4_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_lcommunity_list_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_safi_lcommunity_list_cmd); + + install_element (VIEW_NODE, &show_bgp_ipv4_prefix_longer_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_safi_prefix_longer_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_safi_prefix_longer_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_safi_neighbor_advertised_route_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_safi_neighbor_advertised_route_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_safi_neighbor_received_routes_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_safi_neighbor_received_routes_cmd); + install_element (VIEW_NODE, &show_bgp_view_afi_safi_neighbor_adv_recd_routes_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_safi_neighbor_routes_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_safi_neighbor_routes_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_safi_neighbor_received_prefix_filter_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_safi_neighbor_received_prefix_filter_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_safi_dampened_paths_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_safi_dampened_paths_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_safi_damp_dampened_paths_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_safi_damp_dampened_paths_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_safi_flap_statistics_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_safi_flap_statistics_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_safi_damp_flap_statistics_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_safi_damp_flap_statistics_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_safi_flap_address_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_flap_address_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_safi_damp_flap_address_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_safi_flap_prefix_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_safi_flap_prefix_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_safi_damp_flap_prefix_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_safi_damp_flap_prefix_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_safi_flap_cidr_only_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_safi_damp_flap_cidr_only_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_safi_flap_regexp_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_safi_flap_regexp_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_safi_damp_flap_regexp_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_safi_damp_flap_regexp_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_safi_flap_filter_list_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_safi_flap_filter_list_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_safi_damp_flap_filter_list_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_safi_damp_flap_filter_list_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_safi_flap_prefix_list_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_safi_flap_prefix_list_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_safi_damp_flap_prefix_list_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_safi_damp_flap_prefix_list_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_safi_flap_prefix_longer_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_safi_flap_prefix_longer_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_safi_damp_flap_prefix_longer_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_safi_damp_flap_prefix_longer_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_safi_flap_route_map_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_safi_flap_route_map_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_safi_damp_flap_route_map_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_safi_damp_flap_route_map_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_safi_neighbor_flap_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_safi_neighbor_flap_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_safi_neighbor_damp_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_safi_neighbor_damp_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_safi_rsclient_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_safi_rsclient_route_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_safi_rsclient_prefix_cmd); + install_element (VIEW_NODE, &show_bgp_view_ipv4_safi_rsclient_cmd); + install_element (VIEW_NODE, &show_bgp_view_ipv4_safi_rsclient_route_cmd); + install_element (VIEW_NODE, &show_bgp_view_ipv4_safi_rsclient_prefix_cmd); + + /* Restricted node: VIEW_NODE - (set of dangerous commands) */ + install_element (RESTRICTED_NODE, &show_bgp_ipv4_safi_route_cmd); + install_element (RESTRICTED_NODE, &show_bgp_ipv4_vpn_rd_route_cmd); + install_element (RESTRICTED_NODE, &show_bgp_ipv6_vpn_rd_route_cmd); + install_element (RESTRICTED_NODE, &show_bgp_ipv4_safi_rd_route_cmd); + install_element (RESTRICTED_NODE, &show_bgp_ipv6_safi_rd_route_cmd); + install_element (RESTRICTED_NODE, &show_bgp_ipv4_prefix_cmd); + install_element (RESTRICTED_NODE, &show_bgp_ipv4_safi_prefix_cmd); + install_element (RESTRICTED_NODE, &show_bgp_ipv4_vpn_prefix_cmd); + install_element (RESTRICTED_NODE, &show_bgp_ipv6_vpn_prefix_cmd); + install_element (RESTRICTED_NODE, &show_bgp_ipv4_encap_prefix_cmd); + install_element (RESTRICTED_NODE, &show_bgp_ipv6_encap_prefix_cmd); + install_element (RESTRICTED_NODE, &show_bgp_ipv4_safi_rd_prefix_cmd); + install_element (RESTRICTED_NODE, &show_bgp_ipv6_safi_rd_prefix_cmd); + install_element (RESTRICTED_NODE, &show_bgp_view_afi_safi_route_cmd); + install_element (RESTRICTED_NODE, &show_bgp_view_afi_safi_prefix_cmd); + install_element (RESTRICTED_NODE, &show_bgp_ipv4_community_cmd); + install_element (RESTRICTED_NODE, &show_bgp_ipv4_community2_cmd); + install_element (RESTRICTED_NODE, &show_bgp_ipv4_community3_cmd); + install_element (RESTRICTED_NODE, &show_bgp_ipv4_community4_cmd); + install_element (RESTRICTED_NODE, &show_bgp_ipv4_safi_community_cmd); + install_element (RESTRICTED_NODE, &show_bgp_ipv4_safi_community2_cmd); + install_element (RESTRICTED_NODE, &show_bgp_ipv4_safi_community3_cmd); + install_element (RESTRICTED_NODE, &show_bgp_ipv4_safi_community4_cmd); + install_element (RESTRICTED_NODE, &show_bgp_view_afi_safi_community_all_cmd); + install_element (RESTRICTED_NODE, &show_bgp_view_afi_safi_community_cmd); + install_element (RESTRICTED_NODE, &show_bgp_view_afi_safi_community2_cmd); + install_element (RESTRICTED_NODE, &show_bgp_view_afi_safi_community3_cmd); + install_element (RESTRICTED_NODE, &show_bgp_view_afi_safi_community4_cmd); + install_element (RESTRICTED_NODE, &show_bgp_ipv4_community_exact_cmd); + install_element (RESTRICTED_NODE, &show_bgp_ipv4_community2_exact_cmd); + install_element (RESTRICTED_NODE, &show_bgp_ipv4_community3_exact_cmd); + install_element (RESTRICTED_NODE, &show_bgp_ipv4_community4_exact_cmd); + install_element (RESTRICTED_NODE, &show_bgp_ipv4_safi_community_exact_cmd); + install_element (RESTRICTED_NODE, &show_bgp_ipv4_safi_community2_exact_cmd); + install_element (RESTRICTED_NODE, &show_bgp_ipv4_safi_community3_exact_cmd); + install_element (RESTRICTED_NODE, &show_bgp_ipv4_safi_community4_exact_cmd); + + /* large-community */ + install_element (RESTRICTED_NODE, &show_bgp_ipv4_lcommunity_cmd); + install_element (RESTRICTED_NODE, &show_bgp_ipv4_lcommunity2_cmd); + install_element (RESTRICTED_NODE, &show_bgp_ipv4_lcommunity3_cmd); + install_element (RESTRICTED_NODE, &show_bgp_ipv4_lcommunity4_cmd); + install_element (RESTRICTED_NODE, &show_bgp_ipv4_safi_lcommunity_cmd); + install_element (RESTRICTED_NODE, &show_bgp_ipv4_safi_lcommunity2_cmd); + install_element (RESTRICTED_NODE, &show_bgp_ipv4_safi_lcommunity3_cmd); + install_element (RESTRICTED_NODE, &show_bgp_ipv4_safi_lcommunity4_cmd); + install_element (RESTRICTED_NODE, &show_bgp_view_afi_safi_lcommunity_all_cmd); + install_element (RESTRICTED_NODE, &show_bgp_view_afi_safi_lcommunity_cmd); + install_element (RESTRICTED_NODE, &show_bgp_view_afi_safi_lcommunity2_cmd); + install_element (RESTRICTED_NODE, &show_bgp_view_afi_safi_lcommunity3_cmd); + install_element (RESTRICTED_NODE, &show_bgp_view_afi_safi_lcommunity4_cmd); + + install_element (RESTRICTED_NODE, &show_bgp_ipv4_safi_rsclient_route_cmd); + install_element (RESTRICTED_NODE, &show_bgp_ipv4_safi_rsclient_prefix_cmd); + install_element (RESTRICTED_NODE, &show_bgp_view_ipv4_safi_rsclient_route_cmd); + install_element (RESTRICTED_NODE, &show_bgp_view_ipv4_safi_rsclient_prefix_cmd); + + /* BGP dampening clear commands */ + install_element (ENABLE_NODE, &clear_ip_bgp_dampening_cmd); + install_element (ENABLE_NODE, &clear_ip_bgp_dampening_prefix_cmd); + + install_element (ENABLE_NODE, &clear_ip_bgp_dampening_address_cmd); + install_element (ENABLE_NODE, &clear_ip_bgp_dampening_address_mask_cmd); + + /* New config IPv6 BGP commands. */ + install_element (BGP_IPV6_NODE, &ipv6_bgp_network_cmd); + install_element (BGP_IPV6_NODE, &ipv6_bgp_network_route_map_cmd); + install_element (BGP_IPV6_NODE, &no_ipv6_bgp_network_cmd); + install_element (BGP_IPV6_NODE, &no_ipv6_bgp_network_route_map_cmd); + + install_element (BGP_IPV6_NODE, &ipv6_aggregate_address_cmd); + install_element (BGP_IPV6_NODE, &ipv6_aggregate_address_summary_only_cmd); + install_element (BGP_IPV6_NODE, &no_ipv6_aggregate_address_cmd); + install_element (BGP_IPV6_NODE, &no_ipv6_aggregate_address_summary_only_cmd); + + install_element (BGP_IPV6M_NODE, &ipv6_bgp_network_cmd); + install_element (BGP_IPV6M_NODE, &no_ipv6_bgp_network_cmd); + + /* Old config IPv6 BGP commands. */ + install_element (BGP_NODE, &old_ipv6_bgp_network_cmd); + install_element (BGP_NODE, &old_no_ipv6_bgp_network_cmd); + + install_element (BGP_NODE, &old_ipv6_aggregate_address_cmd); + install_element (BGP_NODE, &old_ipv6_aggregate_address_summary_only_cmd); + install_element (BGP_NODE, &old_no_ipv6_aggregate_address_cmd); + install_element (BGP_NODE, &old_no_ipv6_aggregate_address_summary_only_cmd); + + install_element (VIEW_NODE, &show_bgp_ipv6_safi_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_route_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_safi_route_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_prefix_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_safi_prefix_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_regexp_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_prefix_list_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_filter_list_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_route_map_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_safi_community_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_safi_community2_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_safi_community3_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_safi_community4_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_safi_community_exact_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_safi_community2_exact_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_safi_community3_exact_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_safi_community4_exact_cmd); + install_element (VIEW_NODE, &show_bgp_community_list_cmd); + + /* large-community */ + install_element (VIEW_NODE, &show_bgp_ipv6_safi_lcommunity_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_safi_lcommunity2_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_safi_lcommunity3_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_safi_lcommunity4_cmd); + install_element (VIEW_NODE, &show_bgp_lcommunity_list_cmd); + + install_element (VIEW_NODE, &show_bgp_ipv6_prefix_longer_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_neighbor_advertised_route_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_neighbor_received_routes_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_neighbor_routes_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_neighbor_received_prefix_filter_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_neighbor_flap_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_neighbor_damp_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_rsclient_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_rsclient_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_safi_rsclient_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_rsclient_route_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_safi_rsclient_route_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_rsclient_prefix_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_safi_rsclient_prefix_cmd); + install_element (VIEW_NODE, &show_bgp_view_ipv6_cmd); + install_element (VIEW_NODE, &show_bgp_view_ipv6_route_cmd); + install_element (VIEW_NODE, &show_bgp_view_ipv6_prefix_cmd); + install_element (VIEW_NODE, &show_bgp_view_ipv6_neighbor_advertised_route_cmd); + install_element (VIEW_NODE, &show_bgp_view_ipv6_neighbor_received_routes_cmd); + install_element (VIEW_NODE, &show_bgp_view_ipv6_neighbor_routes_cmd); + install_element (VIEW_NODE, &show_bgp_view_ipv6_neighbor_received_prefix_filter_cmd); + install_element (VIEW_NODE, &show_bgp_view_ipv6_neighbor_flap_cmd); + install_element (VIEW_NODE, &show_bgp_view_ipv6_neighbor_damp_cmd); + install_element (VIEW_NODE, &show_bgp_view_ipv4_rsclient_cmd); + install_element (VIEW_NODE, &show_bgp_view_ipv6_rsclient_cmd); + install_element (VIEW_NODE, &show_bgp_view_ipv6_safi_rsclient_cmd); + install_element (VIEW_NODE, &show_bgp_view_ipv6_rsclient_route_cmd); + install_element (VIEW_NODE, &show_bgp_view_ipv6_safi_rsclient_route_cmd); + install_element (VIEW_NODE, &show_bgp_view_ipv6_rsclient_prefix_cmd); + install_element (VIEW_NODE, &show_bgp_view_ipv6_safi_rsclient_prefix_cmd); + + /* Restricted: + * VIEW_NODE - (set of dangerous commands) - (commands dependent on prev) + */ + install_element (RESTRICTED_NODE, &show_bgp_ipv6_route_cmd); + install_element (RESTRICTED_NODE, &show_bgp_ipv6_safi_route_cmd); + install_element (RESTRICTED_NODE, &show_bgp_ipv6_prefix_cmd); + install_element (RESTRICTED_NODE, &show_bgp_ipv6_safi_prefix_cmd); + install_element (RESTRICTED_NODE, &show_bgp_ipv6_safi_community_cmd); + install_element (RESTRICTED_NODE, &show_bgp_ipv6_safi_community2_cmd); + install_element (RESTRICTED_NODE, &show_bgp_ipv6_safi_community3_cmd); + install_element (RESTRICTED_NODE, &show_bgp_ipv6_safi_community4_cmd); + install_element (RESTRICTED_NODE, &show_bgp_ipv6_safi_community_exact_cmd); + install_element (RESTRICTED_NODE, &show_bgp_ipv6_safi_community2_exact_cmd); + install_element (RESTRICTED_NODE, &show_bgp_ipv6_safi_community3_exact_cmd); + install_element (RESTRICTED_NODE, &show_bgp_ipv6_safi_community4_exact_cmd); + install_element (RESTRICTED_NODE, &show_bgp_ipv6_safi_lcommunity_cmd); + install_element (RESTRICTED_NODE, &show_bgp_ipv6_safi_lcommunity2_cmd); + install_element (RESTRICTED_NODE, &show_bgp_ipv6_safi_lcommunity3_cmd); + install_element (RESTRICTED_NODE, &show_bgp_ipv6_safi_lcommunity4_cmd); + install_element (RESTRICTED_NODE, &show_bgp_ipv6_rsclient_route_cmd); + install_element (RESTRICTED_NODE, &show_bgp_ipv6_safi_rsclient_route_cmd); + install_element (RESTRICTED_NODE, &show_bgp_ipv6_rsclient_prefix_cmd); + install_element (RESTRICTED_NODE, &show_bgp_ipv6_safi_rsclient_prefix_cmd); + install_element (RESTRICTED_NODE, &show_bgp_view_ipv6_route_cmd); + install_element (RESTRICTED_NODE, &show_bgp_view_ipv6_prefix_cmd); + install_element (RESTRICTED_NODE, &show_bgp_view_ipv6_neighbor_received_prefix_filter_cmd); + install_element (RESTRICTED_NODE, &show_bgp_view_ipv6_rsclient_route_cmd); + install_element (RESTRICTED_NODE, &show_bgp_view_ipv6_safi_rsclient_route_cmd); + install_element (RESTRICTED_NODE, &show_bgp_view_ipv6_rsclient_prefix_cmd); + install_element (RESTRICTED_NODE, &show_bgp_view_ipv6_safi_rsclient_prefix_cmd); + + /* Statistics */ + install_element (ENABLE_NODE, &show_bgp_statistics_cmd); + install_element (ENABLE_NODE, &show_bgp_statistics_view_cmd); + + install_element (BGP_NODE, &bgp_distance_cmd); + install_element (BGP_NODE, &no_bgp_distance_cmd); + install_element (BGP_NODE, &no_bgp_distance2_cmd); + install_element (BGP_NODE, &bgp_distance_source_cmd); + install_element (BGP_NODE, &no_bgp_distance_source_cmd); + install_element (BGP_NODE, &bgp_distance_source_access_list_cmd); + install_element (BGP_NODE, &no_bgp_distance_source_access_list_cmd); +#ifdef HAVE_IPV6 + install_element (BGP_IPV6_NODE, &ipv6_bgp_distance_cmd); + install_element (BGP_IPV6_NODE, &no_ipv6_bgp_distance_cmd); + install_element (BGP_IPV6_NODE, &no_ipv6_bgp_distance2_cmd); + install_element (BGP_IPV6_NODE, &ipv6_bgp_distance_source_cmd); + install_element (BGP_IPV6_NODE, &no_ipv6_bgp_distance_source_cmd); + install_element (BGP_IPV6_NODE, &ipv6_bgp_distance_source_access_list_cmd); + install_element (BGP_IPV6_NODE, &no_ipv6_bgp_distance_source_access_list_cmd); +#endif /* HAVE_IPV6 */ + + install_element (BGP_NODE, &bgp_damp_set_cmd); + install_element (BGP_NODE, &bgp_damp_set2_cmd); + install_element (BGP_NODE, &bgp_damp_set3_cmd); + install_element (BGP_NODE, &bgp_damp_unset_cmd); + install_element (BGP_NODE, &bgp_damp_unset2_cmd); + install_element (BGP_IPV4_NODE, &bgp_damp_set_cmd); + install_element (BGP_IPV4_NODE, &bgp_damp_set2_cmd); + install_element (BGP_IPV4_NODE, &bgp_damp_set3_cmd); + install_element (BGP_IPV4_NODE, &bgp_damp_unset_cmd); + install_element (BGP_IPV4_NODE, &bgp_damp_unset2_cmd); + + /* IPv4 Multicast Mode */ + install_element (BGP_IPV4M_NODE, &bgp_damp_set_cmd); + install_element (BGP_IPV4M_NODE, &bgp_damp_set2_cmd); + install_element (BGP_IPV4M_NODE, &bgp_damp_set3_cmd); + install_element (BGP_IPV4M_NODE, &bgp_damp_unset_cmd); + install_element (BGP_IPV4M_NODE, &bgp_damp_unset2_cmd); + + + /* Deprecated AS-Pathlimit commands */ + install_element (BGP_NODE, &bgp_network_ttl_cmd); + install_element (BGP_NODE, &bgp_network_mask_ttl_cmd); + install_element (BGP_NODE, &bgp_network_mask_natural_ttl_cmd); + install_element (BGP_NODE, &bgp_network_backdoor_ttl_cmd); + install_element (BGP_NODE, &bgp_network_mask_backdoor_ttl_cmd); + install_element (BGP_NODE, &bgp_network_mask_natural_backdoor_ttl_cmd); + + install_element (BGP_NODE, &no_bgp_network_ttl_cmd); + install_element (BGP_NODE, &no_bgp_network_mask_ttl_cmd); + install_element (BGP_NODE, &no_bgp_network_mask_natural_ttl_cmd); + install_element (BGP_NODE, &no_bgp_network_backdoor_ttl_cmd); + install_element (BGP_NODE, &no_bgp_network_mask_backdoor_ttl_cmd); + install_element (BGP_NODE, &no_bgp_network_mask_natural_backdoor_ttl_cmd); + + install_element (BGP_IPV4_NODE, &bgp_network_ttl_cmd); + install_element (BGP_IPV4_NODE, &bgp_network_mask_ttl_cmd); + install_element (BGP_IPV4_NODE, &bgp_network_mask_natural_ttl_cmd); + install_element (BGP_IPV4_NODE, &bgp_network_backdoor_ttl_cmd); + install_element (BGP_IPV4_NODE, &bgp_network_mask_backdoor_ttl_cmd); + install_element (BGP_IPV4_NODE, &bgp_network_mask_natural_backdoor_ttl_cmd); + + install_element (BGP_IPV4_NODE, &no_bgp_network_ttl_cmd); + install_element (BGP_IPV4_NODE, &no_bgp_network_mask_ttl_cmd); + install_element (BGP_IPV4_NODE, &no_bgp_network_mask_natural_ttl_cmd); + install_element (BGP_IPV4_NODE, &no_bgp_network_backdoor_ttl_cmd); + install_element (BGP_IPV4_NODE, &no_bgp_network_mask_backdoor_ttl_cmd); + install_element (BGP_IPV4_NODE, &no_bgp_network_mask_natural_backdoor_ttl_cmd); + + install_element (BGP_IPV4M_NODE, &bgp_network_ttl_cmd); + install_element (BGP_IPV4M_NODE, &bgp_network_mask_ttl_cmd); + install_element (BGP_IPV4M_NODE, &bgp_network_mask_natural_ttl_cmd); + install_element (BGP_IPV4M_NODE, &bgp_network_backdoor_ttl_cmd); + install_element (BGP_IPV4M_NODE, &bgp_network_mask_backdoor_ttl_cmd); + install_element (BGP_IPV4M_NODE, &bgp_network_mask_natural_backdoor_ttl_cmd); + + install_element (BGP_IPV4M_NODE, &no_bgp_network_ttl_cmd); + install_element (BGP_IPV4M_NODE, &no_bgp_network_mask_ttl_cmd); + install_element (BGP_IPV4M_NODE, &no_bgp_network_mask_natural_ttl_cmd); + install_element (BGP_IPV4M_NODE, &no_bgp_network_backdoor_ttl_cmd); + install_element (BGP_IPV4M_NODE, &no_bgp_network_mask_backdoor_ttl_cmd); + install_element (BGP_IPV4M_NODE, &no_bgp_network_mask_natural_backdoor_ttl_cmd); + + install_element (BGP_IPV6_NODE, &ipv6_bgp_network_ttl_cmd); + install_element (BGP_IPV6_NODE, &no_ipv6_bgp_network_ttl_cmd); + + /* old style commands */ install_element (VIEW_NODE, &show_ip_bgp_cmd); install_element (VIEW_NODE, &show_ip_bgp_ipv4_cmd); - install_element (VIEW_NODE, &show_bgp_ipv4_safi_cmd); install_element (VIEW_NODE, &show_ip_bgp_route_cmd); + install_element (VIEW_NODE, &show_ip_bgp_route_pathtype_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_safi_route_pathtype_cmd); install_element (VIEW_NODE, &show_ip_bgp_ipv4_route_cmd); - install_element (VIEW_NODE, &show_bgp_ipv4_safi_route_cmd); install_element (VIEW_NODE, &show_ip_bgp_vpnv4_all_route_cmd); install_element (VIEW_NODE, &show_ip_bgp_vpnv4_rd_route_cmd); install_element (VIEW_NODE, &show_ip_bgp_prefix_cmd); install_element (VIEW_NODE, &show_ip_bgp_ipv4_prefix_cmd); - install_element (VIEW_NODE, &show_bgp_ipv4_safi_prefix_cmd); + install_element (VIEW_NODE, &show_ip_bgp_ipv4_prefix_pathtype_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_safi_prefix_pathtype_cmd); + install_element (VIEW_NODE, &show_ip_bgp_prefix_pathtype_cmd); install_element (VIEW_NODE, &show_ip_bgp_vpnv4_all_prefix_cmd); install_element (VIEW_NODE, &show_ip_bgp_vpnv4_rd_prefix_cmd); install_element (VIEW_NODE, &show_ip_bgp_view_cmd); @@ -12368,11 +17691,6 @@ bgp_route_init (void) install_element (VIEW_NODE, &show_ip_bgp_ipv4_community2_cmd); install_element (VIEW_NODE, &show_ip_bgp_ipv4_community3_cmd); install_element (VIEW_NODE, &show_ip_bgp_ipv4_community4_cmd); - install_element (VIEW_NODE, &show_bgp_view_afi_safi_community_all_cmd); - install_element (VIEW_NODE, &show_bgp_view_afi_safi_community_cmd); - install_element (VIEW_NODE, &show_bgp_view_afi_safi_community2_cmd); - install_element (VIEW_NODE, &show_bgp_view_afi_safi_community3_cmd); - install_element (VIEW_NODE, &show_bgp_view_afi_safi_community4_cmd); install_element (VIEW_NODE, &show_ip_bgp_community_exact_cmd); install_element (VIEW_NODE, &show_ip_bgp_community2_exact_cmd); install_element (VIEW_NODE, &show_ip_bgp_community3_exact_cmd); @@ -12385,52 +17703,71 @@ bgp_route_init (void) install_element (VIEW_NODE, &show_ip_bgp_ipv4_community_list_cmd); install_element (VIEW_NODE, &show_ip_bgp_community_list_exact_cmd); install_element (VIEW_NODE, &show_ip_bgp_ipv4_community_list_exact_cmd); + install_element (VIEW_NODE, &show_ip_bgp_lcommunity_all_cmd); + install_element (VIEW_NODE, &show_ip_bgp_ipv4_lcommunity_all_cmd); + install_element (VIEW_NODE, &show_ip_bgp_lcommunity_cmd); + install_element (VIEW_NODE, &show_ip_bgp_lcommunity2_cmd); + install_element (VIEW_NODE, &show_ip_bgp_lcommunity3_cmd); + install_element (VIEW_NODE, &show_ip_bgp_lcommunity4_cmd); + install_element (VIEW_NODE, &show_ip_bgp_ipv4_lcommunity_cmd); + install_element (VIEW_NODE, &show_ip_bgp_ipv4_lcommunity2_cmd); + install_element (VIEW_NODE, &show_ip_bgp_ipv4_lcommunity3_cmd); + install_element (VIEW_NODE, &show_ip_bgp_ipv4_lcommunity4_cmd); + install_element (VIEW_NODE, &show_ip_bgp_lcommunity_list_cmd); + install_element (VIEW_NODE, &show_ip_bgp_ipv4_lcommunity_list_cmd); install_element (VIEW_NODE, &show_ip_bgp_prefix_longer_cmd); install_element (VIEW_NODE, &show_ip_bgp_ipv4_prefix_longer_cmd); install_element (VIEW_NODE, &show_ip_bgp_neighbor_advertised_route_cmd); install_element (VIEW_NODE, &show_ip_bgp_ipv4_neighbor_advertised_route_cmd); install_element (VIEW_NODE, &show_ip_bgp_neighbor_received_routes_cmd); install_element (VIEW_NODE, &show_ip_bgp_ipv4_neighbor_received_routes_cmd); - install_element (VIEW_NODE, &show_bgp_view_afi_safi_neighbor_adv_recd_routes_cmd); install_element (VIEW_NODE, &show_ip_bgp_neighbor_routes_cmd); install_element (VIEW_NODE, &show_ip_bgp_ipv4_neighbor_routes_cmd); install_element (VIEW_NODE, &show_ip_bgp_neighbor_received_prefix_filter_cmd); install_element (VIEW_NODE, &show_ip_bgp_ipv4_neighbor_received_prefix_filter_cmd); + install_element (VIEW_NODE, &show_ip_bgp_dampening_params_cmd); + install_element (VIEW_NODE, &show_ip_bgp_ipv4_dampening_parameters_cmd); install_element (VIEW_NODE, &show_ip_bgp_dampened_paths_cmd); + install_element (VIEW_NODE, &show_ip_bgp_ipv4_dampening_dampd_paths_cmd); + install_element (VIEW_NODE, &show_ip_bgp_ipv4_dampening_flap_stats_cmd); + install_element (VIEW_NODE, &show_ip_bgp_damp_dampened_paths_cmd); install_element (VIEW_NODE, &show_ip_bgp_flap_statistics_cmd); + install_element (VIEW_NODE, &show_ip_bgp_damp_flap_statistics_cmd); install_element (VIEW_NODE, &show_ip_bgp_flap_address_cmd); + install_element (VIEW_NODE, &show_ip_bgp_damp_flap_address_cmd); install_element (VIEW_NODE, &show_ip_bgp_flap_prefix_cmd); install_element (VIEW_NODE, &show_ip_bgp_flap_cidr_only_cmd); + install_element (VIEW_NODE, &show_ip_bgp_damp_flap_cidr_only_cmd); install_element (VIEW_NODE, &show_ip_bgp_flap_regexp_cmd); install_element (VIEW_NODE, &show_ip_bgp_flap_filter_list_cmd); + install_element (VIEW_NODE, &show_ip_bgp_damp_flap_filter_list_cmd); install_element (VIEW_NODE, &show_ip_bgp_flap_prefix_list_cmd); + install_element (VIEW_NODE, &show_ip_bgp_damp_flap_prefix_list_cmd); install_element (VIEW_NODE, &show_ip_bgp_flap_prefix_longer_cmd); + install_element (VIEW_NODE, &show_ip_bgp_damp_flap_prefix_longer_cmd); install_element (VIEW_NODE, &show_ip_bgp_flap_route_map_cmd); + install_element (VIEW_NODE, &show_ip_bgp_damp_flap_route_map_cmd); install_element (VIEW_NODE, &show_ip_bgp_neighbor_flap_cmd); install_element (VIEW_NODE, &show_ip_bgp_neighbor_damp_cmd); install_element (VIEW_NODE, &show_ip_bgp_rsclient_cmd); - install_element (VIEW_NODE, &show_bgp_ipv4_safi_rsclient_cmd); install_element (VIEW_NODE, &show_ip_bgp_rsclient_route_cmd); - install_element (VIEW_NODE, &show_bgp_ipv4_safi_rsclient_route_cmd); install_element (VIEW_NODE, &show_ip_bgp_rsclient_prefix_cmd); - install_element (VIEW_NODE, &show_bgp_ipv4_safi_rsclient_prefix_cmd); install_element (VIEW_NODE, &show_ip_bgp_view_neighbor_advertised_route_cmd); install_element (VIEW_NODE, &show_ip_bgp_view_neighbor_received_routes_cmd); install_element (VIEW_NODE, &show_ip_bgp_view_rsclient_cmd); - install_element (VIEW_NODE, &show_bgp_view_ipv4_safi_rsclient_cmd); install_element (VIEW_NODE, &show_ip_bgp_view_rsclient_route_cmd); - install_element (VIEW_NODE, &show_bgp_view_ipv4_safi_rsclient_route_cmd); install_element (VIEW_NODE, &show_ip_bgp_view_rsclient_prefix_cmd); - install_element (VIEW_NODE, &show_bgp_view_ipv4_safi_rsclient_prefix_cmd); - /* Restricted node: VIEW_NODE - (set of dangerous commands) */ install_element (RESTRICTED_NODE, &show_ip_bgp_route_cmd); + install_element (RESTRICTED_NODE, &show_ip_bgp_route_pathtype_cmd); + install_element (RESTRICTED_NODE, &show_bgp_ipv4_safi_route_pathtype_cmd); install_element (RESTRICTED_NODE, &show_ip_bgp_ipv4_route_cmd); - install_element (RESTRICTED_NODE, &show_bgp_ipv4_safi_route_cmd); install_element (RESTRICTED_NODE, &show_ip_bgp_vpnv4_rd_route_cmd); install_element (RESTRICTED_NODE, &show_ip_bgp_prefix_cmd); install_element (RESTRICTED_NODE, &show_ip_bgp_ipv4_prefix_cmd); - install_element (RESTRICTED_NODE, &show_bgp_ipv4_safi_prefix_cmd); + install_element (RESTRICTED_NODE, &show_ip_bgp_ipv4_prefix_pathtype_cmd); + install_element (RESTRICTED_NODE, &show_bgp_ipv4_safi_prefix_pathtype_cmd); + install_element (RESTRICTED_NODE, &show_ip_bgp_prefix_pathtype_cmd); install_element (RESTRICTED_NODE, &show_ip_bgp_vpnv4_all_prefix_cmd); install_element (RESTRICTED_NODE, &show_ip_bgp_vpnv4_rd_prefix_cmd); install_element (RESTRICTED_NODE, &show_ip_bgp_view_route_cmd); @@ -12443,11 +17780,6 @@ bgp_route_init (void) install_element (RESTRICTED_NODE, &show_ip_bgp_ipv4_community2_cmd); install_element (RESTRICTED_NODE, &show_ip_bgp_ipv4_community3_cmd); install_element (RESTRICTED_NODE, &show_ip_bgp_ipv4_community4_cmd); - install_element (RESTRICTED_NODE, &show_bgp_view_afi_safi_community_all_cmd); - install_element (RESTRICTED_NODE, &show_bgp_view_afi_safi_community_cmd); - install_element (RESTRICTED_NODE, &show_bgp_view_afi_safi_community2_cmd); - install_element (RESTRICTED_NODE, &show_bgp_view_afi_safi_community3_cmd); - install_element (RESTRICTED_NODE, &show_bgp_view_afi_safi_community4_cmd); install_element (RESTRICTED_NODE, &show_ip_bgp_community_exact_cmd); install_element (RESTRICTED_NODE, &show_ip_bgp_community2_exact_cmd); install_element (RESTRICTED_NODE, &show_ip_bgp_community3_exact_cmd); @@ -12456,357 +17788,103 @@ bgp_route_init (void) install_element (RESTRICTED_NODE, &show_ip_bgp_ipv4_community2_exact_cmd); install_element (RESTRICTED_NODE, &show_ip_bgp_ipv4_community3_exact_cmd); install_element (RESTRICTED_NODE, &show_ip_bgp_ipv4_community4_exact_cmd); + install_element (RESTRICTED_NODE, &show_ip_bgp_lcommunity_cmd); + install_element (RESTRICTED_NODE, &show_ip_bgp_lcommunity2_cmd); + install_element (RESTRICTED_NODE, &show_ip_bgp_lcommunity3_cmd); + install_element (RESTRICTED_NODE, &show_ip_bgp_lcommunity4_cmd); + install_element (RESTRICTED_NODE, &show_ip_bgp_ipv4_lcommunity_cmd); + install_element (RESTRICTED_NODE, &show_ip_bgp_ipv4_lcommunity2_cmd); + install_element (RESTRICTED_NODE, &show_ip_bgp_ipv4_lcommunity3_cmd); + install_element (RESTRICTED_NODE, &show_ip_bgp_ipv4_lcommunity4_cmd); install_element (RESTRICTED_NODE, &show_ip_bgp_rsclient_route_cmd); - install_element (RESTRICTED_NODE, &show_bgp_ipv4_safi_rsclient_route_cmd); install_element (RESTRICTED_NODE, &show_ip_bgp_rsclient_prefix_cmd); - install_element (RESTRICTED_NODE, &show_bgp_ipv4_safi_rsclient_prefix_cmd); install_element (RESTRICTED_NODE, &show_ip_bgp_view_rsclient_route_cmd); - install_element (RESTRICTED_NODE, &show_bgp_view_ipv4_safi_rsclient_route_cmd); install_element (RESTRICTED_NODE, &show_ip_bgp_view_rsclient_prefix_cmd); - install_element (RESTRICTED_NODE, &show_bgp_view_ipv4_safi_rsclient_prefix_cmd); - - install_element (ENABLE_NODE, &show_ip_bgp_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_ipv4_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv4_safi_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_route_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_ipv4_route_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv4_safi_route_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_vpnv4_all_route_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_vpnv4_rd_route_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_prefix_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_ipv4_prefix_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv4_safi_prefix_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_vpnv4_all_prefix_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_vpnv4_rd_prefix_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_view_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_view_route_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_view_prefix_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_regexp_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_ipv4_regexp_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_prefix_list_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_ipv4_prefix_list_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_filter_list_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_ipv4_filter_list_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_route_map_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_ipv4_route_map_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_cidr_only_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_ipv4_cidr_only_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_community_all_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_ipv4_community_all_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_community_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_community2_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_community3_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_community4_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_ipv4_community_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_ipv4_community2_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_ipv4_community3_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_ipv4_community4_cmd); - install_element (ENABLE_NODE, &show_bgp_view_afi_safi_community_all_cmd); - install_element (ENABLE_NODE, &show_bgp_view_afi_safi_community_cmd); - install_element (ENABLE_NODE, &show_bgp_view_afi_safi_community2_cmd); - install_element (ENABLE_NODE, &show_bgp_view_afi_safi_community3_cmd); - install_element (ENABLE_NODE, &show_bgp_view_afi_safi_community4_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_community_exact_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_community2_exact_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_community3_exact_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_community4_exact_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_ipv4_community_exact_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_ipv4_community2_exact_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_ipv4_community3_exact_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_ipv4_community4_exact_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_community_list_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_ipv4_community_list_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_community_list_exact_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_ipv4_community_list_exact_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_prefix_longer_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_ipv4_prefix_longer_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_neighbor_advertised_route_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_ipv4_neighbor_advertised_route_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_neighbor_received_routes_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_ipv4_neighbor_received_routes_cmd); - install_element (ENABLE_NODE, &show_bgp_view_afi_safi_neighbor_adv_recd_routes_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_neighbor_routes_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_ipv4_neighbor_routes_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_neighbor_received_prefix_filter_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_ipv4_neighbor_received_prefix_filter_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_dampened_paths_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_flap_statistics_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_flap_address_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_flap_prefix_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_flap_cidr_only_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_flap_regexp_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_flap_filter_list_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_flap_prefix_list_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_flap_prefix_longer_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_flap_route_map_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_neighbor_flap_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_neighbor_damp_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_rsclient_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv4_safi_rsclient_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_rsclient_route_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv4_safi_rsclient_route_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_rsclient_prefix_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv4_safi_rsclient_prefix_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_view_neighbor_advertised_route_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_view_neighbor_received_routes_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_view_rsclient_cmd); - install_element (ENABLE_NODE, &show_bgp_view_ipv4_safi_rsclient_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_view_rsclient_route_cmd); - install_element (ENABLE_NODE, &show_bgp_view_ipv4_safi_rsclient_route_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_view_rsclient_prefix_cmd); - install_element (ENABLE_NODE, &show_bgp_view_ipv4_safi_rsclient_prefix_cmd); - - /* BGP dampening clear commands */ - install_element (ENABLE_NODE, &clear_ip_bgp_dampening_cmd); - install_element (ENABLE_NODE, &clear_ip_bgp_dampening_prefix_cmd); - install_element (ENABLE_NODE, &clear_ip_bgp_dampening_address_cmd); - install_element (ENABLE_NODE, &clear_ip_bgp_dampening_address_mask_cmd); - - /* prefix count */ - install_element (ENABLE_NODE, &show_ip_bgp_neighbor_prefix_counts_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_ipv4_neighbor_prefix_counts_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_vpnv4_neighbor_prefix_counts_cmd); -#ifdef HAVE_IPV6 - install_element (ENABLE_NODE, &show_bgp_ipv6_neighbor_prefix_counts_cmd); - - /* New config IPv6 BGP commands. */ - install_element (BGP_IPV6_NODE, &ipv6_bgp_network_cmd); - install_element (BGP_IPV6_NODE, &ipv6_bgp_network_route_map_cmd); - install_element (BGP_IPV6_NODE, &no_ipv6_bgp_network_cmd); - install_element (BGP_IPV6_NODE, &no_ipv6_bgp_network_route_map_cmd); - - install_element (BGP_IPV6_NODE, &ipv6_aggregate_address_cmd); - install_element (BGP_IPV6_NODE, &ipv6_aggregate_address_summary_only_cmd); - install_element (BGP_IPV6_NODE, &no_ipv6_aggregate_address_cmd); - install_element (BGP_IPV6_NODE, &no_ipv6_aggregate_address_summary_only_cmd); - - install_element (BGP_IPV6M_NODE, &ipv6_bgp_network_cmd); - install_element (BGP_IPV6M_NODE, &no_ipv6_bgp_network_cmd); - - /* Old config IPv6 BGP commands. */ - install_element (BGP_NODE, &old_ipv6_bgp_network_cmd); - install_element (BGP_NODE, &old_no_ipv6_bgp_network_cmd); - - install_element (BGP_NODE, &old_ipv6_aggregate_address_cmd); - install_element (BGP_NODE, &old_ipv6_aggregate_address_summary_only_cmd); - install_element (BGP_NODE, &old_no_ipv6_aggregate_address_cmd); - install_element (BGP_NODE, &old_no_ipv6_aggregate_address_summary_only_cmd); + + install_element (VIEW_NODE, &show_ip_bgp_neighbor_prefix_counts_cmd); + install_element (VIEW_NODE, &show_ip_bgp_ipv4_neighbor_prefix_counts_cmd); + install_element (VIEW_NODE, &show_ip_bgp_vpnv4_neighbor_prefix_counts_cmd); install_element (VIEW_NODE, &show_bgp_cmd); install_element (VIEW_NODE, &show_bgp_ipv6_cmd); - install_element (VIEW_NODE, &show_bgp_ipv6_safi_cmd); install_element (VIEW_NODE, &show_bgp_route_cmd); - install_element (VIEW_NODE, &show_bgp_ipv6_route_cmd); - install_element (VIEW_NODE, &show_bgp_ipv6_safi_route_cmd); install_element (VIEW_NODE, &show_bgp_prefix_cmd); - install_element (VIEW_NODE, &show_bgp_ipv6_prefix_cmd); - install_element (VIEW_NODE, &show_bgp_ipv6_safi_prefix_cmd); + install_element (VIEW_NODE, &show_bgp_route_pathtype_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_route_pathtype_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_safi_route_pathtype_cmd); + install_element (VIEW_NODE, &show_bgp_prefix_pathtype_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_prefix_pathtype_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_safi_prefix_pathtype_cmd); install_element (VIEW_NODE, &show_bgp_regexp_cmd); - install_element (VIEW_NODE, &show_bgp_ipv6_regexp_cmd); install_element (VIEW_NODE, &show_bgp_prefix_list_cmd); - install_element (VIEW_NODE, &show_bgp_ipv6_prefix_list_cmd); install_element (VIEW_NODE, &show_bgp_filter_list_cmd); - install_element (VIEW_NODE, &show_bgp_ipv6_filter_list_cmd); install_element (VIEW_NODE, &show_bgp_route_map_cmd); - install_element (VIEW_NODE, &show_bgp_ipv6_route_map_cmd); install_element (VIEW_NODE, &show_bgp_community_all_cmd); install_element (VIEW_NODE, &show_bgp_ipv6_community_all_cmd); install_element (VIEW_NODE, &show_bgp_community_cmd); - install_element (VIEW_NODE, &show_bgp_ipv6_community_cmd); install_element (VIEW_NODE, &show_bgp_community2_cmd); - install_element (VIEW_NODE, &show_bgp_ipv6_community2_cmd); install_element (VIEW_NODE, &show_bgp_community3_cmd); - install_element (VIEW_NODE, &show_bgp_ipv6_community3_cmd); install_element (VIEW_NODE, &show_bgp_community4_cmd); - install_element (VIEW_NODE, &show_bgp_ipv6_community4_cmd); install_element (VIEW_NODE, &show_bgp_community_exact_cmd); - install_element (VIEW_NODE, &show_bgp_ipv6_community_exact_cmd); install_element (VIEW_NODE, &show_bgp_community2_exact_cmd); - install_element (VIEW_NODE, &show_bgp_ipv6_community2_exact_cmd); install_element (VIEW_NODE, &show_bgp_community3_exact_cmd); - install_element (VIEW_NODE, &show_bgp_ipv6_community3_exact_cmd); install_element (VIEW_NODE, &show_bgp_community4_exact_cmd); - install_element (VIEW_NODE, &show_bgp_ipv6_community4_exact_cmd); - install_element (VIEW_NODE, &show_bgp_community_list_cmd); - install_element (VIEW_NODE, &show_bgp_ipv6_community_list_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_safi_community_list_cmd); install_element (VIEW_NODE, &show_bgp_community_list_exact_cmd); - install_element (VIEW_NODE, &show_bgp_ipv6_community_list_exact_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_safi_community_list_exact_cmd); + install_element (VIEW_NODE, &show_bgp_lcommunity_all_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_lcommunity_all_cmd); + install_element (VIEW_NODE, &show_bgp_lcommunity_cmd); + install_element (VIEW_NODE, &show_bgp_lcommunity2_cmd); + install_element (VIEW_NODE, &show_bgp_lcommunity3_cmd); + install_element (VIEW_NODE, &show_bgp_lcommunity4_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_safi_lcommunity_list_cmd); install_element (VIEW_NODE, &show_bgp_prefix_longer_cmd); - install_element (VIEW_NODE, &show_bgp_ipv6_prefix_longer_cmd); install_element (VIEW_NODE, &show_bgp_neighbor_advertised_route_cmd); - install_element (VIEW_NODE, &show_bgp_ipv6_neighbor_advertised_route_cmd); install_element (VIEW_NODE, &show_bgp_neighbor_received_routes_cmd); - install_element (VIEW_NODE, &show_bgp_ipv6_neighbor_received_routes_cmd); install_element (VIEW_NODE, &show_bgp_neighbor_routes_cmd); - install_element (VIEW_NODE, &show_bgp_ipv6_neighbor_routes_cmd); install_element (VIEW_NODE, &show_bgp_neighbor_received_prefix_filter_cmd); - install_element (VIEW_NODE, &show_bgp_ipv6_neighbor_received_prefix_filter_cmd); install_element (VIEW_NODE, &show_bgp_neighbor_flap_cmd); - install_element (VIEW_NODE, &show_bgp_ipv6_neighbor_flap_cmd); install_element (VIEW_NODE, &show_bgp_neighbor_damp_cmd); - install_element (VIEW_NODE, &show_bgp_ipv6_neighbor_damp_cmd); install_element (VIEW_NODE, &show_bgp_rsclient_cmd); - install_element (VIEW_NODE, &show_bgp_ipv6_safi_rsclient_cmd); - install_element (VIEW_NODE, &show_bgp_rsclient_route_cmd); - install_element (VIEW_NODE, &show_bgp_ipv6_safi_rsclient_route_cmd); - install_element (VIEW_NODE, &show_bgp_rsclient_prefix_cmd); - install_element (VIEW_NODE, &show_bgp_ipv6_safi_rsclient_prefix_cmd); install_element (VIEW_NODE, &show_bgp_view_cmd); - install_element (VIEW_NODE, &show_bgp_view_ipv6_cmd); install_element (VIEW_NODE, &show_bgp_view_route_cmd); - install_element (VIEW_NODE, &show_bgp_view_ipv6_route_cmd); install_element (VIEW_NODE, &show_bgp_view_prefix_cmd); - install_element (VIEW_NODE, &show_bgp_view_ipv6_prefix_cmd); install_element (VIEW_NODE, &show_bgp_view_neighbor_advertised_route_cmd); - install_element (VIEW_NODE, &show_bgp_view_ipv6_neighbor_advertised_route_cmd); install_element (VIEW_NODE, &show_bgp_view_neighbor_received_routes_cmd); - install_element (VIEW_NODE, &show_bgp_view_ipv6_neighbor_received_routes_cmd); install_element (VIEW_NODE, &show_bgp_view_neighbor_routes_cmd); - install_element (VIEW_NODE, &show_bgp_view_ipv6_neighbor_routes_cmd); install_element (VIEW_NODE, &show_bgp_view_neighbor_received_prefix_filter_cmd); - install_element (VIEW_NODE, &show_bgp_view_ipv6_neighbor_received_prefix_filter_cmd); install_element (VIEW_NODE, &show_bgp_view_neighbor_flap_cmd); - install_element (VIEW_NODE, &show_bgp_view_ipv6_neighbor_flap_cmd); install_element (VIEW_NODE, &show_bgp_view_neighbor_damp_cmd); - install_element (VIEW_NODE, &show_bgp_view_ipv6_neighbor_damp_cmd); install_element (VIEW_NODE, &show_bgp_view_rsclient_cmd); - install_element (VIEW_NODE, &show_bgp_view_ipv6_safi_rsclient_cmd); - install_element (VIEW_NODE, &show_bgp_view_rsclient_route_cmd); - install_element (VIEW_NODE, &show_bgp_view_ipv6_safi_rsclient_route_cmd); - install_element (VIEW_NODE, &show_bgp_view_rsclient_prefix_cmd); - install_element (VIEW_NODE, &show_bgp_view_ipv6_safi_rsclient_prefix_cmd); - /* Restricted: - * VIEW_NODE - (set of dangerous commands) - (commands dependent on prev) - */ install_element (RESTRICTED_NODE, &show_bgp_route_cmd); - install_element (RESTRICTED_NODE, &show_bgp_ipv6_route_cmd); - install_element (RESTRICTED_NODE, &show_bgp_ipv6_safi_route_cmd); install_element (RESTRICTED_NODE, &show_bgp_prefix_cmd); - install_element (RESTRICTED_NODE, &show_bgp_ipv6_prefix_cmd); - install_element (RESTRICTED_NODE, &show_bgp_ipv6_safi_prefix_cmd); + install_element (RESTRICTED_NODE, &show_bgp_route_pathtype_cmd); + install_element (RESTRICTED_NODE, &show_bgp_ipv6_route_pathtype_cmd); + install_element (RESTRICTED_NODE, &show_bgp_ipv6_safi_route_pathtype_cmd); + install_element (RESTRICTED_NODE, &show_bgp_prefix_pathtype_cmd); + install_element (RESTRICTED_NODE, &show_bgp_ipv6_prefix_pathtype_cmd); + install_element (RESTRICTED_NODE, &show_bgp_ipv6_safi_prefix_pathtype_cmd); install_element (RESTRICTED_NODE, &show_bgp_community_cmd); - install_element (RESTRICTED_NODE, &show_bgp_ipv6_community_cmd); install_element (RESTRICTED_NODE, &show_bgp_community2_cmd); - install_element (RESTRICTED_NODE, &show_bgp_ipv6_community2_cmd); install_element (RESTRICTED_NODE, &show_bgp_community3_cmd); - install_element (RESTRICTED_NODE, &show_bgp_ipv6_community3_cmd); install_element (RESTRICTED_NODE, &show_bgp_community4_cmd); - install_element (RESTRICTED_NODE, &show_bgp_ipv6_community4_cmd); install_element (RESTRICTED_NODE, &show_bgp_community_exact_cmd); - install_element (RESTRICTED_NODE, &show_bgp_ipv6_community_exact_cmd); install_element (RESTRICTED_NODE, &show_bgp_community2_exact_cmd); - install_element (RESTRICTED_NODE, &show_bgp_ipv6_community2_exact_cmd); install_element (RESTRICTED_NODE, &show_bgp_community3_exact_cmd); - install_element (RESTRICTED_NODE, &show_bgp_ipv6_community3_exact_cmd); install_element (RESTRICTED_NODE, &show_bgp_community4_exact_cmd); - install_element (RESTRICTED_NODE, &show_bgp_ipv6_community4_exact_cmd); - install_element (RESTRICTED_NODE, &show_bgp_rsclient_route_cmd); - install_element (RESTRICTED_NODE, &show_bgp_ipv6_safi_rsclient_route_cmd); - install_element (RESTRICTED_NODE, &show_bgp_rsclient_prefix_cmd); - install_element (RESTRICTED_NODE, &show_bgp_ipv6_safi_rsclient_prefix_cmd); + install_element (RESTRICTED_NODE, &show_bgp_lcommunity_cmd); + install_element (RESTRICTED_NODE, &show_bgp_lcommunity2_cmd); + install_element (RESTRICTED_NODE, &show_bgp_lcommunity3_cmd); + install_element (RESTRICTED_NODE, &show_bgp_lcommunity4_cmd); install_element (RESTRICTED_NODE, &show_bgp_view_route_cmd); - install_element (RESTRICTED_NODE, &show_bgp_view_ipv6_route_cmd); install_element (RESTRICTED_NODE, &show_bgp_view_prefix_cmd); - install_element (RESTRICTED_NODE, &show_bgp_view_ipv6_prefix_cmd); install_element (RESTRICTED_NODE, &show_bgp_view_neighbor_received_prefix_filter_cmd); - install_element (RESTRICTED_NODE, &show_bgp_view_ipv6_neighbor_received_prefix_filter_cmd); - install_element (RESTRICTED_NODE, &show_bgp_view_rsclient_route_cmd); - install_element (RESTRICTED_NODE, &show_bgp_view_ipv6_safi_rsclient_route_cmd); - install_element (RESTRICTED_NODE, &show_bgp_view_rsclient_prefix_cmd); - install_element (RESTRICTED_NODE, &show_bgp_view_ipv6_safi_rsclient_prefix_cmd); - install_element (ENABLE_NODE, &show_bgp_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv6_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv6_safi_cmd); - install_element (ENABLE_NODE, &show_bgp_route_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv6_route_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv6_safi_route_cmd); - install_element (ENABLE_NODE, &show_bgp_prefix_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv6_prefix_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv6_safi_prefix_cmd); - install_element (ENABLE_NODE, &show_bgp_regexp_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv6_regexp_cmd); - install_element (ENABLE_NODE, &show_bgp_prefix_list_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv6_prefix_list_cmd); - install_element (ENABLE_NODE, &show_bgp_filter_list_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv6_filter_list_cmd); - install_element (ENABLE_NODE, &show_bgp_route_map_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv6_route_map_cmd); - install_element (ENABLE_NODE, &show_bgp_community_all_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv6_community_all_cmd); - install_element (ENABLE_NODE, &show_bgp_community_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv6_community_cmd); - install_element (ENABLE_NODE, &show_bgp_community2_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv6_community2_cmd); - install_element (ENABLE_NODE, &show_bgp_community3_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv6_community3_cmd); - install_element (ENABLE_NODE, &show_bgp_community4_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv6_community4_cmd); - install_element (ENABLE_NODE, &show_bgp_community_exact_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv6_community_exact_cmd); - install_element (ENABLE_NODE, &show_bgp_community2_exact_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv6_community2_exact_cmd); - install_element (ENABLE_NODE, &show_bgp_community3_exact_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv6_community3_exact_cmd); - install_element (ENABLE_NODE, &show_bgp_community4_exact_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv6_community4_exact_cmd); - install_element (ENABLE_NODE, &show_bgp_community_list_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv6_community_list_cmd); - install_element (ENABLE_NODE, &show_bgp_community_list_exact_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv6_community_list_exact_cmd); - install_element (ENABLE_NODE, &show_bgp_prefix_longer_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv6_prefix_longer_cmd); - install_element (ENABLE_NODE, &show_bgp_neighbor_advertised_route_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv6_neighbor_advertised_route_cmd); - install_element (ENABLE_NODE, &show_bgp_neighbor_received_routes_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv6_neighbor_received_routes_cmd); - install_element (ENABLE_NODE, &show_bgp_neighbor_routes_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv6_neighbor_routes_cmd); - install_element (ENABLE_NODE, &show_bgp_neighbor_received_prefix_filter_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv6_neighbor_received_prefix_filter_cmd); - install_element (ENABLE_NODE, &show_bgp_neighbor_flap_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv6_neighbor_flap_cmd); - install_element (ENABLE_NODE, &show_bgp_neighbor_damp_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv6_neighbor_damp_cmd); - install_element (ENABLE_NODE, &show_bgp_rsclient_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv6_safi_rsclient_cmd); - install_element (ENABLE_NODE, &show_bgp_rsclient_route_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv6_safi_rsclient_route_cmd); - install_element (ENABLE_NODE, &show_bgp_rsclient_prefix_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv6_safi_rsclient_prefix_cmd); - install_element (ENABLE_NODE, &show_bgp_view_cmd); - install_element (ENABLE_NODE, &show_bgp_view_ipv6_cmd); - install_element (ENABLE_NODE, &show_bgp_view_route_cmd); - install_element (ENABLE_NODE, &show_bgp_view_ipv6_route_cmd); - install_element (ENABLE_NODE, &show_bgp_view_prefix_cmd); - install_element (ENABLE_NODE, &show_bgp_view_ipv6_prefix_cmd); - install_element (ENABLE_NODE, &show_bgp_view_neighbor_advertised_route_cmd); - install_element (ENABLE_NODE, &show_bgp_view_ipv6_neighbor_advertised_route_cmd); - install_element (ENABLE_NODE, &show_bgp_view_neighbor_received_routes_cmd); - install_element (ENABLE_NODE, &show_bgp_view_ipv6_neighbor_received_routes_cmd); - install_element (ENABLE_NODE, &show_bgp_view_neighbor_routes_cmd); - install_element (ENABLE_NODE, &show_bgp_view_ipv6_neighbor_routes_cmd); - install_element (ENABLE_NODE, &show_bgp_view_neighbor_received_prefix_filter_cmd); - install_element (ENABLE_NODE, &show_bgp_view_ipv6_neighbor_received_prefix_filter_cmd); - install_element (ENABLE_NODE, &show_bgp_view_neighbor_flap_cmd); - install_element (ENABLE_NODE, &show_bgp_view_ipv6_neighbor_flap_cmd); - install_element (ENABLE_NODE, &show_bgp_view_neighbor_damp_cmd); - install_element (ENABLE_NODE, &show_bgp_view_ipv6_neighbor_damp_cmd); - install_element (ENABLE_NODE, &show_bgp_view_rsclient_cmd); - install_element (ENABLE_NODE, &show_bgp_view_ipv6_safi_rsclient_cmd); - install_element (ENABLE_NODE, &show_bgp_view_rsclient_route_cmd); - install_element (ENABLE_NODE, &show_bgp_view_ipv6_safi_rsclient_route_cmd); - install_element (ENABLE_NODE, &show_bgp_view_rsclient_prefix_cmd); - install_element (ENABLE_NODE, &show_bgp_view_ipv6_safi_rsclient_prefix_cmd); - - /* Statistics */ - install_element (ENABLE_NODE, &show_bgp_statistics_cmd); install_element (ENABLE_NODE, &show_bgp_statistics_vpnv4_cmd); - install_element (ENABLE_NODE, &show_bgp_statistics_view_cmd); install_element (ENABLE_NODE, &show_bgp_statistics_view_vpnv4_cmd); - - /* old command */ + install_element (VIEW_NODE, &show_ipv6_bgp_cmd); install_element (VIEW_NODE, &show_ipv6_bgp_route_cmd); install_element (VIEW_NODE, &show_ipv6_bgp_prefix_cmd); @@ -12824,6 +17902,12 @@ bgp_route_init (void) install_element (VIEW_NODE, &show_ipv6_bgp_community4_exact_cmd); install_element (VIEW_NODE, &show_ipv6_bgp_community_list_cmd); install_element (VIEW_NODE, &show_ipv6_bgp_community_list_exact_cmd); + install_element (VIEW_NODE, &show_ipv6_bgp_lcommunity_all_cmd); + install_element (VIEW_NODE, &show_ipv6_bgp_lcommunity_cmd); + install_element (VIEW_NODE, &show_ipv6_bgp_lcommunity2_cmd); + install_element (VIEW_NODE, &show_ipv6_bgp_lcommunity3_cmd); + install_element (VIEW_NODE, &show_ipv6_bgp_lcommunity4_cmd); + install_element (VIEW_NODE, &show_ipv6_bgp_lcommunity_list_cmd); install_element (VIEW_NODE, &show_ipv6_bgp_prefix_longer_cmd); install_element (VIEW_NODE, &show_ipv6_mbgp_cmd); install_element (VIEW_NODE, &show_ipv6_mbgp_route_cmd); @@ -12842,131 +17926,59 @@ bgp_route_init (void) install_element (VIEW_NODE, &show_ipv6_mbgp_community4_exact_cmd); install_element (VIEW_NODE, &show_ipv6_mbgp_community_list_cmd); install_element (VIEW_NODE, &show_ipv6_mbgp_community_list_exact_cmd); + install_element (VIEW_NODE, &show_ipv6_mbgp_lcommunity_all_cmd); + install_element (VIEW_NODE, &show_ipv6_mbgp_lcommunity_cmd); + install_element (VIEW_NODE, &show_ipv6_mbgp_lcommunity2_cmd); + install_element (VIEW_NODE, &show_ipv6_mbgp_lcommunity3_cmd); + install_element (VIEW_NODE, &show_ipv6_mbgp_lcommunity4_cmd); + install_element (VIEW_NODE, &show_ipv6_mbgp_lcommunity_list_cmd); install_element (VIEW_NODE, &show_ipv6_mbgp_prefix_longer_cmd); - - /* old command */ - install_element (ENABLE_NODE, &show_ipv6_bgp_cmd); - install_element (ENABLE_NODE, &show_ipv6_bgp_route_cmd); - install_element (ENABLE_NODE, &show_ipv6_bgp_prefix_cmd); - install_element (ENABLE_NODE, &show_ipv6_bgp_regexp_cmd); - install_element (ENABLE_NODE, &show_ipv6_bgp_prefix_list_cmd); - install_element (ENABLE_NODE, &show_ipv6_bgp_filter_list_cmd); - install_element (ENABLE_NODE, &show_ipv6_bgp_community_all_cmd); - install_element (ENABLE_NODE, &show_ipv6_bgp_community_cmd); - install_element (ENABLE_NODE, &show_ipv6_bgp_community2_cmd); - install_element (ENABLE_NODE, &show_ipv6_bgp_community3_cmd); - install_element (ENABLE_NODE, &show_ipv6_bgp_community4_cmd); - install_element (ENABLE_NODE, &show_ipv6_bgp_community_exact_cmd); - install_element (ENABLE_NODE, &show_ipv6_bgp_community2_exact_cmd); - install_element (ENABLE_NODE, &show_ipv6_bgp_community3_exact_cmd); - install_element (ENABLE_NODE, &show_ipv6_bgp_community4_exact_cmd); - install_element (ENABLE_NODE, &show_ipv6_bgp_community_list_cmd); - install_element (ENABLE_NODE, &show_ipv6_bgp_community_list_exact_cmd); - install_element (ENABLE_NODE, &show_ipv6_bgp_prefix_longer_cmd); - install_element (ENABLE_NODE, &show_ipv6_mbgp_cmd); - install_element (ENABLE_NODE, &show_ipv6_mbgp_route_cmd); - install_element (ENABLE_NODE, &show_ipv6_mbgp_prefix_cmd); - install_element (ENABLE_NODE, &show_ipv6_mbgp_regexp_cmd); - install_element (ENABLE_NODE, &show_ipv6_mbgp_prefix_list_cmd); - install_element (ENABLE_NODE, &show_ipv6_mbgp_filter_list_cmd); - install_element (ENABLE_NODE, &show_ipv6_mbgp_community_all_cmd); - install_element (ENABLE_NODE, &show_ipv6_mbgp_community_cmd); - install_element (ENABLE_NODE, &show_ipv6_mbgp_community2_cmd); - install_element (ENABLE_NODE, &show_ipv6_mbgp_community3_cmd); - install_element (ENABLE_NODE, &show_ipv6_mbgp_community4_cmd); - install_element (ENABLE_NODE, &show_ipv6_mbgp_community_exact_cmd); - install_element (ENABLE_NODE, &show_ipv6_mbgp_community2_exact_cmd); - install_element (ENABLE_NODE, &show_ipv6_mbgp_community3_exact_cmd); - install_element (ENABLE_NODE, &show_ipv6_mbgp_community4_exact_cmd); - install_element (ENABLE_NODE, &show_ipv6_mbgp_community_list_cmd); - install_element (ENABLE_NODE, &show_ipv6_mbgp_community_list_exact_cmd); - install_element (ENABLE_NODE, &show_ipv6_mbgp_prefix_longer_cmd); - - /* old command */ install_element (VIEW_NODE, &ipv6_bgp_neighbor_advertised_route_cmd); - install_element (ENABLE_NODE, &ipv6_bgp_neighbor_advertised_route_cmd); install_element (VIEW_NODE, &ipv6_mbgp_neighbor_advertised_route_cmd); - install_element (ENABLE_NODE, &ipv6_mbgp_neighbor_advertised_route_cmd); - - /* old command */ install_element (VIEW_NODE, &ipv6_bgp_neighbor_received_routes_cmd); - install_element (ENABLE_NODE, &ipv6_bgp_neighbor_received_routes_cmd); install_element (VIEW_NODE, &ipv6_mbgp_neighbor_received_routes_cmd); - install_element (ENABLE_NODE, &ipv6_mbgp_neighbor_received_routes_cmd); - - /* old command */ install_element (VIEW_NODE, &ipv6_bgp_neighbor_routes_cmd); - install_element (ENABLE_NODE, &ipv6_bgp_neighbor_routes_cmd); install_element (VIEW_NODE, &ipv6_mbgp_neighbor_routes_cmd); - install_element (ENABLE_NODE, &ipv6_mbgp_neighbor_routes_cmd); -#endif /* HAVE_IPV6 */ + /* old with name safi collision */ + install_element (VIEW_NODE, &show_bgp_ipv6_community_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_community2_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_community3_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_community4_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_community_exact_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_community2_exact_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_community3_exact_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_community4_exact_cmd); + install_element (RESTRICTED_NODE, &show_bgp_ipv6_community_cmd); + install_element (RESTRICTED_NODE, &show_bgp_ipv6_community2_cmd); + install_element (RESTRICTED_NODE, &show_bgp_ipv6_community3_cmd); + install_element (RESTRICTED_NODE, &show_bgp_ipv6_community4_cmd); + install_element (RESTRICTED_NODE, &show_bgp_ipv6_community_exact_cmd); + install_element (RESTRICTED_NODE, &show_bgp_ipv6_community2_exact_cmd); + install_element (RESTRICTED_NODE, &show_bgp_ipv6_community3_exact_cmd); + install_element (RESTRICTED_NODE, &show_bgp_ipv6_community4_exact_cmd); - install_element (BGP_NODE, &bgp_distance_cmd); - install_element (BGP_NODE, &no_bgp_distance_cmd); - install_element (BGP_NODE, &no_bgp_distance2_cmd); - install_element (BGP_NODE, &bgp_distance_source_cmd); - install_element (BGP_NODE, &no_bgp_distance_source_cmd); - install_element (BGP_NODE, &bgp_distance_source_access_list_cmd); - install_element (BGP_NODE, &no_bgp_distance_source_access_list_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_community_list_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_community_list_exact_cmd); - install_element (BGP_NODE, &bgp_damp_set_cmd); - install_element (BGP_NODE, &bgp_damp_set2_cmd); - install_element (BGP_NODE, &bgp_damp_set3_cmd); - install_element (BGP_NODE, &bgp_damp_unset_cmd); - install_element (BGP_NODE, &bgp_damp_unset2_cmd); - install_element (BGP_IPV4_NODE, &bgp_damp_set_cmd); - install_element (BGP_IPV4_NODE, &bgp_damp_set2_cmd); - install_element (BGP_IPV4_NODE, &bgp_damp_set3_cmd); - install_element (BGP_IPV4_NODE, &bgp_damp_unset_cmd); - install_element (BGP_IPV4_NODE, &bgp_damp_unset2_cmd); - - /* Deprecated AS-Pathlimit commands */ - install_element (BGP_NODE, &bgp_network_ttl_cmd); - install_element (BGP_NODE, &bgp_network_mask_ttl_cmd); - install_element (BGP_NODE, &bgp_network_mask_natural_ttl_cmd); - install_element (BGP_NODE, &bgp_network_backdoor_ttl_cmd); - install_element (BGP_NODE, &bgp_network_mask_backdoor_ttl_cmd); - install_element (BGP_NODE, &bgp_network_mask_natural_backdoor_ttl_cmd); - - install_element (BGP_NODE, &no_bgp_network_ttl_cmd); - install_element (BGP_NODE, &no_bgp_network_mask_ttl_cmd); - install_element (BGP_NODE, &no_bgp_network_mask_natural_ttl_cmd); - install_element (BGP_NODE, &no_bgp_network_backdoor_ttl_cmd); - install_element (BGP_NODE, &no_bgp_network_mask_backdoor_ttl_cmd); - install_element (BGP_NODE, &no_bgp_network_mask_natural_backdoor_ttl_cmd); - - install_element (BGP_IPV4_NODE, &bgp_network_ttl_cmd); - install_element (BGP_IPV4_NODE, &bgp_network_mask_ttl_cmd); - install_element (BGP_IPV4_NODE, &bgp_network_mask_natural_ttl_cmd); - install_element (BGP_IPV4_NODE, &bgp_network_backdoor_ttl_cmd); - install_element (BGP_IPV4_NODE, &bgp_network_mask_backdoor_ttl_cmd); - install_element (BGP_IPV4_NODE, &bgp_network_mask_natural_backdoor_ttl_cmd); - - install_element (BGP_IPV4_NODE, &no_bgp_network_ttl_cmd); - install_element (BGP_IPV4_NODE, &no_bgp_network_mask_ttl_cmd); - install_element (BGP_IPV4_NODE, &no_bgp_network_mask_natural_ttl_cmd); - install_element (BGP_IPV4_NODE, &no_bgp_network_backdoor_ttl_cmd); - install_element (BGP_IPV4_NODE, &no_bgp_network_mask_backdoor_ttl_cmd); - install_element (BGP_IPV4_NODE, &no_bgp_network_mask_natural_backdoor_ttl_cmd); - - install_element (BGP_IPV4M_NODE, &bgp_network_ttl_cmd); - install_element (BGP_IPV4M_NODE, &bgp_network_mask_ttl_cmd); - install_element (BGP_IPV4M_NODE, &bgp_network_mask_natural_ttl_cmd); - install_element (BGP_IPV4M_NODE, &bgp_network_backdoor_ttl_cmd); - install_element (BGP_IPV4M_NODE, &bgp_network_mask_backdoor_ttl_cmd); - install_element (BGP_IPV4M_NODE, &bgp_network_mask_natural_backdoor_ttl_cmd); - - install_element (BGP_IPV4M_NODE, &no_bgp_network_ttl_cmd); - install_element (BGP_IPV4M_NODE, &no_bgp_network_mask_ttl_cmd); - install_element (BGP_IPV4M_NODE, &no_bgp_network_mask_natural_ttl_cmd); - install_element (BGP_IPV4M_NODE, &no_bgp_network_backdoor_ttl_cmd); - install_element (BGP_IPV4M_NODE, &no_bgp_network_mask_backdoor_ttl_cmd); - install_element (BGP_IPV4M_NODE, &no_bgp_network_mask_natural_backdoor_ttl_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_lcommunity_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_lcommunity2_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_lcommunity3_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_lcommunity4_cmd); + install_element (RESTRICTED_NODE, &show_bgp_ipv6_lcommunity_cmd); + install_element (RESTRICTED_NODE, &show_bgp_ipv6_lcommunity2_cmd); + install_element (RESTRICTED_NODE, &show_bgp_ipv6_lcommunity3_cmd); + install_element (RESTRICTED_NODE, &show_bgp_ipv6_lcommunity4_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_lcommunity_list_cmd); -#ifdef HAVE_IPV6 - install_element (BGP_IPV6_NODE, &ipv6_bgp_network_ttl_cmd); - install_element (BGP_IPV6_NODE, &no_ipv6_bgp_network_ttl_cmd); -#endif + install_element (VIEW_NODE, &show_bgp_rsclient_route_cmd); + install_element (VIEW_NODE, &show_bgp_rsclient_prefix_cmd); + install_element (RESTRICTED_NODE, &show_bgp_rsclient_route_cmd); + install_element (RESTRICTED_NODE, &show_bgp_rsclient_prefix_cmd); + + install_element (VIEW_NODE, &show_bgp_view_rsclient_route_cmd); + install_element (VIEW_NODE, &show_bgp_view_rsclient_prefix_cmd); + install_element (RESTRICTED_NODE, &show_bgp_view_rsclient_route_cmd); + install_element (RESTRICTED_NODE, &show_bgp_view_rsclient_prefix_cmd); } void diff --git a/bgpd/bgp_route.h b/bgpd/bgp_route.h index 7adc573b8..332714c4f 100644 --- a/bgpd/bgp_route.h +++ b/bgpd/bgp_route.h @@ -21,8 +21,11 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #ifndef _QUAGGA_BGP_ROUTE_H #define _QUAGGA_BGP_ROUTE_H +#include "queue.h" #include "bgp_table.h" +struct bgp_nexthop_cache; + /* Ancillary information to struct bgp_info, * used for uncommonly used data (aggregation, MPLS, etc.) * and lazily allocated to save memory. @@ -47,7 +50,16 @@ struct bgp_info /* For linked list. */ struct bgp_info *next; struct bgp_info *prev; - + + /* For nexthop linked list */ + LIST_ENTRY(bgp_info) nh_thread; + + /* Back pointer to the prefix node */ + struct bgp_node *net; + + /* Back pointer to the nexthop structure */ + struct bgp_nexthop_cache *nexthop; + /* Peer structure. */ struct peer *peer; @@ -57,6 +69,10 @@ struct bgp_info /* Extra information */ struct bgp_info_extra *extra; + + /* Multipath information */ + struct bgp_info_mpath *mpath; + /* Uptime. */ time_t uptime; @@ -76,6 +92,8 @@ struct bgp_info #define BGP_INFO_STALE (1 << 8) #define BGP_INFO_REMOVED (1 << 9) #define BGP_INFO_COUNTED (1 << 10) +#define BGP_INFO_MULTIPATH (1 << 11) +#define BGP_INFO_MULTIPATH_CHG (1 << 12) /* BGP route type. This can be static, RIP, OSPF, BGP etc. */ u_char type; @@ -114,10 +132,17 @@ struct bgp_static struct route_map *map; } rmap; + /* Route Distinguisher */ + struct prefix_rd prd; + /* MPLS label. */ u_char tag[3]; }; +#define BGP_INFO_COUNTABLE(BI) \ + (! CHECK_FLAG ((BI)->flags, BGP_INFO_HISTORY) \ + && ! CHECK_FLAG ((BI)->flags, BGP_INFO_REMOVED)) + /* Flags which indicate a route is unuseable in some form */ #define BGP_INFO_UNUSEABLE \ (BGP_INFO_HISTORY|BGP_INFO_DAMPED|BGP_INFO_REMOVED) @@ -163,6 +188,13 @@ enum bgp_clear_route_type BGP_CLEAR_ROUTE_MY_RSCLIENT }; +enum bgp_path_type +{ + BGP_PATH_ALL, + BGP_PATH_BESTPATH, + BGP_PATH_MULTIPATH +}; + /* Prototypes. */ extern void bgp_route_init (void); extern void bgp_route_finish (void); @@ -187,14 +219,13 @@ extern struct bgp_info_extra *bgp_info_extra_get (struct bgp_info *); extern void bgp_info_set_flag (struct bgp_node *, struct bgp_info *, u_int32_t); extern void bgp_info_unset_flag (struct bgp_node *, struct bgp_info *, u_int32_t); -extern int bgp_nlri_sanity_check (struct peer *, int, u_char *, bgp_size_t); -extern int bgp_nlri_parse (struct peer *, struct attr *, struct bgp_nlri *); +extern int bgp_nlri_parse_ip (struct peer *, struct attr *, struct bgp_nlri *); extern int bgp_maximum_prefix_overflow (struct peer *, afi_t, safi_t, int); extern void bgp_redistribute_add (struct prefix *, const struct in_addr *, const struct in6_addr *, - u_int32_t, u_char); + u_int32_t, u_char, route_tag_t); extern void bgp_redistribute_delete (struct prefix *, u_char); extern void bgp_redistribute_withdraw (struct bgp *, afi_t, int); @@ -203,10 +234,10 @@ extern void bgp_static_update (struct bgp *, struct prefix *, struct bgp_static afi_t, safi_t); extern void bgp_static_withdraw (struct bgp *, struct prefix *, afi_t, safi_t); -extern int bgp_static_set_vpnv4 (struct vty *vty, const char *, - const char *, const char *); +extern int bgp_static_set_safi (safi_t safi, struct vty *vty, const char *, + const char *, const char *, const char *); -extern int bgp_static_unset_vpnv4 (struct vty *, const char *, +extern int bgp_static_unset_safi (safi_t safi, struct vty *, const char *, const char *, const char *); /* this is primarily for MPLS-VPN */ @@ -219,7 +250,7 @@ extern int bgp_withdraw (struct peer *, struct prefix *, struct attr *, /* for bgp_nexthop and bgp_damp */ extern void bgp_process (struct bgp *, struct bgp_node *, afi_t, safi_t); extern int bgp_config_write_network (struct vty *, struct bgp *, afi_t, safi_t, int *); -extern int bgp_config_write_distance (struct vty *, struct bgp *); +extern int bgp_config_write_distance (struct vty *, struct bgp *, afi_t, safi_t, int *); extern void bgp_aggregate_increment (struct bgp *, struct prefix *, struct bgp_info *, afi_t, safi_t); @@ -227,6 +258,7 @@ extern void bgp_aggregate_decrement (struct bgp *, struct prefix *, struct bgp_i afi_t, safi_t); extern u_char bgp_distance_apply (struct prefix *, struct bgp_info *, struct bgp *); +extern u_char ipv6_bgp_distance_apply (struct prefix *, struct bgp_info *, struct bgp *); extern afi_t bgp_node_afi (struct vty *); extern safi_t bgp_node_safi (struct vty *); @@ -235,4 +267,7 @@ extern void route_vty_out (struct vty *, struct prefix *, struct bgp_info *, int extern void route_vty_out_tag (struct vty *, struct prefix *, struct bgp_info *, int, safi_t); extern void route_vty_out_tmp (struct vty *, struct prefix *, struct attr *, safi_t); +extern void bgp_peer_clear_node_queue_drain_immediate (struct peer *peer); +extern void bgp_process_queues_drain_immediate (void); + #endif /* _QUAGGA_BGP_ROUTE_H */ diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c index abb85fd21..ccd73b6c5 100644 --- a/bgpd/bgp_routemap.c +++ b/bgpd/bgp_routemap.c @@ -51,6 +51,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "bgpd/bgp_filter.h" #include "bgpd/bgp_mplsvpn.h" #include "bgpd/bgp_ecommunity.h" +#include "bgpd/bgp_lcommunity.h" #include "bgpd/bgp_vty.h" /* Memo of route-map commands. @@ -59,6 +60,7 @@ o Cisco route-map match as-path : Done community : Done + lcommunity : Done interface : Not yet ip address : Done ip next-hop : Done @@ -71,12 +73,15 @@ o Cisco route-map length : (This will not be implemented by bgpd) metric : Done route-type : (This will not be implemented by bgpd) - tag : (This will not be implemented by bgpd) + tag : Done + local-preference : Done set as-path prepend : Done as-path tag : Not yet automatic-tag : (This will not be implemented by bgpd) community : Done + large-community : Done + large-comm-list : Done comm-list : Not yet dampning : Not yet default : (This will not be implemented by bgpd) @@ -90,17 +95,139 @@ o Cisco route-map metric : Done metric-type : Not yet origin : Done - tag : (This will not be implemented by bgpd) + tag : Done 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 value manipulation to be shared in multiple rules */ + +#define RMAP_VALUE_SET 0 +#define RMAP_VALUE_ADD 1 +#define RMAP_VALUE_SUB 2 + +struct rmap_value +{ + u_int8_t action; + u_int8_t variable; + u_int32_t value; +}; + +static int +route_value_match (struct rmap_value *rv, u_int32_t value) +{ + if (rv->variable == 0 && value == rv->value) + return RMAP_MATCH; + + return RMAP_NOMATCH; +} + +static u_int32_t +route_value_adjust (struct rmap_value *rv, u_int32_t current, struct peer *peer) +{ + u_int32_t value; + + switch (rv->variable) + { + case 1: + value = peer->rtt; + break; + default: + value = rv->value; + break; + } + + switch (rv->action) + { + case RMAP_VALUE_ADD: + if (current > UINT32_MAX-value) + return UINT32_MAX; + return current + value; + case RMAP_VALUE_SUB: + if (current <= value) + return 0; + return current - value; + default: + return value; + } +} + +static void * +route_value_compile (const char *arg) +{ + u_int8_t action = RMAP_VALUE_SET, var = 0; + unsigned long larg = 0; + char *endptr = NULL; + struct rmap_value *rv; + + if (arg[0] == '+') + { + action = RMAP_VALUE_ADD; + arg++; + } + else if (arg[0] == '-') + { + action = RMAP_VALUE_SUB; + arg++; + } + + if (all_digit(arg)) + { + errno = 0; + larg = strtoul (arg, &endptr, 10); + if (*arg == 0 || *endptr != 0 || errno || larg > UINT32_MAX) + return NULL; + } + else + { + if (strcmp(arg, "rtt") == 0) + var = 1; + else + return NULL; + } + + rv = XMALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof(struct rmap_value)); + if (!rv) + return NULL; + + rv->action = action; + rv->variable = var; + rv->value = larg; + return rv; +} + +static void +route_value_free (void *rule) +{ + XFREE (MTYPE_ROUTE_MAP_COMPILED, rule); +} + + /* 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 @@ -111,7 +238,8 @@ route_match_peer (void *rule, struct prefix *prefix, route_map_object_t type, void *object) { union sockunion *su; - union sockunion *su2; + union sockunion su_def = { .sin = { .sin_family = AF_INET, + .sin_addr.s_addr = INADDR_ANY } }; struct peer_group *group; struct peer *peer; struct listnode *node, *nnode; @@ -127,8 +255,7 @@ route_match_peer (void *rule, struct prefix *prefix, route_map_object_t type, /* If su='0.0.0.0' (command 'match peer local'), and it's a NETWORK, REDISTRIBUTE or DEFAULT_GENERATED route => return RMAP_MATCH */ - su2 = sockunion_str2su ("0.0.0.0"); - if ( sockunion_same (su, su2) ) + if (sockunion_same (su, &su_def)) { int ret; if ( CHECK_FLAG (peer->rmap_type, PEER_RMAP_TYPE_NETWORK) || @@ -137,12 +264,9 @@ route_match_peer (void *rule, struct prefix *prefix, route_map_object_t type, ret = RMAP_MATCH; else ret = RMAP_NOMATCH; - - sockunion_free (su2); return ret; } - sockunion_free (su2); - + if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) { if (sockunion_same (su, &peer->su)) @@ -172,7 +296,7 @@ route_match_peer_compile (const char *arg) su = XMALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof (union sockunion)); - ret = str2sockunion ( (arg)? arg : "0.0.0.0", su); + ret = str2sockunion (strcmp(arg, "local") ? arg : "0.0.0.0", su); if (ret < 0) { XFREE (MTYPE_ROUTE_MAP_COMPILED, su); return NULL; @@ -243,7 +367,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. */ @@ -295,7 +419,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. */ @@ -353,7 +477,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 @@ -393,7 +517,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 @@ -440,7 +564,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 @@ -493,23 +617,23 @@ 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 local-preference LOCAL-PREF' */ /* Match function return 1 if match is success else return zero. */ static route_map_result_t -route_match_metric (void *rule, struct prefix *prefix, - route_map_object_t type, void *object) +route_match_local_pref (void *rule, struct prefix *prefix, + route_map_object_t type, void *object) { - u_int32_t *med; + u_int32_t *local_pref; struct bgp_info *bgp_info; if (type == RMAP_BGP) { - med = rule; + local_pref = rule; bgp_info = object; - - if (bgp_info->attr->med == *med) + + if (bgp_info->attr->local_pref == *local_pref) return RMAP_MATCH; else return RMAP_NOMATCH; @@ -517,15 +641,16 @@ route_match_metric (void *rule, struct prefix *prefix, return RMAP_NOMATCH; } -/* Route map `match metric' match statement. `arg' is MED value */ +/* Route map `match local-preference' match statement. + `arg' is local-pref value */ static void * -route_match_metric_compile (const char *arg) +route_match_local_pref_compile (const char *arg) { - u_int32_t *med; + u_int32_t *local_pref; char *endptr = NULL; unsigned long tmpval; - /* Metric value shoud be integer. */ + /* Locpref value shoud be integer. */ if (! all_digit (arg)) return NULL; @@ -533,32 +658,60 @@ route_match_metric_compile (const char *arg) tmpval = strtoul (arg, &endptr, 10); if (*endptr != '\0' || errno || tmpval > UINT32_MAX) return NULL; - - med = XMALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof (u_int32_t)); - - if (!med) - return med; - - *med = tmpval; - return med; + + local_pref = XMALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof (u_int32_t)); + + if (!local_pref) + return local_pref; + + *local_pref = tmpval; + return local_pref; } -/* Free route map's compiled `match metric' value. */ +/* Free route map's compiled `match local-preference' value. */ static void -route_match_metric_free (void *rule) +route_match_local_pref_free (void *rule) { XFREE (MTYPE_ROUTE_MAP_COMPILED, rule); } +/* Route map commands for metric matching. */ +struct route_map_rule_cmd route_match_local_pref_cmd = +{ + "local-preference", + route_match_local_pref, + route_match_local_pref_compile, + route_match_local_pref_free +}; + +/* `match metric METRIC' */ + +/* Match function return 1 if match is success else return zero. */ +static route_map_result_t +route_match_metric (void *rule, struct prefix *prefix, + route_map_object_t type, void *object) +{ + struct rmap_value *rv; + struct bgp_info *bgp_info; + + if (type == RMAP_BGP) + { + rv = rule; + bgp_info = object; + return route_value_match(rv, bgp_info->attr->med); + } + return RMAP_NOMATCH; +} + /* Route map commands for metric matching. */ struct route_map_rule_cmd route_match_metric_cmd = { "metric", route_match_metric, - route_match_metric_compile, - route_match_metric_free + route_value_compile, + route_value_free, }; - + /* `match as-path ASPATH' */ /* Match function for as-path match. I assume given object is */ @@ -606,7 +759,7 @@ struct route_map_rule_cmd route_match_aspath_cmd = route_match_aspath_compile, route_match_aspath_free }; - + /* `match community COMMUNIY' */ struct rmap_community { @@ -690,22 +843,94 @@ struct route_map_rule_cmd route_match_community_cmd = route_match_community_compile, route_match_community_free }; - + +/* Match function for lcommunity match. */ +static route_map_result_t +route_match_lcommunity (void *rule, struct prefix *prefix, + route_map_object_t type, void *object) +{ + struct community_list *list; + struct bgp_info *bgp_info; + struct rmap_community *rcom; + + if (type == RMAP_BGP) + { + bgp_info = object; + rcom = rule; + + list = community_list_lookup (bgp_clist, rcom->name, + LARGE_COMMUNITY_LIST_MASTER); + if (! list) + return RMAP_NOMATCH; + + if (bgp_info->attr->extra && + lcommunity_list_match (bgp_info->attr->extra->lcommunity, list)) + return RMAP_MATCH; + + } + return RMAP_NOMATCH; +} + +/* Compile function for community match. */ +static void * +route_match_lcommunity_compile (const char *arg) +{ + struct rmap_community *rcom; + int len; + char *p; + + rcom = XCALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof (struct rmap_community)); + + p = strchr (arg, ' '); + if (p) + { + len = p - arg; + rcom->name = XCALLOC (MTYPE_ROUTE_MAP_COMPILED, len + 1); + memcpy (rcom->name, arg, len); + } + else + { + rcom->name = XSTRDUP (MTYPE_ROUTE_MAP_COMPILED, arg); + rcom->exact = 0; + } + return rcom; +} + +/* Compile function for community match. */ +static void +route_match_lcommunity_free (void *rule) +{ + struct rmap_community *rcom = rule; + + XFREE (MTYPE_ROUTE_MAP_COMPILED, rcom->name); + XFREE (MTYPE_ROUTE_MAP_COMPILED, rcom); +} + +/* Route map commands for community matching. */ +struct route_map_rule_cmd route_match_lcommunity_cmd = +{ + "large-community", + route_match_lcommunity, + route_match_lcommunity_compile, + route_match_lcommunity_free +}; + + /* Match function for extcommunity match. */ static route_map_result_t -route_match_ecommunity (void *rule, struct prefix *prefix, +route_match_ecommunity (void *rule, struct prefix *prefix, route_map_object_t type, void *object) { struct community_list *list; struct bgp_info *bgp_info; - if (type == RMAP_BGP) + if (type == RMAP_BGP) { bgp_info = object; - + if (!bgp_info->attr->extra) return RMAP_NOMATCH; - + list = community_list_lookup (bgp_clist, (char *) rule, EXTCOMMUNITY_LIST_MASTER); if (! list) @@ -739,10 +964,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, @@ -802,19 +1027,14 @@ static route_map_result_t route_match_probability (void *rule, struct prefix *prefix, route_map_object_t type, void *object) { - long r; -#if _SVID_SOURCE || _BSD_SOURCE || _XOPEN_SOURCE >= 500 - r = random(); -#else - r = (long) rand(); -#endif + long r = random(); - switch (*(unsigned *) rule) + switch (*(long *) rule) { case 0: break; case RAND_MAX: return RMAP_MATCH; default: - if (r < *(unsigned *) rule) + if (r < *(long *) rule) { return RMAP_MATCH; } @@ -826,17 +1046,11 @@ route_match_probability (void *rule, struct prefix *prefix, static void * route_match_probability_compile (const char *arg) { - unsigned *lobule; + long *lobule; unsigned perc; -#if _SVID_SOURCE || _BSD_SOURCE || _XOPEN_SOURCE >= 500 - srandom (time (NULL)); -#else - srand (time (NULL)); -#endif - perc = atoi (arg); - lobule = XMALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof (unsigned)); + lobule = XMALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof (long)); switch (perc) { @@ -866,6 +1080,38 @@ struct route_map_rule_cmd route_match_probability_cmd = /* `set ip next-hop IP_ADDRESS' */ +/* Match function return 1 if match is success else return zero. */ +static route_map_result_t +route_match_tag (void *rule, struct prefix *prefix, + route_map_object_t type, void *object) +{ + route_tag_t *tag; + struct bgp_info *bgp_info; + + if (type == RMAP_BGP) + { + tag = rule; + bgp_info = object; + + if (!bgp_info->attr->extra) + return RMAP_NOMATCH; + + return ((bgp_info->attr->extra->tag == *tag)? RMAP_MATCH : RMAP_NOMATCH); + } + + return RMAP_NOMATCH; +} + +/* Route map commands for tag matching. */ +static struct route_map_rule_cmd route_match_tag_cmd = +{ + "tag", + route_match_tag, + route_map_rule_tag_compile, + route_map_rule_tag_free, +}; + + /* Set nexthop to object. ojbect must be pointer to struct attr. */ struct rmap_ip_nexthop_set { @@ -878,7 +1124,6 @@ route_set_ip_nexthop (void *rule, struct prefix *prefix, route_map_object_t type, void *object) { struct rmap_ip_nexthop_set *rins = rule; - struct in_addr peer_address; struct bgp_info *bgp_info; struct peer *peer; @@ -894,16 +1139,14 @@ route_set_ip_nexthop (void *rule, struct prefix *prefix, && peer->su_remote && sockunion_family (peer->su_remote) == AF_INET) { - inet_aton (sockunion_su2str (peer->su_remote), &peer_address); - bgp_info->attr->nexthop = peer_address; + bgp_info->attr->nexthop.s_addr = sockunion2ip (peer->su_remote); bgp_info->attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_NEXT_HOP); } else if (CHECK_FLAG (peer->rmap_type, PEER_RMAP_TYPE_OUT) && peer->su_local && sockunion_family (peer->su_local) == AF_INET) { - inet_aton (sockunion_su2str (peer->su_local), &peer_address); - bgp_info->attr->nexthop = peer_address; + bgp_info->attr->nexthop.s_addr = sockunion2ip (peer->su_local); bgp_info->attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_NEXT_HOP); } } @@ -970,7 +1213,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. */ @@ -978,66 +1221,36 @@ static route_map_result_t route_set_local_pref (void *rule, struct prefix *prefix, route_map_object_t type, void *object) { - u_int32_t *local_pref; + struct rmap_value *rv; struct bgp_info *bgp_info; + u_int32_t locpref = 0; if (type == RMAP_BGP) { /* Fetch routemap's rule information. */ - local_pref = rule; + rv = rule; bgp_info = object; /* Set local preference value. */ + if (bgp_info->attr->flag & ATTR_FLAG_BIT (BGP_ATTR_LOCAL_PREF)) + locpref = bgp_info->attr->local_pref; + bgp_info->attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_LOCAL_PREF); - bgp_info->attr->local_pref = *local_pref; + bgp_info->attr->local_pref = route_value_adjust(rv, locpref, bgp_info->peer); } return RMAP_OKAY; } -/* set local preference compilation. */ -static void * -route_set_local_pref_compile (const char *arg) -{ - unsigned long tmp; - u_int32_t *local_pref; - char *endptr = NULL; - - /* Local preference value shoud be integer. */ - if (! all_digit (arg)) - return NULL; - - errno = 0; - tmp = strtoul (arg, &endptr, 10); - if (*endptr != '\0' || errno || tmp > UINT32_MAX) - return NULL; - - local_pref = XMALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof (u_int32_t)); - - if (!local_pref) - return local_pref; - - *local_pref = tmp; - - return local_pref; -} - -/* Free route map's local preference value. */ -static void -route_set_local_pref_free (void *rule) -{ - XFREE (MTYPE_ROUTE_MAP_COMPILED, rule); -} - /* Set local preference rule structure. */ struct route_map_rule_cmd route_set_local_pref_cmd = { "local-preference", route_set_local_pref, - route_set_local_pref_compile, - route_set_local_pref_free, + route_value_compile, + route_value_free, }; - + /* `set weight WEIGHT' */ /* Set weight. */ @@ -1045,18 +1258,20 @@ static route_map_result_t route_set_weight (void *rule, struct prefix *prefix, route_map_object_t type, void *object) { - u_int32_t *weight; + struct rmap_value *rv; struct bgp_info *bgp_info; + u_int32_t weight; if (type == RMAP_BGP) { /* Fetch routemap's rule information. */ - weight = rule; + rv = rule; bgp_info = object; /* Set weight value. */ - if (*weight) - (bgp_attr_extra_get (bgp_info->attr))->weight = *weight; + weight = route_value_adjust(rv, 0, bgp_info->peer); + if (weight) + (bgp_attr_extra_get (bgp_info->attr))->weight = weight; else if (bgp_info->attr->extra) bgp_info->attr->extra->weight = 0; } @@ -1064,49 +1279,15 @@ route_set_weight (void *rule, struct prefix *prefix, route_map_object_t type, return RMAP_OKAY; } -/* set local preference compilation. */ -static void * -route_set_weight_compile (const char *arg) -{ - unsigned long tmp; - u_int32_t *weight; - char *endptr = NULL; - - /* Local preference value shoud be integer. */ - if (! all_digit (arg)) - return NULL; - - errno = 0; - tmp = strtoul (arg, &endptr, 10); - if (*endptr != '\0' || errno || tmp > UINT32_MAX) - return NULL; - - weight = XMALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof (u_int32_t)); - - if (weight == NULL) - return weight; - - *weight = tmp; - - return weight; -} - -/* Free route map's local preference value. */ -static void -route_set_weight_free (void *rule) -{ - XFREE (MTYPE_ROUTE_MAP_COMPILED, rule); -} - /* Set local preference rule structure. */ struct route_map_rule_cmd route_set_weight_cmd = { "weight", route_set_weight, - route_set_weight_compile, - route_set_weight_free, + route_value_compile, + route_value_free, }; - + /* `set metric METRIC' */ /* Set metric to attribute. */ @@ -1114,99 +1295,34 @@ static route_map_result_t route_set_metric (void *rule, struct prefix *prefix, route_map_object_t type, void *object) { - char *metric; - u_int32_t metric_val; + struct rmap_value *rv; struct bgp_info *bgp_info; + u_int32_t med = 0; if (type == RMAP_BGP) { /* Fetch routemap's rule information. */ - metric = rule; + rv = rule; bgp_info = object; - if (! (bgp_info->attr->flag & ATTR_FLAG_BIT (BGP_ATTR_MULTI_EXIT_DISC))) - bgp_info->attr->med = 0; - bgp_info->attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_MULTI_EXIT_DISC); + if (bgp_info->attr->flag & ATTR_FLAG_BIT (BGP_ATTR_MULTI_EXIT_DISC)) + med = bgp_info->attr->med; - if (all_digit (metric)) - { - metric_val = strtoul (metric, (char **)NULL, 10); - bgp_info->attr->med = metric_val; - } - else - { - metric_val = strtoul (metric+1, (char **)NULL, 10); - - if (strncmp (metric, "+", 1) == 0) - { - if (bgp_info->attr->med/2 + metric_val/2 > BGP_MED_MAX/2) - bgp_info->attr->med = BGP_MED_MAX - 1; - else - bgp_info->attr->med += metric_val; - } - else if (strncmp (metric, "-", 1) == 0) - { - if (bgp_info->attr->med <= metric_val) - bgp_info->attr->med = 0; - else - bgp_info->attr->med -= metric_val; - } - } + bgp_info->attr->med = route_value_adjust(rv, med, bgp_info->peer); + bgp_info->attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_MULTI_EXIT_DISC); } return RMAP_OKAY; } -/* set metric compilation. */ -static void * -route_set_metric_compile (const char *arg) -{ - u_int32_t metric; - unsigned long larg; - char *endptr = NULL; - - if (all_digit (arg)) - { - /* set metric value check*/ - errno = 0; - larg = strtoul (arg, &endptr, 10); - if (*endptr != '\0' || errno || larg > UINT32_MAX) - return NULL; - metric = larg; - } - else - { - /* set metric +/-value check */ - if ((strncmp (arg, "+", 1) != 0 - && strncmp (arg, "-", 1) != 0) - || (! all_digit (arg+1))) - return NULL; - - errno = 0; - larg = strtoul (arg+1, &endptr, 10); - if (*endptr != '\0' || errno || larg > UINT32_MAX) - return NULL; - metric = larg; - } - - return XSTRDUP (MTYPE_ROUTE_MAP_COMPILED, arg); -} - -/* Free route map's compiled `set metric' value. */ -static void -route_set_metric_free (void *rule) -{ - XFREE (MTYPE_ROUTE_MAP_COMPILED, rule); -} - /* Set metric rule structure. */ struct route_map_rule_cmd route_set_metric_cmd = { "metric", route_set_metric, - route_set_metric_compile, - route_set_metric_free, + route_value_compile, + route_value_free, }; - + /* `set as-path prepend ASPATH' */ /* For AS path prepend mechanism. */ @@ -1219,7 +1335,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) @@ -1227,34 +1342,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", @@ -1262,7 +1387,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. @@ -1288,39 +1413,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 { @@ -1353,6 +1454,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; } @@ -1441,7 +1545,226 @@ struct route_map_rule_cmd route_set_community_cmd = route_set_community_compile, route_set_community_free, }; - + +/* `set community COMMUNITY' */ +struct rmap_lcom_set +{ + struct lcommunity *lcom; + int additive; + int none; +}; + + +/* For lcommunity set mechanism. */ +static route_map_result_t +route_set_lcommunity (void *rule, struct prefix *prefix, + route_map_object_t type, void *object) +{ + struct rmap_lcom_set *rcs; + struct bgp_info *binfo; + struct attr *attr; + struct lcommunity *new = NULL; + struct lcommunity *old; + struct lcommunity *merge; + + if (type == RMAP_BGP) + { + rcs = rule; + binfo = object; + attr = binfo->attr; + old = (attr->extra) ? attr->extra->lcommunity : NULL; + + /* "none" case. */ + if (rcs->none) + { + attr->flag &= ~(ATTR_FLAG_BIT (BGP_ATTR_LARGE_COMMUNITIES)); + if (attr->extra) { + attr->extra->lcommunity = NULL; + } + /* See the longer comment down below. */ + if (old && old->refcnt == 0) + lcommunity_free(&old); + return RMAP_OKAY; + } + + if (rcs->additive && old) + { + merge = lcommunity_merge (lcommunity_dup (old), rcs->lcom); + + /* HACK: if the old large-community is not intern'd, + * we should free it here, or all reference to it may be lost. + * Really need to cleanup attribute caching sometime. + */ + if (old->refcnt == 0) + lcommunity_free (&old); + new = lcommunity_uniq_sort (merge); + lcommunity_free (&merge); + } + else + { + new = lcommunity_dup (rcs->lcom); + } + + /* will be interned by caller if required */ + bgp_attr_extra_get (attr)->lcommunity = new; + attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_LARGE_COMMUNITIES); + } + + return RMAP_OKAY; +} + +/* Compile function for set community. */ +static void * +route_set_lcommunity_compile (const char *arg) +{ + struct rmap_lcom_set *rcs; + struct lcommunity *lcom = NULL; + char *sp; + int additive = 0; + int none = 0; + + if (strcmp (arg, "none") == 0) + none = 1; + else + { + sp = strstr (arg, "additive"); + + if (sp && sp > arg) + { + /* "additive" keyworkd is included. */ + additive = 1; + *(sp - 1) = '\0'; + } + + lcom = lcommunity_str2com (arg); + + if (additive) + *(sp - 1) = ' '; + + if (! lcom) + return NULL; + } + + rcs = XCALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof (struct rmap_com_set)); + rcs->lcom = lcom; + rcs->additive = additive; + rcs->none = none; + + return rcs; +} + +/* Free function for set lcommunity. */ +static void +route_set_lcommunity_free (void *rule) +{ + struct rmap_lcom_set *rcs = rule; + + if (rcs->lcom) { + lcommunity_free (&rcs->lcom); + } + XFREE (MTYPE_ROUTE_MAP_COMPILED, rcs); +} + +/* Set community rule structure. */ +struct route_map_rule_cmd route_set_lcommunity_cmd = +{ + "large-community", + route_set_lcommunity, + route_set_lcommunity_compile, + route_set_lcommunity_free, +}; + +/* `set large-comm-list (<1-99>|<100-500>|WORD) delete' */ + +/* For large community set mechanism. */ +static route_map_result_t +route_set_lcommunity_delete (void *rule, struct prefix *prefix, + route_map_object_t type, void *object) +{ + struct community_list *list; + struct lcommunity *merge; + struct lcommunity *new; + struct lcommunity *old; + struct bgp_info *binfo; + + if (type == RMAP_BGP) + { + if (! rule) + return RMAP_OKAY; + + binfo = object; + list = community_list_lookup (bgp_clist, rule, + LARGE_COMMUNITY_LIST_MASTER); + old = ((binfo->attr->extra) ? binfo->attr->extra->lcommunity : NULL); + + if (list && old) + { + merge = lcommunity_list_match_delete (lcommunity_dup (old), list); + new = lcommunity_uniq_sort (merge); + lcommunity_free (&merge); + + /* HACK: if the old community is not intern'd, + * we should free it here, or all reference to it may be lost. + * Really need to cleanup attribute caching sometime. + */ + if (old->refcnt == 0) + lcommunity_free (&old); + + if (new->size == 0) + { + binfo->attr->extra->lcommunity = NULL; + binfo->attr->flag &= ~ATTR_FLAG_BIT (BGP_ATTR_LARGE_COMMUNITIES); + lcommunity_free (&new); + } + else + { + binfo->attr->extra->lcommunity = new; + binfo->attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_LARGE_COMMUNITIES); + } + } + } + + return RMAP_OKAY; +} + +/* Compile function for set lcommunity. */ +static void * +route_set_lcommunity_delete_compile (const char *arg) +{ + char *p; + char *str; + int len; + + p = strchr (arg, ' '); + if (p) + { + len = p - arg; + str = XCALLOC (MTYPE_ROUTE_MAP_COMPILED, len + 1); + memcpy (str, arg, len); + } + else + str = NULL; + + return str; +} + +/* Free function for set lcommunity. */ +static void +route_set_lcommunity_delete_free (void *rule) +{ + XFREE (MTYPE_ROUTE_MAP_COMPILED, rule); +} + +/* Set lcommunity rule structure. */ +struct route_map_rule_cmd route_set_lcommunity_delete_cmd = +{ + "large-comm-list", + route_set_lcommunity_delete, + route_set_lcommunity_delete_compile, + route_set_lcommunity_delete_free, +}; + + /* `set comm-list (<1-99>|<100-500>|WORD) delete' */ /* For community set mechanism. */ @@ -1530,13 +1853,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; @@ -1555,14 +1878,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); } @@ -1581,9 +1909,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); @@ -1593,46 +1921,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) @@ -1646,23 +1941,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. */ @@ -1708,7 +1995,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", @@ -1716,7 +2003,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. */ @@ -1757,7 +2044,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 { @@ -1816,8 +2103,40 @@ struct route_map_rule_cmd route_set_aggregator_as_cmd = route_set_aggregator_as_compile, route_set_aggregator_as_free, }; - -#ifdef HAVE_IPV6 + +/* Set tag to object. object must be pointer to struct bgp_info */ +static route_map_result_t +route_set_tag (void *rule, struct prefix *prefix, + route_map_object_t type, void *object) +{ + route_tag_t *tag; + struct bgp_info *bgp_info; + struct attr_extra *ae; + + if (type == RMAP_BGP) + { + tag = rule; + bgp_info = object; + ae = bgp_attr_extra_get (bgp_info->attr); + + /* Set tag value */ + ae->tag=*tag; + + } + + return RMAP_OKAY; +} + +/* Route map commands for tag set. */ +static struct route_map_rule_cmd route_set_tag_cmd = +{ + "tag", + route_set_tag, + route_map_rule_tag_compile, + route_map_rule_tag_free, +}; + + /* `match ipv6 address IP_ACCESS_LIST' */ static route_map_result_t @@ -1858,29 +2177,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; @@ -1920,7 +2238,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 @@ -1960,7 +2278,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. */ @@ -2024,7 +2342,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. */ @@ -2088,8 +2406,90 @@ struct route_map_rule_cmd route_set_ipv6_nexthop_local_cmd = route_set_ipv6_nexthop_local_compile, route_set_ipv6_nexthop_local_free }; -#endif /* HAVE_IPV6 */ - + +/* `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; + + 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) + { + peer_address = peer->su_remote->sin6.sin6_addr; + } + else if (CHECK_FLAG (peer->rmap_type, PEER_RMAP_TYPE_OUT) + && peer->su_local + && sockunion_family (peer->su_local) == AF_INET6) + { + peer_address = peer->su_local->sin6.sin6_addr; + } + + 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 +}; + /* `set vpnv4 nexthop A.B.C.D' */ static route_map_result_t @@ -2107,6 +2507,7 @@ route_set_vpnv4_nexthop (void *rule, struct prefix *prefix, /* Set next hop value. */ (bgp_attr_extra_get (bgp_info->attr))->mp_nexthop_global_in = *address; + (bgp_attr_extra_get (bgp_info->attr))->mp_nexthop_len = 4; } return RMAP_OKAY; @@ -2145,7 +2546,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. */ @@ -2194,7 +2595,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", @@ -2202,7 +2603,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, @@ -2216,10 +2617,10 @@ bgp_route_match_add (struct vty *vty, struct route_map_index *index, switch (ret) { case RMAP_RULE_MISSING: - vty_out (vty, "%% Can't find rule.%s", VTY_NEWLINE); + vty_out (vty, "%% BGP Can't find rule.%s", VTY_NEWLINE); return CMD_WARNING; case RMAP_COMPILE_ERROR: - vty_out (vty, "%% Argument is malformed.%s", VTY_NEWLINE); + vty_out (vty, "%% BGP Argument is malformed.%s", VTY_NEWLINE); return CMD_WARNING; } } @@ -2239,10 +2640,10 @@ bgp_route_match_delete (struct vty *vty, struct route_map_index *index, switch (ret) { case RMAP_RULE_MISSING: - vty_out (vty, "%% Can't find rule.%s", VTY_NEWLINE); + vty_out (vty, "%% BGP Can't find rule.%s", VTY_NEWLINE); return CMD_WARNING; case RMAP_COMPILE_ERROR: - vty_out (vty, "%% Argument is malformed.%s", VTY_NEWLINE); + vty_out (vty, "%% BGP Argument is malformed.%s", VTY_NEWLINE); return CMD_WARNING; } } @@ -2262,10 +2663,10 @@ bgp_route_set_add (struct vty *vty, struct route_map_index *index, switch (ret) { case RMAP_RULE_MISSING: - vty_out (vty, "%% Can't find rule.%s", VTY_NEWLINE); + vty_out (vty, "%% BGP Can't find rule.%s", VTY_NEWLINE); return CMD_WARNING; case RMAP_COMPILE_ERROR: - vty_out (vty, "%% Argument is malformed.%s", VTY_NEWLINE); + vty_out (vty, "%% BGP Argument is malformed.%s", VTY_NEWLINE); return CMD_WARNING; } } @@ -2285,10 +2686,10 @@ bgp_route_set_delete (struct vty *vty, struct route_map_index *index, switch (ret) { case RMAP_RULE_MISSING: - vty_out (vty, "%% Can't find rule.%s", VTY_NEWLINE); + vty_out (vty, "%% BGP Can't find rule.%s", VTY_NEWLINE); return CMD_WARNING; case RMAP_COMPILE_ERROR: - vty_out (vty, "%% Argument is malformed.%s", VTY_NEWLINE); + vty_out (vty, "%% BGP Argument is malformed.%s", VTY_NEWLINE); return CMD_WARNING; } } @@ -2312,6 +2713,9 @@ bgp_route_map_update (const char *unused) struct bgp_node *bn; struct bgp_static *bgp_static; + if (bm->bgp == NULL) /* may be called during cleanup */ + return; + /* For neighbor route-map updates. */ for (ALL_LIST_ELEMENTS (bm->bgp, mnode, mnnode, bgp)) { @@ -2400,25 +2804,23 @@ bgp_route_map_update (const char *unused) { for (i = 0; i < ZEBRA_ROUTE_MAX; i++) { - if (bgp->rmap[ZEBRA_FAMILY_IPV4][i].name) - bgp->rmap[ZEBRA_FAMILY_IPV4][i].map = - route_map_lookup_by_name (bgp->rmap[ZEBRA_FAMILY_IPV4][i].name); -#ifdef HAVE_IPV6 - if (bgp->rmap[ZEBRA_FAMILY_IPV6][i].name) - bgp->rmap[ZEBRA_FAMILY_IPV6][i].map = - route_map_lookup_by_name (bgp->rmap[ZEBRA_FAMILY_IPV6][i].name); -#endif /* HAVE_IPV6 */ + if (bgp->rmap[AFI_IP][i].name) + bgp->rmap[AFI_IP][i].map = + route_map_lookup_by_name (bgp->rmap[AFI_IP][i].name); + if (bgp->rmap[AFI_IP6][i].name) + bgp->rmap[AFI_IP6][i].map = + route_map_lookup_by_name (bgp->rmap[AFI_IP6][i].name); } } } - + DEFUN (match_peer, match_peer_cmd, "match peer (A.B.C.D|X:X::X:X)", MATCH_STR "Match peer address\n" - "IPv6 address of peer\n" - "IP address of peer\n") + "IP address of peer\n" + "IPv6 address of peer\n") { return bgp_route_match_add (vty, vty->index, "peer", argv[0]); } @@ -2430,7 +2832,7 @@ DEFUN (match_peer_local, "Match peer address\n" "Static or Redistributed routes\n") { - return bgp_route_match_add (vty, vty->index, "peer", NULL); + return bgp_route_match_add (vty, vty->index, "peer", "local"); } DEFUN (no_match_peer, @@ -2452,8 +2854,8 @@ ALIAS (no_match_peer, NO_STR MATCH_STR "Match peer address\n" - "IPv6 address of peer\n" - "IP address of peer\n") + "IP address of peer\n" + "IPv6 address of peer\n") ALIAS (no_match_peer, no_match_peer_local_cmd, @@ -2751,6 +3153,37 @@ ALIAS (no_match_metric, "Match metric of route\n" "Metric value\n") +DEFUN (match_local_pref, + match_local_pref_cmd, + "match local-preference <0-4294967295>", + MATCH_STR + "Match local-preference of route\n" + "Metric value\n") +{ + return bgp_route_match_add (vty, vty->index, "local-preference", argv[0]); +} + +DEFUN (no_match_local_pref, + no_match_local_pref_cmd, + "no match local-preference", + NO_STR + MATCH_STR + "Match local preference of route\n") +{ + if (argc == 0) + return bgp_route_match_delete (vty, vty->index, "local-preference", NULL); + + return bgp_route_match_delete (vty, vty->index, "local-preference", argv[0]); +} + +ALIAS (no_match_local_pref, + no_match_local_pref_val_cmd, + "no match local-preference <0-4294967295>", + NO_STR + MATCH_STR + "Match local preference of route\n" + "Local preference value\n") + DEFUN (match_community, match_community_cmd, "match community (<1-99>|<100-500>|WORD)", @@ -2819,6 +3252,32 @@ ALIAS (no_match_community, "Community-list name\n" "Do exact matching of communities\n") +DEFUN (match_lcommunity, + match_lcommunity_cmd, + "match large-community (<1-99>|<100-500>|WORD)", + MATCH_STR + "Match BGP large community list\n" + "Large Community-list number (standard)\n" + "Large Community-list number (expanded)\n" + "Large Community-list name\n") +{ + return bgp_route_match_add (vty, vty->index, "large-community", argv[0]); +} + +DEFUN (no_match_lcommunity, + no_match_lcommunity_cmd, + "no match large-community (<1-99>|<100-500>|WORD)", + NO_STR + MATCH_STR + "Match BGP large community list\n" + "Large Community-list number (standard)\n" + "Large Community-list number (expanded)\n" + "Large Community-list name\n") +{ + return bgp_route_match_delete (vty, vty->index, "large-community", NULL); +} + + DEFUN (match_ecommunity, match_ecommunity_cmd, "match extcommunity (<1-99>|<100-500>|WORD)", @@ -2918,6 +3377,37 @@ ALIAS (no_match_origin, "local IGP\n" "unknown heritage\n") +DEFUN (match_tag, + match_tag_cmd, + "match tag <1-4294967295>", + MATCH_STR + "Match tag of route\n" + "Tag value\n") +{ + return bgp_route_match_add (vty, vty->index, "tag", argv[0]); +} + +DEFUN (no_match_tag, + no_match_tag_cmd, + "no match tag", + NO_STR + MATCH_STR + "Match tag of route\n") +{ + if (argc == 0) + return bgp_route_match_delete (vty, vty->index, "tag", NULL); + + return bgp_route_match_delete (vty, vty->index, "tag", argv[0]); +} + +ALIAS (no_match_tag, + no_match_tag_val_cmd, + "no match tag <1-4294967295>", + NO_STR + MATCH_STR + "Match tag of route\n" + "Tag value\n") + DEFUN (set_ip_nexthop, set_ip_nexthop_cmd, "set ip next-hop A.B.C.D", @@ -3002,6 +3492,15 @@ ALIAS (set_metric, "Metric value for destination routing protocol\n" "Add or subtract metric\n") +ALIAS (set_metric, + set_metric_rtt_cmd, + "set metric (rtt|+rtt|-rtt)", + SET_STR + "Metric value for destination routing protocol\n" + "Assign round trip time\n" + "Add round trip time\n" + "Subtract round trip time\n") + DEFUN (no_set_metric, no_set_metric_cmd, "no set metric", @@ -3103,6 +3602,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", @@ -3313,7 +3821,7 @@ DEFUN (set_community_delete, SET_STR "set BGP community list (for deletion)\n" "Community-list number (standard)\n" - "Communitly-list number (expanded)\n" + "Community-list number (expanded)\n" "Community-list name\n" "Delete matching communities\n") { @@ -3346,10 +3854,108 @@ ALIAS (no_set_community_delete, SET_STR "set BGP community list (for deletion)\n" "Community-list number (standard)\n" - "Communitly-list number (expanded)\n" + "Community-list number (expanded)\n" "Community-list name\n" "Delete matching communities\n") + +DEFUN (set_lcommunity, + set_lcommunity_cmd, + "set large-community .AA:BB:CC", + SET_STR + "BGP large community attribute\n" + "Large Community number in aa:bb:cc format or additive\n") +{ + int ret; + char *str; + + str = argv_concat (argv, argc, 0); + ret = bgp_route_set_add (vty, vty->index, "large-community", str); + XFREE (MTYPE_TMP, str); + + return ret; +} + +DEFUN (set_lcommunity_none, + set_lcommunity_none_cmd, + "set large-community none", + SET_STR + "BGP large community attribute\n" + "No large community attribute\n") +{ + return bgp_route_set_add (vty, vty->index, "large-community", "none"); +} + +DEFUN (no_set_lcommunity, + no_set_lcommunity_cmd, + "no set large-community", + NO_STR + SET_STR + "BGP large community attribute\n" + "Large community\n") +{ + return bgp_route_set_delete (vty, vty->index, "large-community", NULL); +} + +ALIAS (no_set_lcommunity, + no_set_lcommunity_val_cmd, + "no set large-community .AA:BB:CC", + NO_STR + SET_STR + "BGP large community attribute\n" + "Large community in .AA:BB:CC format or additive\n") + +ALIAS (no_set_lcommunity, + no_set_lcommunity_none_cmd, + "no set large-community none", + NO_STR + SET_STR + "BGP community attribute\n" + "No community attribute\n") + +DEFUN (set_lcommunity_delete, + set_lcommunity_delete_cmd, + "set large-comm-list (<1-99>|<100-500>|WORD) delete", + SET_STR + "set BGP large community list (for deletion)\n" + "Large Community-list number (standard)\n" + "Large Communitly-list number (expanded)\n" + "Large Community-list name\n" + "Delete matching large communities\n") +{ + char *str; + + str = XCALLOC (MTYPE_TMP, strlen (argv[0]) + strlen (" delete") + 1); + strcpy (str, argv[0]); + strcpy (str + strlen (argv[0]), " delete"); + + bgp_route_set_add (vty, vty->index, "large-comm-list", str); + + XFREE (MTYPE_TMP, str); + return CMD_SUCCESS; +} + +DEFUN (no_set_lcommunity_delete, + no_set_lcommunity_delete_cmd, + "no set large-comm-list", + NO_STR + SET_STR + "set BGP large community list (for deletion)\n") +{ + return bgp_route_set_delete (vty, vty->index, "large-comm-list", NULL); +} + +ALIAS (no_set_lcommunity_delete, + no_set_lcommunity_delete_val_cmd, + "no set large-comm-list (<1-99>|<100-500>|WORD) delete", + NO_STR + SET_STR + "set BGP large community list (for deletion)\n" + "Large Community-list number (standard)\n" + "Large Communitly-list number (expanded)\n" + "Large Community-list name\n" + "Delete matching large communities\n") + DEFUN (set_ecommunity_rt, set_ecommunity_rt_cmd, "set extcommunity rt .ASN:nn_or_IP-address:nn", @@ -3493,7 +4099,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; @@ -3527,7 +4133,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; @@ -3565,8 +4171,38 @@ ALIAS (no_set_aggregator_as, "AS number\n" "IP address of aggregator\n") - -#ifdef HAVE_IPV6 +DEFUN (set_tag, + set_tag_cmd, + "set tag <1-4294967295>", + SET_STR + "Tag value for routing protocol\n" + "Tag value\n") +{ + return bgp_route_set_add (vty, vty->index, "tag", argv[0]); +} + +DEFUN (no_set_tag, + no_set_tag_cmd, + "no set tag", + NO_STR + SET_STR + "Tag value for routing protocol\n") +{ + if (argc == 0) + bgp_route_set_delete(vty, vty->index, "tag", NULL); + + return bgp_route_set_delete (vty, vty->index, "tag", argv[0]); +} + +ALIAS (no_set_tag, + no_set_tag_val_cmd, + "no set tag <1-4294967295>", + NO_STR + SET_STR + "Tag value for routing protocol\n" + "Tag value\n") + + DEFUN (match_ipv6_address, match_ipv6_address_cmd, "match ipv6 address WORD", @@ -3638,6 +4274,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", @@ -3711,7 +4370,6 @@ ALIAS (no_set_ipv6_nexthop_local, "IPv6 next-hop address\n" "IPv6 local address\n" "IPv6 address of next hop\n") -#endif /* HAVE_IPV6 */ DEFUN (set_vpnv4_nexthop, set_vpnv4_nexthop_cmd, @@ -3836,7 +4494,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) @@ -3847,6 +4505,7 @@ bgp_route_map_init (void) route_map_delete_hook (bgp_route_map_update); route_map_install_match (&route_match_peer_cmd); + route_map_install_match (&route_match_local_pref_cmd); route_map_install_match (&route_match_ip_address_cmd); route_map_install_match (&route_match_ip_next_hop_cmd); route_map_install_match (&route_match_ip_route_source_cmd); @@ -3855,10 +4514,13 @@ bgp_route_map_init (void) route_map_install_match (&route_match_ip_route_source_prefix_list_cmd); route_map_install_match (&route_match_aspath_cmd); route_map_install_match (&route_match_community_cmd); + route_map_install_match (&route_match_lcommunity_cmd); route_map_install_match (&route_match_ecommunity_cmd); + route_map_install_match (&route_match_local_pref_cmd); route_map_install_match (&route_match_metric_cmd); route_map_install_match (&route_match_origin_cmd); route_map_install_match (&route_match_probability_cmd); + route_map_install_match (&route_match_tag_cmd); route_map_install_set (&route_set_ip_nexthop_cmd); route_map_install_set (&route_set_local_pref_cmd); @@ -3871,10 +4533,13 @@ bgp_route_map_init (void) route_map_install_set (&route_set_aggregator_as_cmd); route_map_install_set (&route_set_community_cmd); route_map_install_set (&route_set_community_delete_cmd); + route_map_install_set (&route_set_lcommunity_cmd); + route_map_install_set (&route_set_lcommunity_delete_cmd); route_map_install_set (&route_set_vpnv4_nexthop_cmd); route_map_install_set (&route_set_originator_id_cmd); route_map_install_set (&route_set_ecommunity_rt_cmd); route_map_install_set (&route_set_ecommunity_soo_cmd); + route_map_install_set (&route_set_tag_cmd); install_element (RMAP_NODE, &match_peer_cmd); install_element (RMAP_NODE, &match_peer_local_cmd); @@ -3906,11 +4571,16 @@ bgp_route_map_init (void) install_element (RMAP_NODE, &match_metric_cmd); install_element (RMAP_NODE, &no_match_metric_cmd); install_element (RMAP_NODE, &no_match_metric_val_cmd); + install_element (RMAP_NODE, &match_local_pref_cmd); + install_element (RMAP_NODE, &no_match_local_pref_cmd); + install_element (RMAP_NODE, &no_match_local_pref_val_cmd); install_element (RMAP_NODE, &match_community_cmd); install_element (RMAP_NODE, &match_community_exact_cmd); install_element (RMAP_NODE, &no_match_community_cmd); install_element (RMAP_NODE, &no_match_community_val_cmd); install_element (RMAP_NODE, &no_match_community_exact_cmd); + install_element (RMAP_NODE, &match_lcommunity_cmd); + install_element (RMAP_NODE, &no_match_lcommunity_cmd); install_element (RMAP_NODE, &match_ecommunity_cmd); install_element (RMAP_NODE, &no_match_ecommunity_cmd); install_element (RMAP_NODE, &no_match_ecommunity_val_cmd); @@ -3920,6 +4590,9 @@ bgp_route_map_init (void) install_element (RMAP_NODE, &match_probability_cmd); install_element (RMAP_NODE, &no_match_probability_cmd); install_element (RMAP_NODE, &no_match_probability_val_cmd); + install_element (RMAP_NODE, &match_tag_cmd); + install_element (RMAP_NODE, &no_match_tag_cmd); + install_element (RMAP_NODE, &no_match_tag_val_cmd); install_element (RMAP_NODE, &set_ip_nexthop_cmd); install_element (RMAP_NODE, &set_ip_nexthop_peer_cmd); @@ -3933,9 +4606,11 @@ bgp_route_map_init (void) install_element (RMAP_NODE, &no_set_weight_val_cmd); install_element (RMAP_NODE, &set_metric_cmd); install_element (RMAP_NODE, &set_metric_addsub_cmd); + install_element (RMAP_NODE, &set_metric_rtt_cmd); 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); @@ -3957,6 +4632,14 @@ bgp_route_map_init (void) install_element (RMAP_NODE, &set_community_delete_cmd); install_element (RMAP_NODE, &no_set_community_delete_cmd); install_element (RMAP_NODE, &no_set_community_delete_val_cmd); + install_element (RMAP_NODE, &set_lcommunity_cmd); + install_element (RMAP_NODE, &set_lcommunity_none_cmd); + install_element (RMAP_NODE, &no_set_lcommunity_cmd); + install_element (RMAP_NODE, &no_set_lcommunity_val_cmd); + install_element (RMAP_NODE, &no_set_lcommunity_none_cmd); + install_element (RMAP_NODE, &set_lcommunity_delete_cmd); + install_element (RMAP_NODE, &no_set_lcommunity_delete_cmd); + install_element (RMAP_NODE, &no_set_lcommunity_delete_val_cmd); install_element (RMAP_NODE, &set_ecommunity_rt_cmd); install_element (RMAP_NODE, &no_set_ecommunity_rt_cmd); install_element (RMAP_NODE, &no_set_ecommunity_rt_val_cmd); @@ -3969,14 +4652,17 @@ bgp_route_map_init (void) install_element (RMAP_NODE, &set_originator_id_cmd); install_element (RMAP_NODE, &no_set_originator_id_cmd); install_element (RMAP_NODE, &no_set_originator_id_val_cmd); + install_element (RMAP_NODE, &set_tag_cmd); + install_element (RMAP_NODE, &no_set_tag_cmd); + install_element (RMAP_NODE, &no_set_tag_val_cmd); -#ifdef HAVE_IPV6 route_map_install_match (&route_match_ipv6_address_cmd); route_map_install_match (&route_match_ipv6_next_hop_cmd); 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); @@ -3989,7 +4675,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); -#endif /* HAVE_IPV6 */ + install_element (RMAP_NODE, &set_ipv6_nexthop_peer_cmd); + install_element (RMAP_NODE, &no_set_ipv6_nexthop_peer_cmd); /* AS-Pathlimit: functionality removed, commands kept for * compatibility. diff --git a/bgpd/bgp_snmp.c b/bgpd/bgp_snmp.c index 86cc08794..c4490bff0 100644 --- a/bgpd/bgp_snmp.c +++ b/bgpd/bgp_snmp.c @@ -21,14 +21,8 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include #ifdef HAVE_SNMP -#ifdef HAVE_NETSNMP #include #include -#else -#include -#include -#include -#endif #include "if.h" #include "log.h" @@ -36,6 +30,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "command.h" #include "thread.h" #include "smux.h" +#include "filter.h" #include "bgpd/bgpd.h" #include "bgpd/bgp_table.h" @@ -44,7 +39,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 @@ -80,9 +75,8 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #define BGPPEERKEEPALIVE 19 #define BGPPEERHOLDTIMECONFIGURED 20 #define BGPPEERKEEPALIVECONFIGURED 21 -#define BGPPEERMINASORIGINATIONINTERVAL 22 -#define BGPPEERMINROUTEADVERTISEMENTINTERVAL 23 -#define BGPPEERINUPDATEELAPSEDTIME 24 +#define BGPPEERMINROUTEADVERTISEMENTINTERVAL 22 +#define BGPPEERINUPDATEELAPSEDTIME 23 /* BGP MIB bgpIdentifier. */ #define BGPIDENTIFIER 0 @@ -118,15 +112,16 @@ 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 /* BGP-MIB instances. */ oid bgp_oid [] = { BGP4MIB }; +oid bgp_trap_oid [] = { BGP4MIB, 0 }; /* IP address 0.0.0.0. */ -static struct in_addr bgp_empty_addr = {0}; +static struct in_addr bgp_empty_addr = { .s_addr = 0 }; /* Hook functions. */ static u_char *bgpVersion (struct variable *, oid [], size_t *, int, @@ -194,8 +189,6 @@ struct variable bgp_variables[] = 3, {3, 1, 20}}, {BGPPEERKEEPALIVECONFIGURED, INTEGER, RWRITE, bgpPeerTable, 3, {3, 1, 21}}, - {BGPPEERMINASORIGINATIONINTERVAL, INTEGER, RWRITE, bgpPeerTable, - 3, {3, 1, 22}}, {BGPPEERMINROUTEADVERTISEMENTINTERVAL, INTEGER, RWRITE, bgpPeerTable, 3, {3, 1, 23}}, {BGPPEERINUPDATEELAPSEDTIME, GAUGE32, RONLY, bgpPeerTable, @@ -247,7 +240,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) @@ -342,38 +335,42 @@ bgp_peer_lookup_next (struct in_addr *src) return NULL; } +/* 1.3.6.1.2.1.15.3.1.x = 10 */ +#define PEERTAB_NAMELEN 10 + static struct peer * bgpPeerTable_lookup (struct variable *v, oid name[], size_t *length, struct in_addr *addr, int exact) { struct peer *peer = NULL; + size_t namelen = v ? v->namelen : PEERTAB_NAMELEN; int len; if (exact) { /* Check the length. */ - if (*length - v->namelen != sizeof (struct in_addr)) + if (*length - namelen != sizeof (struct in_addr)) return NULL; - oid2in_addr (name + v->namelen, IN_ADDR_SIZE, addr); + oid2in_addr (name + namelen, IN_ADDR_SIZE, addr); peer = peer_lookup_addr_ipv4 (addr); return peer; } else { - len = *length - v->namelen; + len = *length - namelen; if (len > 4) len = 4; - oid2in_addr (name + v->namelen, len, addr); + oid2in_addr (name + namelen, len, addr); peer = bgp_peer_lookup_next (addr); if (peer == NULL) return NULL; - oid_copy_addr (name + v->namelen, addr, sizeof (struct in_addr)); - *length = sizeof (struct in_addr) + v->namelen; + oid_copy_addr (name + namelen, addr, sizeof (struct in_addr)); + *length = sizeof (struct in_addr) + namelen; return peer; } @@ -384,14 +381,12 @@ bgpPeerTable_lookup (struct variable *v, oid name[], size_t *length, static int write_bgpPeerTable (int action, u_char *var_val, u_char var_val_type, size_t var_val_len, - u_char *statP, oid *name, size_t length, - struct variable *v) + u_char *statP, oid *name, size_t length) { struct in_addr addr; struct peer *peer; long intval; - size_t bigsize = SNMP_MAX_LEN; - + if (var_val_type != ASN_INTEGER) { return SNMP_ERR_WRONGTYPE; @@ -401,21 +396,21 @@ write_bgpPeerTable (int action, u_char *var_val, return SNMP_ERR_WRONGLENGTH; } - if (! asn_parse_int(var_val, &bigsize, &var_val_type, - &intval, sizeof(long))) - { - return SNMP_ERR_WRONGENCODING; - } + intval = *(long *)var_val; memset (&addr, 0, sizeof (struct in_addr)); - peer = bgpPeerTable_lookup (v, name, &length, &addr, 1); + peer = bgpPeerTable_lookup (NULL, name, &length, &addr, 1); if (! peer) return SNMP_ERR_NOSUCHNAME; - printf ("val: %ld\n", intval); + if (action != SNMP_MSG_INTERNAL_SET_COMMIT) + return SNMP_ERR_NOERROR; - switch (v->magic) + zlog_info ("%s: SNMP write .%ld = %ld", + peer->host, (long)name[PEERTAB_NAMELEN - 1], intval); + + switch (name[PEERTAB_NAMELEN - 1]) { case BGPPEERADMINSTATUS: #define BGP_PeerAdmin_stop 1 @@ -443,9 +438,6 @@ write_bgpPeerTable (int action, u_char *var_val, peer->keepalive = intval; peer->v_keepalive = intval; break; - case BGPPEERMINASORIGINATIONINTERVAL: - peer->v_asorig = intval; - break; case BGPPEERMINROUTEADVERTISEMENTINTERVAL: peer->v_routeadv = intval; break; @@ -460,7 +452,9 @@ bgpPeerTable (struct variable *v, oid name[], size_t *length, static struct in_addr addr; struct peer *peer; - *write_method = NULL; + if (smux_header_table(v, name, length, exact, var_len, write_method) + == MATCH_FAILED) + return NULL; memset (&addr, 0, sizeof (struct in_addr)); peer = bgpPeerTable_lookup (v, name, length, &addr, exact); @@ -572,10 +566,6 @@ bgpPeerTable (struct variable *v, oid name[], size_t *length, else return SNMP_INTEGER (peer->v_keepalive); break; - case BGPPEERMINASORIGINATIONINTERVAL: - *write_method = write_bgpPeerTable; - return SNMP_INTEGER (peer->v_asorig); - break; case BGPPEERMINROUTEADVERTISEMENTINTERVAL: *write_method = write_bgpPeerTable; return SNMP_INTEGER (peer->v_routeadv); @@ -770,6 +760,9 @@ bgp4PathAttrTable (struct variable *v, oid name[], size_t *length, if (! bgp) return NULL; + if (smux_header_table(v, name, length, exact, var_len, write_method) + == MATCH_FAILED) + return NULL; memset (&addr, 0, sizeof (struct prefix_ipv4)); binfo = bgp4PathAttrLookup (v, name, length, bgp, &addr, exact); @@ -835,12 +828,12 @@ bgp4PathAttrTable (struct variable *v, oid name[], size_t *length, } return NULL; } - + /* BGP Traps. */ struct trap_object bgpTrapList[] = { - {bgpPeerTable, 3, {3, 1, BGPPEERLASTERROR}}, - {bgpPeerTable, 3, {3, 1, BGPPEERSTATE}} + {3, {3, 1, BGPPEERLASTERROR}}, + {3, {3, 1, BGPPEERSTATE}} }; void @@ -856,10 +849,12 @@ bgpTrapEstablished (struct peer *peer) oid_copy_addr (index, &addr, IN_ADDR_SIZE); - smux_trap (bgp_oid, sizeof bgp_oid / sizeof (oid), + smux_trap (bgp_variables, sizeof bgp_variables / sizeof (struct variable), + bgp_trap_oid, sizeof bgp_trap_oid / sizeof (oid), + bgp_oid, sizeof bgp_oid / sizeof (oid), index, IN_ADDR_SIZE, bgpTrapList, sizeof bgpTrapList / sizeof (struct trap_object), - bm->start_time - bgp_clock (), BGPESTABLISHED); + BGPESTABLISHED); } void @@ -875,10 +870,12 @@ bgpTrapBackwardTransition (struct peer *peer) oid_copy_addr (index, &addr, IN_ADDR_SIZE); - smux_trap (bgp_oid, sizeof bgp_oid / sizeof (oid), + smux_trap (bgp_variables, sizeof bgp_variables / sizeof (struct variable), + bgp_trap_oid, sizeof bgp_trap_oid / sizeof (oid), + bgp_oid, sizeof bgp_oid / sizeof (oid), index, IN_ADDR_SIZE, bgpTrapList, sizeof bgpTrapList / sizeof (struct trap_object), - bm->start_time - bgp_clock (), BGPBACKWARDTRANSITION); + BGPBACKWARDTRANSITION); } void diff --git a/bgpd/bgp_table.c b/bgpd/bgp_table.c index a249c23d9..92bb95753 100644 --- a/bgpd/bgp_table.c +++ b/bgpd/bgp_table.c @@ -24,28 +24,11 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "memory.h" #include "sockunion.h" #include "vty.h" +#include "filter.h" #include "bgpd/bgpd.h" #include "bgpd/bgp_table.h" -static void bgp_node_delete (struct bgp_node *); -static void bgp_table_free (struct bgp_table *); - -struct bgp_table * -bgp_table_init (afi_t afi, safi_t safi) -{ - struct bgp_table *rt; - - rt = XCALLOC (MTYPE_BGP_TABLE, sizeof (struct bgp_table)); - - bgp_table_lock(rt); - rt->type = BGP_TABLE_MAIN; - rt->afi = afi; - rt->safi = safi; - - return rt; -} - void bgp_table_lock (struct bgp_table *rt) { @@ -58,97 +41,13 @@ bgp_table_unlock (struct bgp_table *rt) assert (rt->lock > 0); rt->lock--; - if (rt->lock == 0) - bgp_table_free (rt); -} - -void -bgp_table_finish (struct bgp_table **rt) -{ - if (*rt != NULL) + if (rt->lock != 0) { - bgp_table_unlock(*rt); - *rt = NULL; + return; } -} - -static struct bgp_node * -bgp_node_create (void) -{ - return XCALLOC (MTYPE_BGP_NODE, sizeof (struct bgp_node)); -} - -/* Allocate new route node with prefix set. */ -static struct bgp_node * -bgp_node_set (struct bgp_table *table, struct prefix *prefix) -{ - struct bgp_node *node; - - node = bgp_node_create (); - - prefix_copy (&node->p, prefix); - node->table = table; - - return node; -} - -/* Free route node. */ -static void -bgp_node_free (struct bgp_node *node) -{ - XFREE (MTYPE_BGP_NODE, node); -} - -/* Free route table. */ -static void -bgp_table_free (struct bgp_table *rt) -{ - struct bgp_node *tmp_node; - struct bgp_node *node; - - if (rt == NULL) - return; - - node = rt->top; - - /* Bulk deletion of nodes remaining in this table. This function is not - called until workers have completed their dependency on this table. - A final bgp_unlock_node() will not be called for these nodes. */ - while (node) - { - if (node->l_left) - { - node = node->l_left; - continue; - } - - if (node->l_right) - { - node = node->l_right; - continue; - } - tmp_node = node; - node = node->parent; - - tmp_node->table->count--; - tmp_node->lock = 0; /* to cause assert if unlocked after this */ - bgp_node_free (tmp_node); - - if (node != NULL) - { - if (node->l_left == tmp_node) - node->l_left = NULL; - else - node->l_right = NULL; - } - else - { - break; - } - } - - assert (rt->count == 0); + route_table_finish (rt->route_table); + rt->route_table = NULL; if (rt->owner) { @@ -157,350 +56,71 @@ bgp_table_free (struct bgp_table *rt) } XFREE (MTYPE_BGP_TABLE, rt); - return; -} - -/* Utility mask array. */ -static u_char maskbit[] = -{ - 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff -}; - -/* Common prefix route genaration. */ -static void -route_common (struct prefix *n, struct prefix *p, struct prefix *new) -{ - int i; - u_char diff; - u_char mask; - - u_char *np = (u_char *)&n->u.prefix; - u_char *pp = (u_char *)&p->u.prefix; - u_char *newp = (u_char *)&new->u.prefix; - - for (i = 0; i < p->prefixlen / 8; i++) - { - if (np[i] == pp[i]) - newp[i] = np[i]; - else - break; - } - - new->prefixlen = i * 8; - - if (new->prefixlen != p->prefixlen) - { - diff = np[i] ^ pp[i]; - mask = 0x80; - while (new->prefixlen < p->prefixlen && !(mask & diff)) - { - mask >>= 1; - new->prefixlen++; - } - newp[i] = np[i] & maskbit[new->prefixlen % 8]; - } -} - -static void -set_link (struct bgp_node *node, struct bgp_node *new) -{ - unsigned int bit = prefix_bit (&new->p.u.prefix, node->p.prefixlen); - - node->link[bit] = new; - new->parent = node; } -/* Lock node. */ -struct bgp_node * -bgp_lock_node (struct bgp_node *node) -{ - node->lock++; - return node; -} - -/* Unlock node. */ void -bgp_unlock_node (struct bgp_node *node) -{ - assert (node->lock > 0); - node->lock--; - - if (node->lock == 0) - bgp_node_delete (node); -} - -/* Find matched prefix. */ -struct bgp_node * -bgp_node_match (const struct bgp_table *table, struct prefix *p) -{ - struct bgp_node *node; - struct bgp_node *matched; - - matched = NULL; - node = table->top; - - /* Walk down tree. If there is matched route then store it to - matched. */ - while (node && node->p.prefixlen <= p->prefixlen && - prefix_match (&node->p, p)) - { - if (node->info) - matched = node; - node = node->link[prefix_bit(&p->u.prefix, node->p.prefixlen)]; - } - - /* If matched route found, return it. */ - if (matched) - return bgp_lock_node (matched); - - return NULL; -} - -struct bgp_node * -bgp_node_match_ipv4 (const struct bgp_table *table, struct in_addr *addr) -{ - struct prefix_ipv4 p; - - memset (&p, 0, sizeof (struct prefix_ipv4)); - p.family = AF_INET; - p.prefixlen = IPV4_MAX_PREFIXLEN; - p.prefix = *addr; - - return bgp_node_match (table, (struct prefix *) &p); -} - -#ifdef HAVE_IPV6 -struct bgp_node * -bgp_node_match_ipv6 (const struct bgp_table *table, struct in6_addr *addr) -{ - struct prefix_ipv6 p; - - memset (&p, 0, sizeof (struct prefix_ipv6)); - p.family = AF_INET6; - p.prefixlen = IPV6_MAX_PREFIXLEN; - p.prefix = *addr; - - return bgp_node_match (table, (struct prefix *) &p); -} -#endif /* HAVE_IPV6 */ - -/* Lookup same prefix node. Return NULL when we can't find route. */ -struct bgp_node * -bgp_node_lookup (const struct bgp_table *table, struct prefix *p) +bgp_table_finish (struct bgp_table **rt) { - struct bgp_node *node; - - node = table->top; - - while (node && node->p.prefixlen <= p->prefixlen && - prefix_match (&node->p, p)) + if (*rt != NULL) { - if (node->p.prefixlen == p->prefixlen && node->info) - return bgp_lock_node (node); - - node = node->link[prefix_bit(&p->u.prefix, node->p.prefixlen)]; + bgp_table_unlock(*rt); + *rt = NULL; } - - return NULL; } -/* Add node to routing table. */ -struct bgp_node * -bgp_node_get (struct bgp_table *const table, struct prefix *p) +/* + * bgp_node_create + */ +static struct route_node * +bgp_node_create (route_table_delegate_t *delegate, struct route_table *table) { - struct bgp_node *new; struct bgp_node *node; - struct bgp_node *match; - - match = NULL; - node = table->top; - while (node && node->p.prefixlen <= p->prefixlen && - prefix_match (&node->p, p)) - { - if (node->p.prefixlen == p->prefixlen) - { - bgp_lock_node (node); - return node; - } - match = node; - node = node->link[prefix_bit(&p->u.prefix, node->p.prefixlen)]; - } - - if (node == NULL) - { - new = bgp_node_set (table, p); - if (match) - set_link (match, new); - else - table->top = new; - } - else - { - new = bgp_node_create (); - route_common (&node->p, p, &new->p); - new->p.family = p->family; - new->table = table; - set_link (new, node); - - if (match) - set_link (match, new); - else - table->top = new; - - if (new->p.prefixlen != p->prefixlen) - { - match = new; - new = bgp_node_set (table, p); - set_link (match, new); - table->count++; - } - } - table->count++; - bgp_lock_node (new); - - return new; + node = XCALLOC (MTYPE_BGP_NODE, sizeof (struct bgp_node)); + return bgp_node_to_rnode (node); } -/* Delete node from the routing table. */ +/* + * bgp_node_destroy + */ static void -bgp_node_delete (struct bgp_node *node) +bgp_node_destroy (route_table_delegate_t *delegate, + struct route_table *table, struct route_node *node) { - struct bgp_node *child; - struct bgp_node *parent; - - assert (node->lock == 0); - assert (node->info == NULL); - - if (node->l_left && node->l_right) - return; - - if (node->l_left) - child = node->l_left; - else - child = node->l_right; - - parent = node->parent; - - if (child) - child->parent = parent; - - if (parent) - { - if (parent->l_left == node) - parent->l_left = child; - else - parent->l_right = child; - } - else - node->table->top = child; - - node->table->count--; - - bgp_node_free (node); - - /* If parent node is stub then delete it also. */ - if (parent && parent->lock == 0) - bgp_node_delete (parent); + struct bgp_node *bgp_node; + bgp_node = bgp_node_from_rnode (node); + XFREE (MTYPE_BGP_NODE, bgp_node); } -/* Get fist node and lock it. This function is useful when one want - to lookup all the node exist in the routing table. */ -struct bgp_node * -bgp_table_top (const struct bgp_table *const table) -{ - /* If there is no node in the routing table return NULL. */ - if (table->top == NULL) - return NULL; - - /* Lock the top node and return it. */ - bgp_lock_node (table->top); - return table->top; -} +/* + * Function vector to customize the behavior of the route table + * library for BGP route tables. + */ +route_table_delegate_t bgp_table_delegate = { + .create_node = bgp_node_create, + .destroy_node = bgp_node_destroy +}; -/* Unlock current node and lock next node then return it. */ -struct bgp_node * -bgp_route_next (struct bgp_node *node) +/* + * bgp_table_init + */ +struct bgp_table * +bgp_table_init (afi_t afi, safi_t safi) { - struct bgp_node *next; - struct bgp_node *start; - - /* Node may be deleted from bgp_unlock_node so we have to preserve - next node's pointer. */ - - if (node->l_left) - { - next = node->l_left; - bgp_lock_node (next); - bgp_unlock_node (node); - return next; - } - if (node->l_right) - { - next = node->l_right; - bgp_lock_node (next); - bgp_unlock_node (node); - return next; - } + struct bgp_table *rt; - start = node; - while (node->parent) - { - if (node->parent->l_left == node && node->parent->l_right) - { - next = node->parent->l_right; - bgp_lock_node (next); - bgp_unlock_node (start); - return next; - } - node = node->parent; - } - bgp_unlock_node (start); - return NULL; -} + rt = XCALLOC (MTYPE_BGP_TABLE, sizeof (struct bgp_table)); -/* Unlock current node and lock next node until limit. */ -struct bgp_node * -bgp_route_next_until (struct bgp_node *node, struct bgp_node *limit) -{ - struct bgp_node *next; - struct bgp_node *start; + rt->route_table = route_table_init_with_delegate (&bgp_table_delegate); - /* Node may be deleted from bgp_unlock_node so we have to preserve - next node's pointer. */ + /* + * Set up back pointer to bgp_table. + */ + rt->route_table->info = rt; - if (node->l_left) - { - next = node->l_left; - bgp_lock_node (next); - bgp_unlock_node (node); - return next; - } - if (node->l_right) - { - next = node->l_right; - bgp_lock_node (next); - bgp_unlock_node (node); - return next; - } - - start = node; - while (node->parent && node != limit) - { - if (node->parent->l_left == node && node->parent->l_right) - { - next = node->parent->l_right; - bgp_lock_node (next); - bgp_unlock_node (start); - return next; - } - node = node->parent; - } - bgp_unlock_node (start); - return NULL; -} + bgp_table_lock (rt); + rt->type = BGP_TABLE_MAIN; + rt->afi = afi; + rt->safi = safi; -unsigned long -bgp_table_count (const struct bgp_table *table) -{ - return table->count; + return rt; } diff --git a/bgpd/bgp_table.h b/bgpd/bgp_table.h index 53df0bc6c..8e963aeee 100644 --- a/bgpd/bgp_table.h +++ b/bgpd/bgp_table.h @@ -21,6 +21,8 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #ifndef _QUAGGA_BGP_TABLE_H #define _QUAGGA_BGP_TABLE_H +#include "table.h" + typedef enum { BGP_TABLE_MAIN, @@ -40,22 +42,20 @@ struct bgp_table /* The owner of this 'bgp_table' structure. */ struct peer *owner; - struct bgp_node *top; - - unsigned long count; + struct route_table *route_table; }; struct bgp_node { - struct prefix p; - - struct bgp_table *table; - struct bgp_node *parent; - struct bgp_node *link[2]; -#define l_left link[0] -#define l_right link[1] - - void *info; + /* + * CAUTION + * + * These fields must be the very first fields in this structure. + * + * @see bgp_node_to_rnode + * @see bgp_node_from_rnode + */ + ROUTE_NODE_FIELDS struct bgp_adj_out *adj_out; @@ -63,29 +63,252 @@ struct bgp_node struct bgp_node *prn; - int lock; - u_char flags; #define BGP_NODE_PROCESS_SCHEDULED (1 << 0) +#define BGP_NODE_USER_CLEAR (1 << 1) }; +/* + * bgp_table_iter_t + * + * Structure that holds state for iterating over a bgp table. + */ +typedef struct bgp_table_iter_t_ +{ + struct bgp_table *table; + route_table_iter_t rt_iter; +} bgp_table_iter_t; + extern struct bgp_table *bgp_table_init (afi_t, safi_t); extern void bgp_table_lock (struct bgp_table *); extern void bgp_table_unlock (struct bgp_table *); extern void bgp_table_finish (struct bgp_table **); -extern void bgp_unlock_node (struct bgp_node *node); -extern struct bgp_node *bgp_table_top (const struct bgp_table *const); -extern struct bgp_node *bgp_route_next (struct bgp_node *); -extern struct bgp_node *bgp_route_next_until (struct bgp_node *, struct bgp_node *); -extern struct bgp_node *bgp_node_get (struct bgp_table *const, struct prefix *); -extern struct bgp_node *bgp_node_lookup (const struct bgp_table *const, struct prefix *); -extern struct bgp_node *bgp_lock_node (struct bgp_node *node); -extern struct bgp_node *bgp_node_match (const struct bgp_table *, struct prefix *); -extern struct bgp_node *bgp_node_match_ipv4 (const struct bgp_table *, - struct in_addr *); -#ifdef HAVE_IPV6 -extern struct bgp_node *bgp_node_match_ipv6 (const struct bgp_table *, - struct in6_addr *); -#endif /* HAVE_IPV6 */ -extern unsigned long bgp_table_count (const struct bgp_table *const); + + +/* + * bgp_node_from_rnode + * + * Returns the bgp_node structure corresponding to a route_node. + */ +static inline struct bgp_node * +bgp_node_from_rnode (struct route_node *rnode) +{ + return (struct bgp_node *) rnode; +} + +/* + * bgp_node_to_rnode + * + * Returns the route_node structure corresponding to a bgp_node. + */ +static inline struct route_node * +bgp_node_to_rnode (struct bgp_node *node) +{ + return (struct route_node *) node; +} + +/* + * bgp_node_table + * + * Returns the bgp_table that the given node is in. + */ +static inline struct bgp_table * +bgp_node_table (struct bgp_node *node) +{ + return bgp_node_to_rnode (node)->table->info; +} + +/* + * bgp_node_parent_nolock + * + * Gets the parent node of the given node without locking it. + */ +static inline struct bgp_node * +bgp_node_parent_nolock (struct bgp_node *node) +{ + return bgp_node_from_rnode (node->parent); +} + +/* + * bgp_unlock_node + */ +static inline void +bgp_unlock_node (struct bgp_node *node) +{ + route_unlock_node (bgp_node_to_rnode (node)); +} + +/* + * bgp_table_top_nolock + * + * Gets the top node in the table without locking it. + * + * @see bgp_table_top + */ +static inline struct bgp_node * +bgp_table_top_nolock (const struct bgp_table *const table) +{ + return bgp_node_from_rnode (table->route_table->top); +} + +/* + * bgp_table_top + */ +static inline struct bgp_node * +bgp_table_top (const struct bgp_table *const table) +{ + return bgp_node_from_rnode (route_top (table->route_table)); +} + +/* + * bgp_route_next + */ +static inline struct bgp_node * +bgp_route_next (struct bgp_node *node) +{ + return bgp_node_from_rnode (route_next (bgp_node_to_rnode (node))); +} + +/* + * bgp_route_next_until + */ +static inline struct bgp_node * +bgp_route_next_until (struct bgp_node *node, struct bgp_node *limit) +{ + struct route_node *rnode; + + rnode = route_next_until (bgp_node_to_rnode (node), + bgp_node_to_rnode (limit)); + return bgp_node_from_rnode (rnode); +} + +/* + * bgp_node_get + */ +static inline struct bgp_node * +bgp_node_get (struct bgp_table *const table, struct prefix *p) +{ + return bgp_node_from_rnode (route_node_get (table->route_table, p)); +} + +/* + * bgp_node_lookup + */ +static inline struct bgp_node * +bgp_node_lookup (const struct bgp_table *const table, struct prefix *p) +{ + return bgp_node_from_rnode (route_node_lookup (table->route_table, p)); +} + +/* + * bgp_lock_node + */ +static inline struct bgp_node * +bgp_lock_node (struct bgp_node *node) +{ + return bgp_node_from_rnode (route_lock_node (bgp_node_to_rnode (node))); +} + +/* + * bgp_node_match + */ +static inline struct bgp_node * +bgp_node_match (const struct bgp_table *table, struct prefix *p) +{ + return bgp_node_from_rnode (route_node_match (table->route_table, p)); +} + +/* + * bgp_node_match_ipv4 + */ +static inline struct bgp_node * +bgp_node_match_ipv4 (const struct bgp_table *table, struct in_addr *addr) +{ + return bgp_node_from_rnode (route_node_match_ipv4 (table->route_table, + addr)); +} + +/* + * bgp_node_match_ipv6 + */ +static inline struct bgp_node * +bgp_node_match_ipv6 (const struct bgp_table *table, struct in6_addr *addr) +{ + return bgp_node_from_rnode (route_node_match_ipv6 (table->route_table, + addr)); +} + +static inline unsigned long +bgp_table_count (const struct bgp_table *const table) +{ + return route_table_count (table->route_table); +} + +/* + * bgp_table_get_next + */ +static inline struct bgp_node * +bgp_table_get_next (const struct bgp_table *table, struct prefix *p) +{ + return bgp_node_from_rnode (route_table_get_next (table->route_table, p)); +} + +/* + * bgp_table_iter_init + */ +static inline void +bgp_table_iter_init (bgp_table_iter_t * iter, struct bgp_table *table) +{ + bgp_table_lock (table); + iter->table = table; + route_table_iter_init (&iter->rt_iter, table->route_table); +} + +/* + * bgp_table_iter_next + */ +static inline struct bgp_node * +bgp_table_iter_next (bgp_table_iter_t * iter) +{ + return bgp_node_from_rnode (route_table_iter_next (&iter->rt_iter)); +} + +/* + * bgp_table_iter_cleanup + */ +static inline void +bgp_table_iter_cleanup (bgp_table_iter_t * iter) +{ + route_table_iter_cleanup (&iter->rt_iter); + bgp_table_unlock (iter->table); + iter->table = NULL; +} + +/* + * bgp_table_iter_pause + */ +static inline void +bgp_table_iter_pause (bgp_table_iter_t * iter) +{ + route_table_iter_pause (&iter->rt_iter); +} + +/* + * bgp_table_iter_is_done + */ +static inline int +bgp_table_iter_is_done (bgp_table_iter_t * iter) +{ + return route_table_iter_is_done (&iter->rt_iter); +} + +/* + * bgp_table_iter_started + */ +static inline int +bgp_table_iter_started (bgp_table_iter_t * iter) +{ + return route_table_iter_started (&iter->rt_iter); +} + #endif /* _QUAGGA_BGP_TABLE_H */ diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index f65bb1571..0040d62b6 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -30,6 +30,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "log.h" #include "memory.h" #include "hash.h" +#include "filter.h" #include "bgpd/bgpd.h" #include "bgpd/bgp_advertise.h" @@ -37,6 +38,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "bgpd/bgp_aspath.h" #include "bgpd/bgp_community.h" #include "bgpd/bgp_ecommunity.h" +#include "bgpd/bgp_lcommunity.h" #include "bgpd/bgp_damp.h" #include "bgpd/bgp_debug.h" #include "bgpd/bgp_fsm.h" @@ -48,16 +50,26 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "bgpd/bgp_zebra.h" #include "bgpd/bgp_table.h" #include "bgpd/bgp_vty.h" - -extern struct in_addr router_id_zebra; +#include "bgpd/bgp_mpath.h" /* Utility function to get address family from current node. */ afi_t bgp_node_afi (struct vty *vty) { - if (vty->node == BGP_IPV6_NODE || vty->node == BGP_IPV6M_NODE) - return AFI_IP6; - return AFI_IP; + afi_t afi; + switch (vty->node) + { + case BGP_IPV6_NODE: + case BGP_IPV6M_NODE: + case BGP_VPNV6_NODE: + case BGP_ENCAPV6_NODE: + afi = AFI_IP6; + break; + default: + afi = AFI_IP; + break; + } + return afi; } /* Utility function to get subsequent address family from current @@ -65,11 +77,62 @@ bgp_node_afi (struct vty *vty) safi_t bgp_node_safi (struct vty *vty) { - if (vty->node == BGP_VPNV4_NODE) - return SAFI_MPLS_VPN; - if (vty->node == BGP_IPV4M_NODE || vty->node == BGP_IPV6M_NODE) - return SAFI_MULTICAST; - return SAFI_UNICAST; + safi_t safi; + switch (vty->node) + { + case BGP_ENCAP_NODE: + case BGP_ENCAPV6_NODE: + safi = SAFI_ENCAP; + break; + case BGP_VPNV4_NODE: + case BGP_VPNV6_NODE: + safi = SAFI_MPLS_VPN; + break; + case BGP_IPV4M_NODE: + case BGP_IPV6M_NODE: + safi = SAFI_MULTICAST; + break; + default: + safi = SAFI_UNICAST; + break; + } + return safi; +} + +int +bgp_parse_afi(const char *str, afi_t *afi) +{ + if (!strcmp(str, "ipv4")) { + *afi = AFI_IP; + return 0; + } + if (!strcmp(str, "ipv6")) { + *afi = AFI_IP6; + return 0; + } + return -1; +} + +int +bgp_parse_safi(const char *str, safi_t *safi) +{ + if (!strcmp(str, "encap")) { + *safi = SAFI_ENCAP; + return 0; + } + if (!strcmp(str, "multicast")) { + *safi = SAFI_MULTICAST; + return 0; + } + if (!strcmp(str, "unicast")) { + *safi = SAFI_UNICAST; + return 0; + } + if (!strcmp(str, "vpn")) { + *safi = SAFI_MPLS_VPN; + return 0; + } + return -1; } static int @@ -79,10 +142,8 @@ peer_address_self_check (union sockunion *su) if (su->sa.sa_family == AF_INET) ifp = if_lookup_by_ipv4_exact (&su->sin.sin_addr); -#ifdef HAVE_IPV6 else if (su->sa.sa_family == AF_INET6) ifp = if_lookup_by_ipv6_exact (&su->sin6.sin6_addr); -#endif /* HAVE IPV6 */ if (ifp) return 1; @@ -313,7 +374,7 @@ DEFUN_DEPRECATED (neighbor_version, { return CMD_SUCCESS; } - + /* "router bgp" commands. */ DEFUN (router_bgp, router_bgp_cmd, @@ -363,7 +424,7 @@ ALIAS (router_bgp, AS_STR "BGP view\n" "view name\n") - + /* "no router bgp" commands. */ DEFUN (no_router_bgp, no_router_bgp_cmd, @@ -404,7 +465,7 @@ ALIAS (no_router_bgp, AS_STR "BGP view\n" "view name\n") - + /* BGP router-id. */ DEFUN (bgp_router_id, @@ -427,8 +488,7 @@ DEFUN (bgp_router_id, return CMD_WARNING; } - bgp->router_id_static = id; - bgp_router_id_set (bgp, &id); + bgp_router_id_static_set (bgp, id); return CMD_SUCCESS; } @@ -462,8 +522,8 @@ DEFUN (no_bgp_router_id, } } - bgp->router_id_static.s_addr = 0; - bgp_router_id_set (bgp, &router_id_zebra); + id.s_addr = 0; + bgp_router_id_static_set (bgp, id); return CMD_SUCCESS; } @@ -475,7 +535,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, @@ -545,7 +605,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, @@ -575,7 +635,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; @@ -595,7 +655,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, @@ -649,7 +709,150 @@ DEFUN (no_bgp_confederation_peers, } return CMD_SUCCESS; } - + +/* Maximum-paths configuration */ +DEFUN (bgp_maxpaths, + bgp_maxpaths_cmd, + "maximum-paths " CMD_RANGE_STR(1, MULTIPATH_NUM), + "Forward packets over multiple paths\n" + "Number of paths\n") +{ + struct bgp *bgp; + u_int16_t maxpaths; + int ret; + + bgp = vty->index; + + VTY_GET_INTEGER_RANGE ("maximum-paths", maxpaths, argv[0], 1, 255); + + ret = bgp_maximum_paths_set (bgp, bgp_node_afi (vty), bgp_node_safi(vty), + BGP_PEER_EBGP, maxpaths); + if (ret < 0) + { + vty_out (vty, + "%% Failed to set maximum-paths %u for afi %u, safi %u%s", + maxpaths, bgp_node_afi (vty), bgp_node_safi(vty), VTY_NEWLINE); + return CMD_WARNING; + } + + return CMD_SUCCESS; +} + +DEFUN (bgp_maxpaths_ibgp, + bgp_maxpaths_ibgp_cmd, + "maximum-paths ibgp " CMD_RANGE_STR(1, MULTIPATH_NUM), + "Forward packets over multiple paths\n" + "iBGP-multipath\n" + "Number of paths\n") +{ + struct bgp *bgp; + u_int16_t maxpaths; + int ret; + + bgp = vty->index; + + VTY_GET_INTEGER_RANGE ("maximum-paths", maxpaths, argv[0], 1, 255); + + ret = bgp_maximum_paths_set (bgp, bgp_node_afi (vty), bgp_node_safi(vty), + BGP_PEER_IBGP, maxpaths); + if (ret < 0) + { + vty_out (vty, + "%% Failed to set maximum-paths ibgp %u for afi %u, safi %u%s", + maxpaths, bgp_node_afi (vty), bgp_node_safi(vty), VTY_NEWLINE); + return CMD_WARNING; + } + + return CMD_SUCCESS; +} + +DEFUN (no_bgp_maxpaths, + no_bgp_maxpaths_cmd, + "no maximum-paths", + NO_STR + "Forward packets over multiple paths\n" + "Number of paths\n") +{ + struct bgp *bgp; + int ret; + + bgp = vty->index; + + ret = bgp_maximum_paths_unset (bgp, bgp_node_afi (vty), bgp_node_safi(vty), + BGP_PEER_EBGP); + if (ret < 0) + { + vty_out (vty, + "%% Failed to unset maximum-paths for afi %u, safi %u%s", + bgp_node_afi (vty), bgp_node_safi(vty), VTY_NEWLINE); + return CMD_WARNING; + } + + return CMD_SUCCESS; +} + +ALIAS (no_bgp_maxpaths, + no_bgp_maxpaths_arg_cmd, + "no maximum-paths " CMD_RANGE_STR(1, MULTIPATH_NUM), + NO_STR + "Forward packets over multiple paths\n" + "Number of paths\n") + +DEFUN (no_bgp_maxpaths_ibgp, + no_bgp_maxpaths_ibgp_cmd, + "no maximum-paths ibgp", + NO_STR + "Forward packets over multiple paths\n" + "iBGP-multipath\n" + "Number of paths\n") +{ + struct bgp *bgp; + int ret; + + bgp = vty->index; + + ret = bgp_maximum_paths_unset (bgp, bgp_node_afi (vty), bgp_node_safi(vty), + BGP_PEER_IBGP); + if (ret < 0) + { + vty_out (vty, + "%% Failed to unset maximum-paths ibgp for afi %u, safi %u%s", + bgp_node_afi (vty), bgp_node_safi(vty), VTY_NEWLINE); + return CMD_WARNING; + } + + return CMD_SUCCESS; +} + +ALIAS (no_bgp_maxpaths_ibgp, + no_bgp_maxpaths_ibgp_arg_cmd, + "no maximum-paths ibgp " CMD_RANGE_STR(1, MULTIPATH_NUM), + NO_STR + "Forward packets over multiple paths\n" + "iBGP-multipath\n" + "Number of paths\n") + +int +bgp_config_write_maxpaths (struct vty *vty, struct bgp *bgp, afi_t afi, + safi_t safi, int *write) +{ + if (bgp->maxpaths[afi][safi].maxpaths_ebgp != BGP_DEFAULT_MAXPATHS) + { + bgp_config_write_family_header (vty, afi, safi, write); + vty_out (vty, " maximum-paths %d%s", + bgp->maxpaths[afi][safi].maxpaths_ebgp, VTY_NEWLINE); + } + + if (bgp->maxpaths[afi][safi].maxpaths_ibgp != BGP_DEFAULT_MAXPATHS) + { + bgp_config_write_family_header (vty, afi, safi, write); + vty_out (vty, " maximum-paths ibgp %d%s", + bgp->maxpaths[afi][safi].maxpaths_ibgp, VTY_NEWLINE); + } + + return 0; +} + /* BGP timers. */ DEFUN (bgp_timers, @@ -705,7 +908,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", @@ -762,7 +965,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, @@ -839,6 +1042,26 @@ DEFUN (bgp_graceful_restart_stalepath_time, return CMD_SUCCESS; } +DEFUN (bgp_graceful_restart_restart_time, + bgp_graceful_restart_restart_time_cmd, + "bgp graceful-restart restart-time <1-3600>", + "BGP specific commands\n" + "Graceful restart capability parameters\n" + "Set the time to wait to delete stale routes before a BGP open message is received\n" + "Delay value (seconds)\n") +{ + struct bgp *bgp; + u_int32_t restart; + + bgp = vty->index; + if (! bgp) + return CMD_WARNING; + + VTY_GET_INTEGER_RANGE ("restart-time", restart, argv[0], 1, 3600); + bgp->restart_time = restart; + return CMD_SUCCESS; +} + DEFUN (no_bgp_graceful_restart_stalepath_time, no_bgp_graceful_restart_stalepath_time_cmd, "no bgp graceful-restart stalepath-time", @@ -857,6 +1080,24 @@ DEFUN (no_bgp_graceful_restart_stalepath_time, return CMD_SUCCESS; } +DEFUN (no_bgp_graceful_restart_restart_time, + no_bgp_graceful_restart_restart_time_cmd, + "no bgp graceful-restart restart-time", + NO_STR + "BGP specific commands\n" + "Graceful restart capability parameters\n" + "Set the time to wait to delete stale routes before a BGP open message is received\n") +{ + struct bgp *bgp; + + bgp = vty->index; + if (! bgp) + return CMD_WARNING; + + bgp->restart_time = BGP_DEFAULT_RESTART_TIME; + return CMD_SUCCESS; +} + ALIAS (no_bgp_graceful_restart_stalepath_time, no_bgp_graceful_restart_stalepath_time_val_cmd, "no bgp graceful-restart stalepath-time <1-3600>", @@ -866,6 +1107,15 @@ ALIAS (no_bgp_graceful_restart_stalepath_time, "Set the max time to hold onto restarting peer's stale paths\n" "Delay value (seconds)\n") +ALIAS (no_bgp_graceful_restart_restart_time, + no_bgp_graceful_restart_restart_time_val_cmd, + "no bgp graceful-restart restart-time <1-3600>", + NO_STR + "BGP specific commands\n" + "Graceful restart capability parameters\n" + "Set the time to wait to delete stale routes before a BGP open message is received\n" + "Delay value (seconds)\n") + /* "bgp fast-external-failover" configuration. */ DEFUN (bgp_fast_external_failover, bgp_fast_external_failover_cmd, @@ -893,7 +1143,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, @@ -921,7 +1171,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, @@ -951,7 +1201,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, @@ -983,7 +1233,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, @@ -1015,7 +1265,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, @@ -1043,7 +1325,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, @@ -1141,7 +1423,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, @@ -1171,7 +1453,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, @@ -1201,7 +1483,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>", @@ -1245,7 +1527,81 @@ ALIAS (no_bgp_default_local_preference, "Configure BGP defaults\n" "local preference (higher=more preferred)\n" "Configure default local preference value\n") - + +static void +peer_announce_routes_if_rmap_out (struct bgp *bgp) +{ + struct peer *peer; + struct listnode *node, *nnode; + struct bgp_filter *filter; + afi_t afi; + safi_t safi; + + /* Reannounce all routes to appropriate neighbors */ + for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer)) + { + for (afi = AFI_IP; afi < AFI_MAX; afi++) + for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) + { + if (CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_REFLECTOR_CLIENT)) + { + /* check if there's an out route-map on this client */ + filter = &peer->filter[afi][safi]; + if (ROUTE_MAP_OUT_NAME(filter)) + { + if (BGP_DEBUG(update, UPDATE_OUT)) + zlog_debug("%s: Announcing routes again for peer %s" + "(afi=%d, safi=%d", __func__, peer->host, afi, + safi); + + bgp_announce_route_all(peer); + } + } + } + } +} + +DEFUN (bgp_rr_allow_outbound_policy, + bgp_rr_allow_outbound_policy_cmd, + "bgp route-reflector allow-outbound-policy", + "BGP specific commands\n" + "Allow modifications made by out route-map\n" + "on ibgp neighbors\n") +{ + struct bgp *bgp; + + bgp = vty->index; + + if (!bgp_flag_check(bgp, BGP_FLAG_RR_ALLOW_OUTBOUND_POLICY)) + { + bgp_flag_set(bgp, BGP_FLAG_RR_ALLOW_OUTBOUND_POLICY); + peer_announce_routes_if_rmap_out(bgp); + } + + return CMD_SUCCESS; +} + +DEFUN (no_bgp_rr_allow_outbound_policy, + no_bgp_rr_allow_outbound_policy_cmd, + "no bgp route-reflector allow-outbound-policy", + NO_STR + "BGP specific commands\n" + "Allow modifications made by out route-map\n" + "on ibgp neighbors\n") +{ + struct bgp *bgp; + + bgp = vty->index; + + if (bgp_flag_check(bgp, BGP_FLAG_RR_ALLOW_OUTBOUND_POLICY)) + { + bgp_flag_unset(bgp, BGP_FLAG_RR_ALLOW_OUTBOUND_POLICY); + peer_announce_routes_if_rmap_out(bgp); + } + + return CMD_SUCCESS; +} + static int peer_remote_as_vty (struct vty *vty, const char *peer_str, const char *as_str, afi_t afi, safi_t safi) @@ -1305,7 +1661,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", @@ -1410,7 +1766,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, @@ -1426,7 +1782,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); } @@ -1446,10 +1802,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", @@ -1487,7 +1865,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", @@ -1525,7 +1914,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", @@ -1564,7 +1953,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", @@ -1646,7 +2035,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) @@ -1699,7 +2088,7 @@ DEFUN (no_neighbor_passive, { return peer_flag_unset_vty (vty, argv[0], PEER_FLAG_PASSIVE); } - + /* neighbor shutdown. */ DEFUN (neighbor_shutdown, neighbor_shutdown_cmd, @@ -1721,7 +2110,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, @@ -1745,7 +2134,7 @@ DEFUN_DEPRECATED (no_neighbor_capability_route_refresh, { return CMD_SUCCESS; } - + /* neighbor capability dynamic. */ DEFUN (neighbor_capability_dynamic, neighbor_capability_dynamic_cmd, @@ -1769,7 +2158,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, @@ -1791,7 +2180,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) @@ -1824,7 +2213,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, @@ -1880,31 +2269,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, @@ -1930,7 +2335,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, @@ -1956,17 +2361,19 @@ 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, - NEIGHBOR_CMD2 "send-community (both|extended|standard)", + NEIGHBOR_CMD2 "send-community (both|all|extended|standard|large)", NEIGHBOR_STR NEIGHBOR_ADDR_STR2 "Send Community attribute to this neighbor\n" - "Send Standard and Extended Community attributes\n" + "Send Standard, Large and Extended Community attributes\n" + "Send Standard, Large and Extended Community attributes\n" "Send Extended Community attributes\n" - "Send Standard Community attributes\n") + "Send Standard Community attributes\n" + "Send Large Community attributes\n") { if (strncmp (argv[1], "s", 1) == 0) return peer_af_flag_set_vty (vty, argv[0], bgp_node_afi (vty), @@ -1976,23 +2383,30 @@ DEFUN (neighbor_send_community_type, return peer_af_flag_set_vty (vty, argv[0], bgp_node_afi (vty), bgp_node_safi (vty), PEER_FLAG_SEND_EXT_COMMUNITY); + if (strncmp (argv[1], "l", 1) == 0) + return peer_af_flag_set_vty (vty, argv[0], bgp_node_afi (vty), + bgp_node_safi (vty), + PEER_FLAG_SEND_LARGE_COMMUNITY); return peer_af_flag_set_vty (vty, argv[0], bgp_node_afi (vty), bgp_node_safi (vty), (PEER_FLAG_SEND_COMMUNITY| - PEER_FLAG_SEND_EXT_COMMUNITY)); + PEER_FLAG_SEND_EXT_COMMUNITY| + PEER_FLAG_SEND_LARGE_COMMUNITY)); } DEFUN (no_neighbor_send_community_type, no_neighbor_send_community_type_cmd, - NO_NEIGHBOR_CMD2 "send-community (both|extended|standard)", + NO_NEIGHBOR_CMD2 "send-community (both|all|extended|standard|large)", NO_STR NEIGHBOR_STR NEIGHBOR_ADDR_STR2 "Send Community attribute to this neighbor\n" - "Send Standard and Extended Community attributes\n" + "Send Standard, Large and Extended Community attributes\n" + "Send Standard, Large and Extended Community attributes\n" "Send Extended Community attributes\n" - "Send Standard Community attributes\n") + "Send Standard Community attributes\n" + "Send Large Community attributes\n") { if (strncmp (argv[1], "s", 1) == 0) return peer_af_flag_unset_vty (vty, argv[0], bgp_node_afi (vty), @@ -2002,13 +2416,18 @@ DEFUN (no_neighbor_send_community_type, return peer_af_flag_unset_vty (vty, argv[0], bgp_node_afi (vty), bgp_node_safi (vty), PEER_FLAG_SEND_EXT_COMMUNITY); + if (strncmp (argv[1], "l", 1) == 0) + return peer_af_flag_unset_vty (vty, argv[0], bgp_node_afi (vty), + bgp_node_safi (vty), + PEER_FLAG_SEND_LARGE_COMMUNITY); return peer_af_flag_unset_vty (vty, argv[0], bgp_node_afi (vty), bgp_node_safi (vty), (PEER_FLAG_SEND_COMMUNITY | - PEER_FLAG_SEND_EXT_COMMUNITY)); + PEER_FLAG_SEND_EXT_COMMUNITY| + PEER_FLAG_SEND_LARGE_COMMUNITY)); } - + /* neighbor soft-reconfig. */ DEFUN (neighbor_soft_reconfiguration, neighbor_soft_reconfiguration_cmd, @@ -2036,7 +2455,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", @@ -2068,7 +2487,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) @@ -2216,7 +2635,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, @@ -2240,7 +2659,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", @@ -2253,7 +2672,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", @@ -2267,7 +2686,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", @@ -2624,7 +3043,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, @@ -2654,7 +3073,7 @@ peer_ebgp_multihop_unset_vty (struct vty *vty, const char *ip_str) if (! peer) return CMD_WARNING; - return bgp_vty_return (vty, peer_ebgp_multihop_unset (peer)); + return bgp_vty_return (vty, peer_ebgp_multihop_set (peer, 0)); } /* neighbor ebgp-multihop. */ @@ -2698,7 +3117,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, @@ -2737,7 +3156,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", @@ -2792,14 +3211,13 @@ 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, const char *source_str) { struct peer *peer; - union sockunion *su; peer = peer_and_group_lookup_vty (vty, peer_str); if (! peer) @@ -2807,12 +3225,11 @@ peer_update_source_vty (struct vty *vty, const char *peer_str, if (source_str) { - su = sockunion_str2su (source_str); - if (su) - { - peer_update_source_addr_set (peer, su); - sockunion_free (su); - } + union sockunion su; + int ret = str2sockunion (source_str, &su); + + if (ret == 0) + peer_update_source_addr_set (peer, &su); else peer_update_source_if_set (peer, source_str); } @@ -2849,7 +3266,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, @@ -2916,7 +3333,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, @@ -2976,13 +3393,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; @@ -2992,9 +3408,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 @@ -3006,9 +3420,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, @@ -3041,7 +3453,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, @@ -3063,7 +3475,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", @@ -3084,7 +3496,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) @@ -3105,7 +3517,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) { @@ -3143,46 +3555,40 @@ 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; - peer = peer_lookup_vty (vty, ip_str); + peer = peer_and_group_lookup_vty (vty, ip_str); if (! peer) return CMD_WARNING; 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, neighbor_timers_connect_cmd, - NEIGHBOR_CMD "timers connect <0-65535>", + NEIGHBOR_CMD2 "timers connect <1-65535>", NEIGHBOR_STR - NEIGHBOR_ADDR_STR + NEIGHBOR_ADDR_STR2 "BGP per neighbor timers\n" "BGP connect timer\n" "Connect timer\n") @@ -3192,10 +3598,10 @@ DEFUN (neighbor_timers_connect, DEFUN (no_neighbor_timers_connect, no_neighbor_timers_connect_cmd, - NO_NEIGHBOR_CMD "timers connect", + NO_NEIGHBOR_CMD2 "timers connect", NO_STR NEIGHBOR_STR - NEIGHBOR_ADDR_STR + NEIGHBOR_ADDR_STR2 "BGP per neighbor timers\n" "BGP connect timer\n") { @@ -3204,14 +3610,14 @@ DEFUN (no_neighbor_timers_connect, ALIAS (no_neighbor_timers_connect, no_neighbor_timers_connect_val_cmd, - NO_NEIGHBOR_CMD "timers connect <0-65535>", + NO_NEIGHBOR_CMD2 "timers connect <1-65535>", NO_STR NEIGHBOR_STR - NEIGHBOR_ADDR_STR + NEIGHBOR_ADDR_STR2 "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) @@ -3220,7 +3626,7 @@ peer_advertise_interval_vty (struct vty *vty, const char *ip_str, struct peer *peer; u_int32_t routeadv = 0; - peer = peer_lookup_vty (vty, ip_str); + peer = peer_and_group_lookup_vty (vty, ip_str); if (! peer) return CMD_WARNING; @@ -3232,14 +3638,14 @@ 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, neighbor_advertise_interval_cmd, - NEIGHBOR_CMD "advertisement-interval <0-600>", + NEIGHBOR_CMD2 "advertisement-interval <0-600>", NEIGHBOR_STR - NEIGHBOR_ADDR_STR + NEIGHBOR_ADDR_STR2 "Minimum interval between sending BGP routing updates\n" "time in seconds\n") { @@ -3248,10 +3654,10 @@ DEFUN (neighbor_advertise_interval, DEFUN (no_neighbor_advertise_interval, no_neighbor_advertise_interval_cmd, - NO_NEIGHBOR_CMD "advertisement-interval", + NO_NEIGHBOR_CMD2 "advertisement-interval", NO_STR NEIGHBOR_STR - NEIGHBOR_ADDR_STR + NEIGHBOR_ADDR_STR2 "Minimum interval between sending BGP routing updates\n") { return peer_advertise_interval_vty (vty, argv[0], NULL, 0); @@ -3259,13 +3665,13 @@ DEFUN (no_neighbor_advertise_interval, ALIAS (no_neighbor_advertise_interval, no_neighbor_advertise_interval_val_cmd, - NO_NEIGHBOR_CMD "advertisement-interval <0-600>", + NO_NEIGHBOR_CMD2 "advertisement-interval <0-600>", NO_STR NEIGHBOR_STR - NEIGHBOR_ADDR_STR + NEIGHBOR_ADDR_STR2 "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) @@ -3282,7 +3688,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, @@ -3307,7 +3713,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, @@ -3388,7 +3794,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, @@ -3465,7 +3871,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, @@ -3542,7 +3948,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, @@ -3631,7 +4037,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, @@ -3690,7 +4096,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, @@ -3900,7 +4306,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, @@ -3955,7 +4361,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>", @@ -3989,9 +4395,9 @@ DEFUN (no_neighbor_ttl_security, if (! peer) return CMD_WARNING; - return bgp_vty_return (vty, peer_ttl_security_hops_unset (peer)); + return bgp_vty_return (vty, peer_ttl_security_hops_set (peer, 0)); } - + /* Address family configuration. */ DEFUN (address_family_ipv4, address_family_ipv4_cmd, @@ -4062,20 +4468,67 @@ ALIAS (address_family_vpnv4, "Address family\n" "Address Family Modifier\n") +DEFUN (address_family_vpnv6, + address_family_vpnv6_cmd, + "address-family vpnv6", + "Enter Address Family command mode\n" + "Address family\n") +{ + vty->node = BGP_VPNV6_NODE; + return CMD_SUCCESS; +} + +ALIAS (address_family_vpnv6, + address_family_vpnv6_unicast_cmd, + "address-family vpnv6 unicast", + "Enter Address Family command mode\n" + "Address family\n" + "Address Family Modifier\n") + +DEFUN (address_family_encap, + address_family_encap_cmd, + "address-family encap", + "Enter Address Family command mode\n" + "Address family\n") +{ + vty->node = BGP_ENCAP_NODE; + return CMD_SUCCESS; +} + +ALIAS (address_family_encap, + address_family_encapv4_cmd, + "address-family encapv4", + "Enter Address Family command mode\n" + "Address family\n") + +DEFUN (address_family_encapv6, + address_family_encapv6_cmd, + "address-family encapv6", + "Enter Address Family command mode\n" + "Address family\n") +{ + vty->node = BGP_ENCAPV6_NODE; + return CMD_SUCCESS; +} + DEFUN (exit_address_family, exit_address_family_cmd, "exit-address-family", "Exit from Address Family configuration mode\n") { + /* should match list in command.c:config_exit */ if (vty->node == BGP_IPV4_NODE + || vty->node == BGP_ENCAP_NODE + || vty->node == BGP_ENCAPV6_NODE || vty->node == BGP_IPV4M_NODE || vty->node == BGP_VPNV4_NODE + || vty->node == BGP_VPNV6_NODE || vty->node == BGP_IPV6_NODE || vty->node == BGP_IPV6M_NODE) vty->node = BGP_NODE; return CMD_SUCCESS; } - + /* BGP clear sort. */ enum clear_sort { @@ -4198,7 +4651,7 @@ bgp_clear (struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi, { for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer)) { - if (peer_sort (peer) == BGP_PEER_IBGP) + if (peer->sort == BGP_PEER_IBGP) continue; if (stype == BGP_CLEAR_SOFT_NONE) @@ -4242,6 +4695,87 @@ bgp_clear (struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi, return CMD_SUCCESS; } +/* Recalculate bestpath and re-advertise a prefix */ +static int +bgp_clear_prefix (struct vty *vty, char *view_name, const char *ip_str, + afi_t afi, safi_t safi, struct prefix_rd *prd) +{ + int ret; + struct prefix match; + struct bgp_node *rn; + struct bgp_node *rm; + struct bgp *bgp; + struct bgp_table *table; + struct bgp_table *rib; + + /* BGP structure lookup. */ + if (view_name) + { + bgp = bgp_lookup_by_name (view_name); + if (bgp == NULL) + { + vty_out (vty, "%% Can't find BGP view %s%s", view_name, VTY_NEWLINE); + return CMD_WARNING; + } + } + else + { + bgp = bgp_get_default (); + if (bgp == NULL) + { + vty_out (vty, "%% No BGP process is configured%s", VTY_NEWLINE); + return CMD_WARNING; + } + } + + /* Check IP address argument. */ + ret = str2prefix (ip_str, &match); + if (! ret) + { + vty_out (vty, "%% address is malformed%s", VTY_NEWLINE); + return CMD_WARNING; + } + + match.family = afi2family (afi); + rib = bgp->rib[afi][safi]; + + if (safi == SAFI_MPLS_VPN) + { + for (rn = bgp_table_top (rib); rn; rn = bgp_route_next (rn)) + { + if (prd && memcmp (rn->p.u.val, prd->val, 8) != 0) + continue; + + if ((table = rn->info) != NULL) + { + if ((rm = bgp_node_match (table, &match)) != NULL) + { + if (rm->p.prefixlen == match.prefixlen) + { + SET_FLAG (rn->flags, BGP_NODE_USER_CLEAR); + bgp_process (bgp, rm, afi, safi); + } + bgp_unlock_node (rm); + } + } + } + } + else + { + if ((rn = bgp_node_match (rib, &match)) != NULL) + { + if (rn->p.prefixlen == match.prefixlen) + { + SET_FLAG (rn->flags, BGP_NODE_USER_CLEAR); + bgp_process (bgp, rn, afi, safi); + } + bgp_unlock_node (rn); + } + } + + return CMD_SUCCESS; +} + static int bgp_clear_vty (struct vty *vty, const char *name, afi_t afi, safi_t safi, enum clear_sort sort, enum bgp_clear_type stype, @@ -4404,6 +4938,27 @@ ALIAS (clear_ip_bgp_external, "Address family\n" "Clear all external peers\n") +DEFUN (clear_ip_bgp_prefix, + clear_ip_bgp_prefix_cmd, + "clear ip bgp prefix A.B.C.D/M", + CLEAR_STR + IP_STR + BGP_STR + "Clear bestpath and re-advertise\n" + "IP prefix /, e.g., 35.0.0.0/8\n") +{ + return bgp_clear_prefix (vty, NULL, argv[0], AFI_IP, SAFI_UNICAST, NULL); +} + +ALIAS (clear_ip_bgp_prefix, + clear_bgp_prefix_cmd, + "clear bgp prefix A.B.C.D/M", + CLEAR_STR + BGP_STR + "Clear bestpath and re-advertise\n" + "IP prefix /, e.g., 35.0.0.0/8\n") + + DEFUN (clear_ip_bgp_as, clear_ip_bgp_as_cmd, "clear ip bgp " CMD_AS_RANGE, @@ -4429,7 +4984,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, @@ -4438,8 +4993,8 @@ DEFUN (clear_ip_bgp_all_soft_out, IP_STR BGP_STR "Clear all peers\n" - "Soft reconfig\n" - "Soft reconfig outbound update\n") + BGP_SOFT_STR + BGP_SOFT_OUT_STR) { if (argc == 1) return bgp_clear_vty (vty, argv[0], AFI_IP, SAFI_UNICAST, clear_all, @@ -4456,7 +5011,7 @@ ALIAS (clear_ip_bgp_all_soft_out, IP_STR BGP_STR "Clear all peers\n" - "Soft reconfig outbound update\n") + BGP_SOFT_OUT_STR) ALIAS (clear_ip_bgp_all_soft_out, clear_ip_bgp_instance_all_soft_out_cmd, @@ -4467,8 +5022,8 @@ ALIAS (clear_ip_bgp_all_soft_out, "BGP view\n" "view name\n" "Clear all peers\n" - "Soft reconfig\n" - "Soft reconfig outbound update\n") + BGP_SOFT_STR + BGP_SOFT_OUT_STR) DEFUN (clear_ip_bgp_all_ipv4_soft_out, clear_ip_bgp_all_ipv4_soft_out_cmd, @@ -4480,8 +5035,8 @@ DEFUN (clear_ip_bgp_all_ipv4_soft_out, "Address family\n" "Address Family modifier\n" "Address Family modifier\n" - "Soft reconfig\n" - "Soft reconfig outbound update\n") + BGP_SOFT_STR + BGP_SOFT_OUT_STR) { if (strncmp (argv[0], "m", 1) == 0) return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_MULTICAST, clear_all, @@ -4501,7 +5056,7 @@ ALIAS (clear_ip_bgp_all_ipv4_soft_out, "Address family\n" "Address Family modifier\n" "Address Family modifier\n" - "Soft reconfig outbound update\n") + BGP_SOFT_OUT_STR) DEFUN (clear_ip_bgp_instance_all_ipv4_soft_out, clear_ip_bgp_instance_all_ipv4_soft_out_cmd, @@ -4515,7 +5070,7 @@ DEFUN (clear_ip_bgp_instance_all_ipv4_soft_out, "Address family\n" "Address Family modifier\n" "Address Family modifier\n" - "Soft reconfig outbound update\n") + BGP_SOFT_OUT_STR) { if (strncmp (argv[1], "m", 1) == 0) return bgp_clear_vty (vty, argv[0], AFI_IP, SAFI_MULTICAST, clear_all, @@ -4534,8 +5089,8 @@ DEFUN (clear_ip_bgp_all_vpnv4_soft_out, "Clear all peers\n" "Address family\n" "Address Family Modifier\n" - "Soft reconfig\n" - "Soft reconfig outbound update\n") + BGP_SOFT_STR + BGP_SOFT_OUT_STR) { return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_MPLS_VPN, clear_all, BGP_CLEAR_SOFT_OUT, NULL); @@ -4550,6 +5105,33 @@ ALIAS (clear_ip_bgp_all_vpnv4_soft_out, "Clear all peers\n" "Address family\n" "Address Family Modifier\n" + BGP_SOFT_OUT_STR) + +DEFUN (clear_ip_bgp_all_encap_soft_out, + clear_ip_bgp_all_encap_soft_out_cmd, + "clear ip bgp * encap unicast soft out", + CLEAR_STR + IP_STR + BGP_STR + "Clear all peers\n" + "Address family\n" + "Address Family Modifier\n" + "Soft reconfig\n" + "Soft reconfig outbound update\n") +{ + return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_ENCAP, clear_all, + BGP_CLEAR_SOFT_OUT, NULL); +} + +ALIAS (clear_ip_bgp_all_encap_soft_out, + clear_ip_bgp_all_encap_out_cmd, + "clear ip bgp * encap unicast out", + CLEAR_STR + IP_STR + BGP_STR + "Clear all peers\n" + "Address family\n" + "Address Family Modifier\n" "Soft reconfig outbound update\n") DEFUN (clear_bgp_all_soft_out, @@ -4558,8 +5140,8 @@ DEFUN (clear_bgp_all_soft_out, CLEAR_STR BGP_STR "Clear all peers\n" - "Soft reconfig\n" - "Soft reconfig outbound update\n") + BGP_SOFT_STR + BGP_SOFT_OUT_STR) { if (argc == 1) return bgp_clear_vty (vty, argv[0], AFI_IP6, SAFI_UNICAST, clear_all, @@ -4577,8 +5159,8 @@ ALIAS (clear_bgp_all_soft_out, "BGP view\n" "view name\n" "Clear all peers\n" - "Soft reconfig\n" - "Soft reconfig outbound update\n") + BGP_SOFT_STR + BGP_SOFT_OUT_STR) ALIAS (clear_bgp_all_soft_out, clear_bgp_all_out_cmd, @@ -4586,7 +5168,7 @@ ALIAS (clear_bgp_all_soft_out, CLEAR_STR BGP_STR "Clear all peers\n" - "Soft reconfig outbound update\n") + BGP_SOFT_OUT_STR) ALIAS (clear_bgp_all_soft_out, clear_bgp_ipv6_all_soft_out_cmd, @@ -4595,8 +5177,8 @@ ALIAS (clear_bgp_all_soft_out, BGP_STR "Address family\n" "Clear all peers\n" - "Soft reconfig\n" - "Soft reconfig outbound update\n") + BGP_SOFT_STR + BGP_SOFT_OUT_STR) ALIAS (clear_bgp_all_soft_out, clear_bgp_ipv6_all_out_cmd, @@ -4605,7 +5187,23 @@ ALIAS (clear_bgp_all_soft_out, BGP_STR "Address family\n" "Clear all peers\n" - "Soft reconfig outbound update\n") + BGP_SOFT_OUT_STR) + +DEFUN (clear_bgp_ipv6_safi_prefix, + clear_bgp_ipv6_safi_prefix_cmd, + "clear bgp ipv6 (unicast|multicast) prefix X:X::X:X/M", + CLEAR_STR + BGP_STR + "Address family\n" + "Address Family Modifier\n" + "Clear bestpath and re-advertise\n" + "IPv6 prefix /, e.g., 3ffe::/16\n") +{ + if (strncmp (argv[0], "m", 1) == 0) + return bgp_clear_prefix (vty, NULL, argv[1], AFI_IP6, SAFI_MULTICAST, NULL); + else + return bgp_clear_prefix (vty, NULL, argv[1], AFI_IP6, SAFI_UNICAST, NULL); +} DEFUN (clear_ip_bgp_peer_soft_out, clear_ip_bgp_peer_soft_out_cmd, @@ -4614,8 +5212,8 @@ DEFUN (clear_ip_bgp_peer_soft_out, IP_STR BGP_STR "BGP neighbor address to clear\n" - "Soft reconfig\n" - "Soft reconfig outbound update\n") + BGP_SOFT_STR + BGP_SOFT_OUT_STR) { return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_UNICAST, clear_peer, BGP_CLEAR_SOFT_OUT, argv[0]); @@ -4628,7 +5226,7 @@ ALIAS (clear_ip_bgp_peer_soft_out, IP_STR BGP_STR "BGP neighbor address to clear\n" - "Soft reconfig outbound update\n") + BGP_SOFT_OUT_STR) DEFUN (clear_ip_bgp_peer_ipv4_soft_out, clear_ip_bgp_peer_ipv4_soft_out_cmd, @@ -4640,8 +5238,8 @@ DEFUN (clear_ip_bgp_peer_ipv4_soft_out, "Address family\n" "Address Family modifier\n" "Address Family modifier\n" - "Soft reconfig\n" - "Soft reconfig outbound update\n") + BGP_SOFT_STR + BGP_SOFT_OUT_STR) { if (strncmp (argv[1], "m", 1) == 0) return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_MULTICAST, clear_peer, @@ -4661,7 +5259,7 @@ ALIAS (clear_ip_bgp_peer_ipv4_soft_out, "Address family\n" "Address Family modifier\n" "Address Family modifier\n" - "Soft reconfig outbound update\n") + BGP_SOFT_OUT_STR) DEFUN (clear_ip_bgp_peer_vpnv4_soft_out, clear_ip_bgp_peer_vpnv4_soft_out_cmd, @@ -4672,8 +5270,8 @@ DEFUN (clear_ip_bgp_peer_vpnv4_soft_out, "BGP neighbor address to clear\n" "Address family\n" "Address Family Modifier\n" - "Soft reconfig\n" - "Soft reconfig outbound update\n") + BGP_SOFT_STR + BGP_SOFT_OUT_STR) { return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_MPLS_VPN, clear_peer, BGP_CLEAR_SOFT_OUT, argv[0]); @@ -4688,6 +5286,33 @@ ALIAS (clear_ip_bgp_peer_vpnv4_soft_out, "BGP neighbor address to clear\n" "Address family\n" "Address Family Modifier\n" + BGP_SOFT_OUT_STR) + +DEFUN (clear_ip_bgp_peer_encap_soft_out, + clear_ip_bgp_peer_encap_soft_out_cmd, + "clear ip bgp A.B.C.D encap unicast soft out", + CLEAR_STR + IP_STR + BGP_STR + "BGP neighbor address to clear\n" + "Address family\n" + "Address Family Modifier\n" + "Soft reconfig\n" + "Soft reconfig outbound update\n") +{ + return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_ENCAP, clear_peer, + BGP_CLEAR_SOFT_OUT, argv[0]); +} + +ALIAS (clear_ip_bgp_peer_encap_soft_out, + clear_ip_bgp_peer_encap_out_cmd, + "clear ip bgp A.B.C.D encap unicast out", + CLEAR_STR + IP_STR + BGP_STR + "BGP neighbor address to clear\n" + "Address family\n" + "Address Family Modifier\n" "Soft reconfig outbound update\n") DEFUN (clear_bgp_peer_soft_out, @@ -4697,8 +5322,8 @@ DEFUN (clear_bgp_peer_soft_out, BGP_STR "BGP neighbor address to clear\n" "BGP IPv6 neighbor to clear\n" - "Soft reconfig\n" - "Soft reconfig outbound update\n") + BGP_SOFT_STR + BGP_SOFT_OUT_STR) { return bgp_clear_vty (vty, NULL, AFI_IP6, SAFI_UNICAST, clear_peer, BGP_CLEAR_SOFT_OUT, argv[0]); @@ -4712,8 +5337,8 @@ ALIAS (clear_bgp_peer_soft_out, "Address family\n" "BGP neighbor address to clear\n" "BGP IPv6 neighbor to clear\n" - "Soft reconfig\n" - "Soft reconfig outbound update\n") + BGP_SOFT_STR + BGP_SOFT_OUT_STR) ALIAS (clear_bgp_peer_soft_out, clear_bgp_peer_out_cmd, @@ -4722,7 +5347,7 @@ ALIAS (clear_bgp_peer_soft_out, BGP_STR "BGP neighbor address to clear\n" "BGP IPv6 neighbor to clear\n" - "Soft reconfig outbound update\n") + BGP_SOFT_OUT_STR) ALIAS (clear_bgp_peer_soft_out, clear_bgp_ipv6_peer_out_cmd, @@ -4732,7 +5357,7 @@ ALIAS (clear_bgp_peer_soft_out, "Address family\n" "BGP neighbor address to clear\n" "BGP IPv6 neighbor to clear\n" - "Soft reconfig outbound update\n") + BGP_SOFT_OUT_STR) DEFUN (clear_ip_bgp_peer_group_soft_out, clear_ip_bgp_peer_group_soft_out_cmd, @@ -4742,8 +5367,8 @@ DEFUN (clear_ip_bgp_peer_group_soft_out, BGP_STR "Clear all members of peer-group\n" "BGP peer-group name\n" - "Soft reconfig\n" - "Soft reconfig outbound update\n") + BGP_SOFT_STR + BGP_SOFT_OUT_STR) { return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_UNICAST, clear_group, BGP_CLEAR_SOFT_OUT, argv[0]); @@ -4757,7 +5382,7 @@ ALIAS (clear_ip_bgp_peer_group_soft_out, BGP_STR "Clear all members of peer-group\n" "BGP peer-group name\n" - "Soft reconfig outbound update\n") + BGP_SOFT_OUT_STR) DEFUN (clear_ip_bgp_peer_group_ipv4_soft_out, clear_ip_bgp_peer_group_ipv4_soft_out_cmd, @@ -4770,8 +5395,8 @@ DEFUN (clear_ip_bgp_peer_group_ipv4_soft_out, "Address family\n" "Address Family modifier\n" "Address Family modifier\n" - "Soft reconfig\n" - "Soft reconfig outbound update\n") + BGP_SOFT_STR + BGP_SOFT_OUT_STR) { if (strncmp (argv[1], "m", 1) == 0) return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_MULTICAST, clear_group, @@ -4792,7 +5417,7 @@ ALIAS (clear_ip_bgp_peer_group_ipv4_soft_out, "Address family\n" "Address Family modifier\n" "Address Family modifier\n" - "Soft reconfig outbound update\n") + BGP_SOFT_OUT_STR) DEFUN (clear_bgp_peer_group_soft_out, clear_bgp_peer_group_soft_out_cmd, @@ -4801,8 +5426,8 @@ DEFUN (clear_bgp_peer_group_soft_out, BGP_STR "Clear all members of peer-group\n" "BGP peer-group name\n" - "Soft reconfig\n" - "Soft reconfig outbound update\n") + BGP_SOFT_STR + BGP_SOFT_OUT_STR) { return bgp_clear_vty (vty, NULL, AFI_IP6, SAFI_UNICAST, clear_group, BGP_CLEAR_SOFT_OUT, argv[0]); @@ -4816,8 +5441,8 @@ ALIAS (clear_bgp_peer_group_soft_out, "Address family\n" "Clear all members of peer-group\n" "BGP peer-group name\n" - "Soft reconfig\n" - "Soft reconfig outbound update\n") + BGP_SOFT_STR + BGP_SOFT_OUT_STR) ALIAS (clear_bgp_peer_group_soft_out, clear_bgp_peer_group_out_cmd, @@ -4826,7 +5451,7 @@ ALIAS (clear_bgp_peer_group_soft_out, BGP_STR "Clear all members of peer-group\n" "BGP peer-group name\n" - "Soft reconfig outbound update\n") + BGP_SOFT_OUT_STR) ALIAS (clear_bgp_peer_group_soft_out, clear_bgp_ipv6_peer_group_out_cmd, @@ -4836,7 +5461,7 @@ ALIAS (clear_bgp_peer_group_soft_out, "Address family\n" "Clear all members of peer-group\n" "BGP peer-group name\n" - "Soft reconfig outbound update\n") + BGP_SOFT_OUT_STR) DEFUN (clear_ip_bgp_external_soft_out, clear_ip_bgp_external_soft_out_cmd, @@ -4845,8 +5470,8 @@ DEFUN (clear_ip_bgp_external_soft_out, IP_STR BGP_STR "Clear all external peers\n" - "Soft reconfig\n" - "Soft reconfig outbound update\n") + BGP_SOFT_STR + BGP_SOFT_OUT_STR) { return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_UNICAST, clear_external, BGP_CLEAR_SOFT_OUT, NULL); @@ -4859,7 +5484,7 @@ ALIAS (clear_ip_bgp_external_soft_out, IP_STR BGP_STR "Clear all external peers\n" - "Soft reconfig outbound update\n") + BGP_SOFT_OUT_STR) DEFUN (clear_ip_bgp_external_ipv4_soft_out, clear_ip_bgp_external_ipv4_soft_out_cmd, @@ -4871,8 +5496,8 @@ DEFUN (clear_ip_bgp_external_ipv4_soft_out, "Address family\n" "Address Family modifier\n" "Address Family modifier\n" - "Soft reconfig\n" - "Soft reconfig outbound update\n") + BGP_SOFT_STR + BGP_SOFT_OUT_STR) { if (strncmp (argv[0], "m", 1) == 0) return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_MULTICAST, clear_external, @@ -4892,7 +5517,7 @@ ALIAS (clear_ip_bgp_external_ipv4_soft_out, "Address family\n" "Address Family modifier\n" "Address Family modifier\n" - "Soft reconfig outbound update\n") + BGP_SOFT_OUT_STR) DEFUN (clear_bgp_external_soft_out, clear_bgp_external_soft_out_cmd, @@ -4900,8 +5525,8 @@ DEFUN (clear_bgp_external_soft_out, CLEAR_STR BGP_STR "Clear all external peers\n" - "Soft reconfig\n" - "Soft reconfig outbound update\n") + BGP_SOFT_STR + BGP_SOFT_OUT_STR) { return bgp_clear_vty (vty, NULL, AFI_IP6, SAFI_UNICAST, clear_external, BGP_CLEAR_SOFT_OUT, NULL); @@ -4914,8 +5539,8 @@ ALIAS (clear_bgp_external_soft_out, BGP_STR "Address family\n" "Clear all external peers\n" - "Soft reconfig\n" - "Soft reconfig outbound update\n") + BGP_SOFT_STR + BGP_SOFT_OUT_STR) ALIAS (clear_bgp_external_soft_out, clear_bgp_external_out_cmd, @@ -4923,7 +5548,7 @@ ALIAS (clear_bgp_external_soft_out, CLEAR_STR BGP_STR "Clear all external peers\n" - "Soft reconfig outbound update\n") + BGP_SOFT_OUT_STR) ALIAS (clear_bgp_external_soft_out, clear_bgp_ipv6_external_out_cmd, @@ -4932,7 +5557,7 @@ ALIAS (clear_bgp_external_soft_out, BGP_STR "Address family\n" "Clear all external peers\n" - "Soft reconfig outbound update\n") + BGP_SOFT_OUT_STR) DEFUN (clear_ip_bgp_as_soft_out, clear_ip_bgp_as_soft_out_cmd, @@ -4941,8 +5566,8 @@ DEFUN (clear_ip_bgp_as_soft_out, IP_STR BGP_STR "Clear peers with the AS number\n" - "Soft reconfig\n" - "Soft reconfig outbound update\n") + BGP_SOFT_STR + BGP_SOFT_OUT_STR) { return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_UNICAST, clear_as, BGP_CLEAR_SOFT_OUT, argv[0]); @@ -4955,7 +5580,7 @@ ALIAS (clear_ip_bgp_as_soft_out, IP_STR BGP_STR "Clear peers with the AS number\n" - "Soft reconfig outbound update\n") + BGP_SOFT_OUT_STR) DEFUN (clear_ip_bgp_as_ipv4_soft_out, clear_ip_bgp_as_ipv4_soft_out_cmd, @@ -4967,8 +5592,8 @@ DEFUN (clear_ip_bgp_as_ipv4_soft_out, "Address family\n" "Address Family modifier\n" "Address Family modifier\n" - "Soft reconfig\n" - "Soft reconfig outbound update\n") + BGP_SOFT_STR + BGP_SOFT_OUT_STR) { if (strncmp (argv[1], "m", 1) == 0) return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_MULTICAST, clear_as, @@ -4988,7 +5613,7 @@ ALIAS (clear_ip_bgp_as_ipv4_soft_out, "Address family\n" "Address Family modifier\n" "Address Family modifier\n" - "Soft reconfig outbound update\n") + BGP_SOFT_OUT_STR) DEFUN (clear_ip_bgp_as_vpnv4_soft_out, clear_ip_bgp_as_vpnv4_soft_out_cmd, @@ -4999,8 +5624,8 @@ DEFUN (clear_ip_bgp_as_vpnv4_soft_out, "Clear peers with the AS number\n" "Address family\n" "Address Family modifier\n" - "Soft reconfig\n" - "Soft reconfig outbound update\n") + BGP_SOFT_STR + BGP_SOFT_OUT_STR) { return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_MPLS_VPN, clear_as, BGP_CLEAR_SOFT_OUT, argv[0]); @@ -5015,6 +5640,33 @@ ALIAS (clear_ip_bgp_as_vpnv4_soft_out, "Clear peers with the AS number\n" "Address family\n" "Address Family modifier\n" + BGP_SOFT_OUT_STR) + +DEFUN (clear_ip_bgp_as_encap_soft_out, + clear_ip_bgp_as_encap_soft_out_cmd, + "clear ip bgp " CMD_AS_RANGE " encap unicast soft out", + CLEAR_STR + IP_STR + BGP_STR + "Clear peers with the AS number\n" + "Address family\n" + "Address Family modifier\n" + "Soft reconfig\n" + "Soft reconfig outbound update\n") +{ + return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_ENCAP, clear_as, + BGP_CLEAR_SOFT_OUT, argv[0]); +} + +ALIAS (clear_ip_bgp_as_encap_soft_out, + clear_ip_bgp_as_encap_out_cmd, + "clear ip bgp " CMD_AS_RANGE " encap unicast out", + CLEAR_STR + IP_STR + BGP_STR + "Clear peers with the AS number\n" + "Address family\n" + "Address Family modifier\n" "Soft reconfig outbound update\n") DEFUN (clear_bgp_as_soft_out, @@ -5023,8 +5675,8 @@ DEFUN (clear_bgp_as_soft_out, CLEAR_STR BGP_STR "Clear peers with the AS number\n" - "Soft reconfig\n" - "Soft reconfig outbound update\n") + BGP_SOFT_STR + BGP_SOFT_OUT_STR) { return bgp_clear_vty (vty, NULL, AFI_IP6, SAFI_UNICAST, clear_as, BGP_CLEAR_SOFT_OUT, argv[0]); @@ -5037,8 +5689,8 @@ ALIAS (clear_bgp_as_soft_out, BGP_STR "Address family\n" "Clear peers with the AS number\n" - "Soft reconfig\n" - "Soft reconfig outbound update\n") + BGP_SOFT_STR + BGP_SOFT_OUT_STR) ALIAS (clear_bgp_as_soft_out, clear_bgp_as_out_cmd, @@ -5046,7 +5698,7 @@ ALIAS (clear_bgp_as_soft_out, CLEAR_STR BGP_STR "Clear peers with the AS number\n" - "Soft reconfig outbound update\n") + BGP_SOFT_OUT_STR) ALIAS (clear_bgp_as_soft_out, clear_bgp_ipv6_as_out_cmd, @@ -5055,8 +5707,8 @@ ALIAS (clear_bgp_as_soft_out, BGP_STR "Address family\n" "Clear peers with the AS number\n" - "Soft reconfig outbound update\n") - + BGP_SOFT_OUT_STR) + /* Inbound soft-reconfiguration */ DEFUN (clear_ip_bgp_all_soft_in, clear_ip_bgp_all_soft_in_cmd, @@ -5065,8 +5717,8 @@ DEFUN (clear_ip_bgp_all_soft_in, IP_STR BGP_STR "Clear all peers\n" - "Soft reconfig\n" - "Soft reconfig inbound update\n") + BGP_SOFT_STR + BGP_SOFT_IN_STR) { if (argc == 1) return bgp_clear_vty (vty, argv[0], AFI_IP, SAFI_UNICAST, clear_all, @@ -5085,8 +5737,8 @@ ALIAS (clear_ip_bgp_all_soft_in, "BGP view\n" "view name\n" "Clear all peers\n" - "Soft reconfig\n" - "Soft reconfig inbound update\n") + BGP_SOFT_STR + BGP_SOFT_IN_STR) ALIAS (clear_ip_bgp_all_soft_in, clear_ip_bgp_all_in_cmd, @@ -5095,7 +5747,7 @@ ALIAS (clear_ip_bgp_all_soft_in, IP_STR BGP_STR "Clear all peers\n" - "Soft reconfig inbound update\n") + BGP_SOFT_IN_STR) DEFUN (clear_ip_bgp_all_in_prefix_filter, clear_ip_bgp_all_in_prefix_filter_cmd, @@ -5104,7 +5756,7 @@ DEFUN (clear_ip_bgp_all_in_prefix_filter, IP_STR BGP_STR "Clear all peers\n" - "Soft reconfig inbound update\n" + BGP_SOFT_IN_STR "Push out prefix-list ORF and do inbound soft reconfig\n") { if (argc== 1) @@ -5124,7 +5776,7 @@ ALIAS (clear_ip_bgp_all_in_prefix_filter, "BGP view\n" "view name\n" "Clear all peers\n" - "Soft reconfig inbound update\n" + BGP_SOFT_IN_STR "Push out prefix-list ORF and do inbound soft reconfig\n") @@ -5138,8 +5790,8 @@ DEFUN (clear_ip_bgp_all_ipv4_soft_in, "Address family\n" "Address Family modifier\n" "Address Family modifier\n" - "Soft reconfig\n" - "Soft reconfig inbound update\n") + BGP_SOFT_STR + BGP_SOFT_IN_STR) { if (strncmp (argv[0], "m", 1) == 0) return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_MULTICAST, clear_all, @@ -5159,7 +5811,7 @@ ALIAS (clear_ip_bgp_all_ipv4_soft_in, "Address family\n" "Address Family modifier\n" "Address Family modifier\n" - "Soft reconfig inbound update\n") + BGP_SOFT_IN_STR) DEFUN (clear_ip_bgp_instance_all_ipv4_soft_in, clear_ip_bgp_instance_all_ipv4_soft_in_cmd, @@ -5173,8 +5825,8 @@ DEFUN (clear_ip_bgp_instance_all_ipv4_soft_in, "Address family\n" "Address Family modifier\n" "Address Family modifier\n" - "Soft reconfig\n" - "Soft reconfig inbound update\n") + BGP_SOFT_STR + BGP_SOFT_IN_STR) { if (strncmp (argv[1], "m", 1) == 0) return bgp_clear_vty (vty, argv[0], AFI_IP, SAFI_MULTICAST, clear_all, @@ -5194,7 +5846,7 @@ DEFUN (clear_ip_bgp_all_ipv4_in_prefix_filter, "Address family\n" "Address Family modifier\n" "Address Family modifier\n" - "Soft reconfig inbound update\n" + BGP_SOFT_IN_STR "Push out prefix-list ORF and do inbound soft reconfig\n") { if (strncmp (argv[0], "m", 1) == 0) @@ -5215,7 +5867,7 @@ DEFUN (clear_ip_bgp_instance_all_ipv4_in_prefix_filter, "Address family\n" "Address Family modifier\n" "Address Family modifier\n" - "Soft reconfig inbound update\n" + BGP_SOFT_IN_STR "Push out prefix-list ORF and do inbound soft reconfig\n") { if (strncmp (argv[1], "m", 1) == 0) @@ -5235,8 +5887,8 @@ DEFUN (clear_ip_bgp_all_vpnv4_soft_in, "Clear all peers\n" "Address family\n" "Address Family Modifier\n" - "Soft reconfig\n" - "Soft reconfig inbound update\n") + BGP_SOFT_STR + BGP_SOFT_IN_STR) { return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_MPLS_VPN, clear_all, BGP_CLEAR_SOFT_IN, NULL); @@ -5251,6 +5903,33 @@ ALIAS (clear_ip_bgp_all_vpnv4_soft_in, "Clear all peers\n" "Address family\n" "Address Family Modifier\n" + BGP_SOFT_IN_STR) + +DEFUN (clear_ip_bgp_all_encap_soft_in, + clear_ip_bgp_all_encap_soft_in_cmd, + "clear ip bgp * encap unicast soft in", + CLEAR_STR + IP_STR + BGP_STR + "Clear all peers\n" + "Address family\n" + "Address Family Modifier\n" + "Soft reconfig\n" + "Soft reconfig inbound update\n") +{ + return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_ENCAP, clear_all, + BGP_CLEAR_SOFT_IN, NULL); +} + +ALIAS (clear_ip_bgp_all_encap_soft_in, + clear_ip_bgp_all_encap_in_cmd, + "clear ip bgp * encap unicast in", + CLEAR_STR + IP_STR + BGP_STR + "Clear all peers\n" + "Address family\n" + "Address Family Modifier\n" "Soft reconfig inbound update\n") DEFUN (clear_bgp_all_soft_in, @@ -5259,8 +5938,8 @@ DEFUN (clear_bgp_all_soft_in, CLEAR_STR BGP_STR "Clear all peers\n" - "Soft reconfig\n" - "Soft reconfig inbound update\n") + BGP_SOFT_STR + BGP_SOFT_IN_STR) { if (argc == 1) return bgp_clear_vty (vty, argv[0], AFI_IP6, SAFI_UNICAST, clear_all, @@ -5278,8 +5957,8 @@ ALIAS (clear_bgp_all_soft_in, "BGP view\n" "view name\n" "Clear all peers\n" - "Soft reconfig\n" - "Soft reconfig inbound update\n") + BGP_SOFT_STR + BGP_SOFT_IN_STR) ALIAS (clear_bgp_all_soft_in, clear_bgp_ipv6_all_soft_in_cmd, @@ -5288,8 +5967,8 @@ ALIAS (clear_bgp_all_soft_in, BGP_STR "Address family\n" "Clear all peers\n" - "Soft reconfig\n" - "Soft reconfig inbound update\n") + BGP_SOFT_STR + BGP_SOFT_IN_STR) ALIAS (clear_bgp_all_soft_in, clear_bgp_all_in_cmd, @@ -5297,7 +5976,7 @@ ALIAS (clear_bgp_all_soft_in, CLEAR_STR BGP_STR "Clear all peers\n" - "Soft reconfig inbound update\n") + BGP_SOFT_IN_STR) ALIAS (clear_bgp_all_soft_in, clear_bgp_ipv6_all_in_cmd, @@ -5306,7 +5985,7 @@ ALIAS (clear_bgp_all_soft_in, BGP_STR "Address family\n" "Clear all peers\n" - "Soft reconfig inbound update\n") + BGP_SOFT_IN_STR) DEFUN (clear_bgp_all_in_prefix_filter, clear_bgp_all_in_prefix_filter_cmd, @@ -5314,7 +5993,7 @@ DEFUN (clear_bgp_all_in_prefix_filter, CLEAR_STR BGP_STR "Clear all peers\n" - "Soft reconfig inbound update\n" + BGP_SOFT_IN_STR "Push out prefix-list ORF and do inbound soft reconfig\n") { return bgp_clear_vty (vty, NULL, AFI_IP6, SAFI_UNICAST, clear_all, @@ -5328,7 +6007,7 @@ ALIAS (clear_bgp_all_in_prefix_filter, BGP_STR "Address family\n" "Clear all peers\n" - "Soft reconfig inbound update\n" + BGP_SOFT_IN_STR "Push out prefix-list ORF and do inbound soft reconfig\n") DEFUN (clear_ip_bgp_peer_soft_in, @@ -5338,8 +6017,8 @@ DEFUN (clear_ip_bgp_peer_soft_in, IP_STR BGP_STR "BGP neighbor address to clear\n" - "Soft reconfig\n" - "Soft reconfig inbound update\n") + BGP_SOFT_STR + BGP_SOFT_IN_STR) { return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_UNICAST, clear_peer, BGP_CLEAR_SOFT_IN, argv[0]); @@ -5352,7 +6031,7 @@ ALIAS (clear_ip_bgp_peer_soft_in, IP_STR BGP_STR "BGP neighbor address to clear\n" - "Soft reconfig inbound update\n") + BGP_SOFT_IN_STR) DEFUN (clear_ip_bgp_peer_in_prefix_filter, clear_ip_bgp_peer_in_prefix_filter_cmd, @@ -5361,7 +6040,7 @@ DEFUN (clear_ip_bgp_peer_in_prefix_filter, IP_STR BGP_STR "BGP neighbor address to clear\n" - "Soft reconfig inbound update\n" + BGP_SOFT_IN_STR "Push out the existing ORF prefix-list\n") { return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_UNICAST, clear_peer, @@ -5378,8 +6057,8 @@ DEFUN (clear_ip_bgp_peer_ipv4_soft_in, "Address family\n" "Address Family modifier\n" "Address Family modifier\n" - "Soft reconfig\n" - "Soft reconfig inbound update\n") + BGP_SOFT_STR + BGP_SOFT_IN_STR) { if (strncmp (argv[1], "m", 1) == 0) return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_MULTICAST, clear_peer, @@ -5399,7 +6078,7 @@ ALIAS (clear_ip_bgp_peer_ipv4_soft_in, "Address family\n" "Address Family modifier\n" "Address Family modifier\n" - "Soft reconfig inbound update\n") + BGP_SOFT_IN_STR) DEFUN (clear_ip_bgp_peer_ipv4_in_prefix_filter, clear_ip_bgp_peer_ipv4_in_prefix_filter_cmd, @@ -5411,7 +6090,7 @@ DEFUN (clear_ip_bgp_peer_ipv4_in_prefix_filter, "Address family\n" "Address Family modifier\n" "Address Family modifier\n" - "Soft reconfig inbound update\n" + BGP_SOFT_IN_STR "Push out the existing ORF prefix-list\n") { if (strncmp (argv[1], "m", 1) == 0) @@ -5431,8 +6110,8 @@ DEFUN (clear_ip_bgp_peer_vpnv4_soft_in, "BGP neighbor address to clear\n" "Address family\n" "Address Family Modifier\n" - "Soft reconfig\n" - "Soft reconfig inbound update\n") + BGP_SOFT_STR + BGP_SOFT_IN_STR) { return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_MPLS_VPN, clear_peer, BGP_CLEAR_SOFT_IN, argv[0]); @@ -5447,6 +6126,33 @@ ALIAS (clear_ip_bgp_peer_vpnv4_soft_in, "BGP neighbor address to clear\n" "Address family\n" "Address Family Modifier\n" + BGP_SOFT_IN_STR) + +DEFUN (clear_ip_bgp_peer_encap_soft_in, + clear_ip_bgp_peer_encap_soft_in_cmd, + "clear ip bgp A.B.C.D encap unicast soft in", + CLEAR_STR + IP_STR + BGP_STR + "BGP neighbor address to clear\n" + "Address family\n" + "Address Family Modifier\n" + "Soft reconfig\n" + "Soft reconfig inbound update\n") +{ + return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_ENCAP, clear_peer, + BGP_CLEAR_SOFT_IN, argv[0]); +} + +ALIAS (clear_ip_bgp_peer_encap_soft_in, + clear_ip_bgp_peer_encap_in_cmd, + "clear ip bgp A.B.C.D encap unicast in", + CLEAR_STR + IP_STR + BGP_STR + "BGP neighbor address to clear\n" + "Address family\n" + "Address Family Modifier\n" "Soft reconfig inbound update\n") DEFUN (clear_bgp_peer_soft_in, @@ -5456,8 +6162,8 @@ DEFUN (clear_bgp_peer_soft_in, BGP_STR "BGP neighbor address to clear\n" "BGP IPv6 neighbor to clear\n" - "Soft reconfig\n" - "Soft reconfig inbound update\n") + BGP_SOFT_STR + BGP_SOFT_IN_STR) { return bgp_clear_vty (vty, NULL, AFI_IP6, SAFI_UNICAST, clear_peer, BGP_CLEAR_SOFT_IN, argv[0]); @@ -5471,8 +6177,8 @@ ALIAS (clear_bgp_peer_soft_in, "Address family\n" "BGP neighbor address to clear\n" "BGP IPv6 neighbor to clear\n" - "Soft reconfig\n" - "Soft reconfig inbound update\n") + BGP_SOFT_STR + BGP_SOFT_IN_STR) ALIAS (clear_bgp_peer_soft_in, clear_bgp_peer_in_cmd, @@ -5481,7 +6187,7 @@ ALIAS (clear_bgp_peer_soft_in, BGP_STR "BGP neighbor address to clear\n" "BGP IPv6 neighbor to clear\n" - "Soft reconfig inbound update\n") + BGP_SOFT_IN_STR) ALIAS (clear_bgp_peer_soft_in, clear_bgp_ipv6_peer_in_cmd, @@ -5491,7 +6197,7 @@ ALIAS (clear_bgp_peer_soft_in, "Address family\n" "BGP neighbor address to clear\n" "BGP IPv6 neighbor to clear\n" - "Soft reconfig inbound update\n") + BGP_SOFT_IN_STR) DEFUN (clear_bgp_peer_in_prefix_filter, clear_bgp_peer_in_prefix_filter_cmd, @@ -5500,7 +6206,7 @@ DEFUN (clear_bgp_peer_in_prefix_filter, BGP_STR "BGP neighbor address to clear\n" "BGP IPv6 neighbor to clear\n" - "Soft reconfig inbound update\n" + BGP_SOFT_IN_STR "Push out the existing ORF prefix-list\n") { return bgp_clear_vty (vty, NULL, AFI_IP6, SAFI_UNICAST, clear_peer, @@ -5515,7 +6221,7 @@ ALIAS (clear_bgp_peer_in_prefix_filter, "Address family\n" "BGP neighbor address to clear\n" "BGP IPv6 neighbor to clear\n" - "Soft reconfig inbound update\n" + BGP_SOFT_IN_STR "Push out the existing ORF prefix-list\n") DEFUN (clear_ip_bgp_peer_group_soft_in, @@ -5526,8 +6232,8 @@ DEFUN (clear_ip_bgp_peer_group_soft_in, BGP_STR "Clear all members of peer-group\n" "BGP peer-group name\n" - "Soft reconfig\n" - "Soft reconfig inbound update\n") + BGP_SOFT_STR + BGP_SOFT_IN_STR) { return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_UNICAST, clear_group, BGP_CLEAR_SOFT_IN, argv[0]); @@ -5541,7 +6247,7 @@ ALIAS (clear_ip_bgp_peer_group_soft_in, BGP_STR "Clear all members of peer-group\n" "BGP peer-group name\n" - "Soft reconfig inbound update\n") + BGP_SOFT_IN_STR) DEFUN (clear_ip_bgp_peer_group_in_prefix_filter, clear_ip_bgp_peer_group_in_prefix_filter_cmd, @@ -5551,7 +6257,7 @@ DEFUN (clear_ip_bgp_peer_group_in_prefix_filter, BGP_STR "Clear all members of peer-group\n" "BGP peer-group name\n" - "Soft reconfig inbound update\n" + BGP_SOFT_IN_STR "Push out prefix-list ORF and do inbound soft reconfig\n") { return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_UNICAST, clear_group, @@ -5569,8 +6275,8 @@ DEFUN (clear_ip_bgp_peer_group_ipv4_soft_in, "Address family\n" "Address Family modifier\n" "Address Family modifier\n" - "Soft reconfig\n" - "Soft reconfig inbound update\n") + BGP_SOFT_STR + BGP_SOFT_IN_STR) { if (strncmp (argv[1], "m", 1) == 0) return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_MULTICAST, clear_group, @@ -5591,7 +6297,7 @@ ALIAS (clear_ip_bgp_peer_group_ipv4_soft_in, "Address family\n" "Address Family modifier\n" "Address Family modifier\n" - "Soft reconfig inbound update\n") + BGP_SOFT_IN_STR) DEFUN (clear_ip_bgp_peer_group_ipv4_in_prefix_filter, clear_ip_bgp_peer_group_ipv4_in_prefix_filter_cmd, @@ -5604,7 +6310,7 @@ DEFUN (clear_ip_bgp_peer_group_ipv4_in_prefix_filter, "Address family\n" "Address Family modifier\n" "Address Family modifier\n" - "Soft reconfig inbound update\n" + BGP_SOFT_IN_STR "Push out prefix-list ORF and do inbound soft reconfig\n") { if (strncmp (argv[1], "m", 1) == 0) @@ -5622,8 +6328,8 @@ DEFUN (clear_bgp_peer_group_soft_in, BGP_STR "Clear all members of peer-group\n" "BGP peer-group name\n" - "Soft reconfig\n" - "Soft reconfig inbound update\n") + BGP_SOFT_STR + BGP_SOFT_IN_STR) { return bgp_clear_vty (vty, NULL, AFI_IP6, SAFI_UNICAST, clear_group, BGP_CLEAR_SOFT_IN, argv[0]); @@ -5637,8 +6343,8 @@ ALIAS (clear_bgp_peer_group_soft_in, "Address family\n" "Clear all members of peer-group\n" "BGP peer-group name\n" - "Soft reconfig\n" - "Soft reconfig inbound update\n") + BGP_SOFT_STR + BGP_SOFT_IN_STR) ALIAS (clear_bgp_peer_group_soft_in, clear_bgp_peer_group_in_cmd, @@ -5647,7 +6353,7 @@ ALIAS (clear_bgp_peer_group_soft_in, BGP_STR "Clear all members of peer-group\n" "BGP peer-group name\n" - "Soft reconfig inbound update\n") + BGP_SOFT_IN_STR) ALIAS (clear_bgp_peer_group_soft_in, clear_bgp_ipv6_peer_group_in_cmd, @@ -5657,7 +6363,7 @@ ALIAS (clear_bgp_peer_group_soft_in, "Address family\n" "Clear all members of peer-group\n" "BGP peer-group name\n" - "Soft reconfig inbound update\n") + BGP_SOFT_IN_STR) DEFUN (clear_bgp_peer_group_in_prefix_filter, clear_bgp_peer_group_in_prefix_filter_cmd, @@ -5666,7 +6372,7 @@ DEFUN (clear_bgp_peer_group_in_prefix_filter, BGP_STR "Clear all members of peer-group\n" "BGP peer-group name\n" - "Soft reconfig inbound update\n" + BGP_SOFT_IN_STR "Push out prefix-list ORF and do inbound soft reconfig\n") { return bgp_clear_vty (vty, NULL, AFI_IP6, SAFI_UNICAST, clear_group, @@ -5681,7 +6387,7 @@ ALIAS (clear_bgp_peer_group_in_prefix_filter, "Address family\n" "Clear all members of peer-group\n" "BGP peer-group name\n" - "Soft reconfig inbound update\n" + BGP_SOFT_IN_STR "Push out prefix-list ORF and do inbound soft reconfig\n") DEFUN (clear_ip_bgp_external_soft_in, @@ -5691,8 +6397,8 @@ DEFUN (clear_ip_bgp_external_soft_in, IP_STR BGP_STR "Clear all external peers\n" - "Soft reconfig\n" - "Soft reconfig inbound update\n") + BGP_SOFT_STR + BGP_SOFT_IN_STR) { return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_UNICAST, clear_external, BGP_CLEAR_SOFT_IN, NULL); @@ -5705,7 +6411,7 @@ ALIAS (clear_ip_bgp_external_soft_in, IP_STR BGP_STR "Clear all external peers\n" - "Soft reconfig inbound update\n") + BGP_SOFT_IN_STR) DEFUN (clear_ip_bgp_external_in_prefix_filter, clear_ip_bgp_external_in_prefix_filter_cmd, @@ -5714,7 +6420,7 @@ DEFUN (clear_ip_bgp_external_in_prefix_filter, IP_STR BGP_STR "Clear all external peers\n" - "Soft reconfig inbound update\n" + BGP_SOFT_IN_STR "Push out prefix-list ORF and do inbound soft reconfig\n") { return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_UNICAST, clear_external, @@ -5731,8 +6437,8 @@ DEFUN (clear_ip_bgp_external_ipv4_soft_in, "Address family\n" "Address Family modifier\n" "Address Family modifier\n" - "Soft reconfig\n" - "Soft reconfig inbound update\n") + BGP_SOFT_STR + BGP_SOFT_IN_STR) { if (strncmp (argv[0], "m", 1) == 0) return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_MULTICAST, clear_external, @@ -5752,7 +6458,7 @@ ALIAS (clear_ip_bgp_external_ipv4_soft_in, "Address family\n" "Address Family modifier\n" "Address Family modifier\n" - "Soft reconfig inbound update\n") + BGP_SOFT_IN_STR) DEFUN (clear_ip_bgp_external_ipv4_in_prefix_filter, clear_ip_bgp_external_ipv4_in_prefix_filter_cmd, @@ -5764,7 +6470,7 @@ DEFUN (clear_ip_bgp_external_ipv4_in_prefix_filter, "Address family\n" "Address Family modifier\n" "Address Family modifier\n" - "Soft reconfig inbound update\n" + BGP_SOFT_IN_STR "Push out prefix-list ORF and do inbound soft reconfig\n") { if (strncmp (argv[0], "m", 1) == 0) @@ -5781,8 +6487,8 @@ DEFUN (clear_bgp_external_soft_in, CLEAR_STR BGP_STR "Clear all external peers\n" - "Soft reconfig\n" - "Soft reconfig inbound update\n") + BGP_SOFT_STR + BGP_SOFT_IN_STR) { return bgp_clear_vty (vty, NULL, AFI_IP6, SAFI_UNICAST, clear_external, BGP_CLEAR_SOFT_IN, NULL); @@ -5795,8 +6501,8 @@ ALIAS (clear_bgp_external_soft_in, BGP_STR "Address family\n" "Clear all external peers\n" - "Soft reconfig\n" - "Soft reconfig inbound update\n") + BGP_SOFT_STR + BGP_SOFT_IN_STR) ALIAS (clear_bgp_external_soft_in, clear_bgp_external_in_cmd, @@ -5804,7 +6510,7 @@ ALIAS (clear_bgp_external_soft_in, CLEAR_STR BGP_STR "Clear all external peers\n" - "Soft reconfig inbound update\n") + BGP_SOFT_IN_STR) ALIAS (clear_bgp_external_soft_in, clear_bgp_ipv6_external_in_cmd, @@ -5813,7 +6519,7 @@ ALIAS (clear_bgp_external_soft_in, BGP_STR "Address family\n" "Clear all external peers\n" - "Soft reconfig inbound update\n") + BGP_SOFT_IN_STR) DEFUN (clear_bgp_external_in_prefix_filter, clear_bgp_external_in_prefix_filter_cmd, @@ -5821,7 +6527,7 @@ DEFUN (clear_bgp_external_in_prefix_filter, CLEAR_STR BGP_STR "Clear all external peers\n" - "Soft reconfig inbound update\n" + BGP_SOFT_IN_STR "Push out prefix-list ORF and do inbound soft reconfig\n") { return bgp_clear_vty (vty, NULL, AFI_IP6, SAFI_UNICAST, clear_external, @@ -5835,7 +6541,7 @@ ALIAS (clear_bgp_external_in_prefix_filter, BGP_STR "Address family\n" "Clear all external peers\n" - "Soft reconfig inbound update\n" + BGP_SOFT_IN_STR "Push out prefix-list ORF and do inbound soft reconfig\n") DEFUN (clear_ip_bgp_as_soft_in, @@ -5845,8 +6551,8 @@ DEFUN (clear_ip_bgp_as_soft_in, IP_STR BGP_STR "Clear peers with the AS number\n" - "Soft reconfig\n" - "Soft reconfig inbound update\n") + BGP_SOFT_STR + BGP_SOFT_IN_STR) { return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_UNICAST, clear_as, BGP_CLEAR_SOFT_IN, argv[0]); @@ -5859,7 +6565,7 @@ ALIAS (clear_ip_bgp_as_soft_in, IP_STR BGP_STR "Clear peers with the AS number\n" - "Soft reconfig inbound update\n") + BGP_SOFT_IN_STR) DEFUN (clear_ip_bgp_as_in_prefix_filter, clear_ip_bgp_as_in_prefix_filter_cmd, @@ -5868,7 +6574,7 @@ DEFUN (clear_ip_bgp_as_in_prefix_filter, IP_STR BGP_STR "Clear peers with the AS number\n" - "Soft reconfig inbound update\n" + BGP_SOFT_IN_STR "Push out prefix-list ORF and do inbound soft reconfig\n") { return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_UNICAST, clear_as, @@ -5885,8 +6591,8 @@ DEFUN (clear_ip_bgp_as_ipv4_soft_in, "Address family\n" "Address Family modifier\n" "Address Family modifier\n" - "Soft reconfig\n" - "Soft reconfig inbound update\n") + BGP_SOFT_STR + BGP_SOFT_IN_STR) { if (strncmp (argv[1], "m", 1) == 0) return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_MULTICAST, clear_as, @@ -5906,7 +6612,7 @@ ALIAS (clear_ip_bgp_as_ipv4_soft_in, "Address family\n" "Address Family modifier\n" "Address Family modifier\n" - "Soft reconfig inbound update\n") + BGP_SOFT_IN_STR) DEFUN (clear_ip_bgp_as_ipv4_in_prefix_filter, clear_ip_bgp_as_ipv4_in_prefix_filter_cmd, @@ -5918,7 +6624,7 @@ DEFUN (clear_ip_bgp_as_ipv4_in_prefix_filter, "Address family\n" "Address Family modifier\n" "Address Family modifier\n" - "Soft reconfig inbound update\n" + BGP_SOFT_IN_STR "Push out prefix-list ORF and do inbound soft reconfig\n") { if (strncmp (argv[1], "m", 1) == 0) @@ -5938,8 +6644,8 @@ DEFUN (clear_ip_bgp_as_vpnv4_soft_in, "Clear peers with the AS number\n" "Address family\n" "Address Family modifier\n" - "Soft reconfig\n" - "Soft reconfig inbound update\n") + BGP_SOFT_STR + BGP_SOFT_IN_STR) { return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_MPLS_VPN, clear_as, BGP_CLEAR_SOFT_IN, argv[0]); @@ -5954,6 +6660,33 @@ ALIAS (clear_ip_bgp_as_vpnv4_soft_in, "Clear peers with the AS number\n" "Address family\n" "Address Family modifier\n" + BGP_SOFT_IN_STR) + +DEFUN (clear_ip_bgp_as_encap_soft_in, + clear_ip_bgp_as_encap_soft_in_cmd, + "clear ip bgp " CMD_AS_RANGE " encap unicast soft in", + CLEAR_STR + IP_STR + BGP_STR + "Clear peers with the AS number\n" + "Address family\n" + "Address Family modifier\n" + "Soft reconfig\n" + "Soft reconfig inbound update\n") +{ + return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_ENCAP, clear_as, + BGP_CLEAR_SOFT_IN, argv[0]); +} + +ALIAS (clear_ip_bgp_as_encap_soft_in, + clear_ip_bgp_as_encap_in_cmd, + "clear ip bgp " CMD_AS_RANGE " encap unicast in", + CLEAR_STR + IP_STR + BGP_STR + "Clear peers with the AS number\n" + "Address family\n" + "Address Family modifier\n" "Soft reconfig inbound update\n") DEFUN (clear_bgp_as_soft_in, @@ -5962,8 +6695,8 @@ DEFUN (clear_bgp_as_soft_in, CLEAR_STR BGP_STR "Clear peers with the AS number\n" - "Soft reconfig\n" - "Soft reconfig inbound update\n") + BGP_SOFT_STR + BGP_SOFT_IN_STR) { return bgp_clear_vty (vty, NULL, AFI_IP6, SAFI_UNICAST, clear_as, BGP_CLEAR_SOFT_IN, argv[0]); @@ -5976,8 +6709,8 @@ ALIAS (clear_bgp_as_soft_in, BGP_STR "Address family\n" "Clear peers with the AS number\n" - "Soft reconfig\n" - "Soft reconfig inbound update\n") + BGP_SOFT_STR + BGP_SOFT_IN_STR) ALIAS (clear_bgp_as_soft_in, clear_bgp_as_in_cmd, @@ -5985,7 +6718,7 @@ ALIAS (clear_bgp_as_soft_in, CLEAR_STR BGP_STR "Clear peers with the AS number\n" - "Soft reconfig inbound update\n") + BGP_SOFT_IN_STR) ALIAS (clear_bgp_as_soft_in, clear_bgp_ipv6_as_in_cmd, @@ -5994,7 +6727,7 @@ ALIAS (clear_bgp_as_soft_in, BGP_STR "Address family\n" "Clear peers with the AS number\n" - "Soft reconfig inbound update\n") + BGP_SOFT_IN_STR) DEFUN (clear_bgp_as_in_prefix_filter, clear_bgp_as_in_prefix_filter_cmd, @@ -6002,7 +6735,7 @@ DEFUN (clear_bgp_as_in_prefix_filter, CLEAR_STR BGP_STR "Clear peers with the AS number\n" - "Soft reconfig inbound update\n" + BGP_SOFT_IN_STR "Push out prefix-list ORF and do inbound soft reconfig\n") { return bgp_clear_vty (vty, NULL, AFI_IP6, SAFI_UNICAST, clear_as, @@ -6016,9 +6749,9 @@ ALIAS (clear_bgp_as_in_prefix_filter, BGP_STR "Address family\n" "Clear peers with the AS number\n" - "Soft reconfig inbound update\n" + BGP_SOFT_IN_STR "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, @@ -6027,7 +6760,7 @@ DEFUN (clear_ip_bgp_all_soft, IP_STR BGP_STR "Clear all peers\n" - "Soft reconfig\n") + BGP_SOFT_STR) { if (argc == 1) return bgp_clear_vty (vty, argv[0], AFI_IP, SAFI_UNICAST, clear_all, @@ -6046,7 +6779,7 @@ ALIAS (clear_ip_bgp_all_soft, "BGP view\n" "view name\n" "Clear all peers\n" - "Soft reconfig\n") + BGP_SOFT_STR) DEFUN (clear_ip_bgp_all_ipv4_soft, @@ -6059,7 +6792,7 @@ DEFUN (clear_ip_bgp_all_ipv4_soft, "Address family\n" "Address Family Modifier\n" "Address Family Modifier\n" - "Soft reconfig\n") + BGP_SOFT_STR) { if (strncmp (argv[0], "m", 1) == 0) return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_MULTICAST, clear_all, @@ -6081,7 +6814,7 @@ DEFUN (clear_ip_bgp_instance_all_ipv4_soft, "Address family\n" "Address Family Modifier\n" "Address Family Modifier\n" - "Soft reconfig\n") + BGP_SOFT_STR) { if (strncmp (argv[1], "m", 1) == 0) return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_MULTICAST, clear_all, @@ -6100,19 +6833,34 @@ DEFUN (clear_ip_bgp_all_vpnv4_soft, "Clear all peers\n" "Address family\n" "Address Family Modifier\n" - "Soft reconfig\n") + BGP_SOFT_STR) { return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_MPLS_VPN, clear_all, BGP_CLEAR_SOFT_BOTH, argv[0]); } +DEFUN (clear_ip_bgp_all_encap_soft, + clear_ip_bgp_all_encap_soft_cmd, + "clear ip bgp * encap unicast soft", + CLEAR_STR + IP_STR + BGP_STR + "Clear all peers\n" + "Address family\n" + "Address Family Modifier\n" + "Soft reconfig\n") +{ + return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_ENCAP, clear_all, + BGP_CLEAR_SOFT_BOTH, argv[0]); +} + DEFUN (clear_bgp_all_soft, clear_bgp_all_soft_cmd, "clear bgp * soft", CLEAR_STR BGP_STR "Clear all peers\n" - "Soft reconfig\n") + BGP_SOFT_STR) { if (argc == 1) return bgp_clear_vty (vty, argv[0], AFI_IP6, SAFI_UNICAST, clear_all, @@ -6130,7 +6878,7 @@ ALIAS (clear_bgp_all_soft, "BGP view\n" "view name\n" "Clear all peers\n" - "Soft reconfig\n") + BGP_SOFT_STR) ALIAS (clear_bgp_all_soft, clear_bgp_ipv6_all_soft_cmd, @@ -6139,7 +6887,7 @@ ALIAS (clear_bgp_all_soft, BGP_STR "Address family\n" "Clear all peers\n" - "Soft reconfig\n") + BGP_SOFT_STR) DEFUN (clear_ip_bgp_peer_soft, clear_ip_bgp_peer_soft_cmd, @@ -6148,7 +6896,7 @@ DEFUN (clear_ip_bgp_peer_soft, IP_STR BGP_STR "BGP neighbor address to clear\n" - "Soft reconfig\n") + BGP_SOFT_STR) { return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_UNICAST, clear_peer, BGP_CLEAR_SOFT_BOTH, argv[0]); @@ -6164,7 +6912,7 @@ DEFUN (clear_ip_bgp_peer_ipv4_soft, "Address family\n" "Address Family Modifier\n" "Address Family Modifier\n" - "Soft reconfig\n") + BGP_SOFT_STR) { if (strncmp (argv[1], "m", 1) == 0) return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_MULTICAST, clear_peer, @@ -6183,12 +6931,27 @@ DEFUN (clear_ip_bgp_peer_vpnv4_soft, "BGP neighbor address to clear\n" "Address family\n" "Address Family Modifier\n" - "Soft reconfig\n") + BGP_SOFT_STR) { return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_MPLS_VPN, clear_peer, BGP_CLEAR_SOFT_BOTH, argv[0]); } +DEFUN (clear_ip_bgp_peer_encap_soft, + clear_ip_bgp_peer_encap_soft_cmd, + "clear ip bgp A.B.C.D encap unicast soft", + CLEAR_STR + IP_STR + BGP_STR + "BGP neighbor address to clear\n" + "Address family\n" + "Address Family Modifier\n" + "Soft reconfig\n") +{ + return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_ENCAP, clear_peer, + BGP_CLEAR_SOFT_BOTH, argv[0]); +} + DEFUN (clear_bgp_peer_soft, clear_bgp_peer_soft_cmd, "clear bgp (A.B.C.D|X:X::X:X) soft", @@ -6196,7 +6959,7 @@ DEFUN (clear_bgp_peer_soft, BGP_STR "BGP neighbor address to clear\n" "BGP IPv6 neighbor to clear\n" - "Soft reconfig\n") + BGP_SOFT_STR) { return bgp_clear_vty (vty, NULL, AFI_IP6, SAFI_UNICAST, clear_peer, BGP_CLEAR_SOFT_BOTH, argv[0]); @@ -6210,7 +6973,7 @@ ALIAS (clear_bgp_peer_soft, "Address family\n" "BGP neighbor address to clear\n" "BGP IPv6 neighbor to clear\n" - "Soft reconfig\n") + BGP_SOFT_STR) DEFUN (clear_ip_bgp_peer_group_soft, clear_ip_bgp_peer_group_soft_cmd, @@ -6220,7 +6983,7 @@ DEFUN (clear_ip_bgp_peer_group_soft, BGP_STR "Clear all members of peer-group\n" "BGP peer-group name\n" - "Soft reconfig\n") + BGP_SOFT_STR) { return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_UNICAST, clear_group, BGP_CLEAR_SOFT_BOTH, argv[0]); @@ -6237,7 +7000,7 @@ DEFUN (clear_ip_bgp_peer_group_ipv4_soft, "Address family\n" "Address Family modifier\n" "Address Family modifier\n" - "Soft reconfig\n") + BGP_SOFT_STR) { if (strncmp (argv[1], "m", 1) == 0) return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_MULTICAST, clear_group, @@ -6254,7 +7017,7 @@ DEFUN (clear_bgp_peer_group_soft, BGP_STR "Clear all members of peer-group\n" "BGP peer-group name\n" - "Soft reconfig\n") + BGP_SOFT_STR) { return bgp_clear_vty (vty, NULL, AFI_IP6, SAFI_UNICAST, clear_group, BGP_CLEAR_SOFT_BOTH, argv[0]); @@ -6268,7 +7031,7 @@ ALIAS (clear_bgp_peer_group_soft, "Address family\n" "Clear all members of peer-group\n" "BGP peer-group name\n" - "Soft reconfig\n") + BGP_SOFT_STR) DEFUN (clear_ip_bgp_external_soft, clear_ip_bgp_external_soft_cmd, @@ -6277,7 +7040,7 @@ DEFUN (clear_ip_bgp_external_soft, IP_STR BGP_STR "Clear all external peers\n" - "Soft reconfig\n") + BGP_SOFT_STR) { return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_UNICAST, clear_external, BGP_CLEAR_SOFT_BOTH, NULL); @@ -6293,7 +7056,7 @@ DEFUN (clear_ip_bgp_external_ipv4_soft, "Address family\n" "Address Family modifier\n" "Address Family modifier\n" - "Soft reconfig\n") + BGP_SOFT_STR) { if (strncmp (argv[0], "m", 1) == 0) return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_MULTICAST, clear_external, @@ -6309,7 +7072,7 @@ DEFUN (clear_bgp_external_soft, CLEAR_STR BGP_STR "Clear all external peers\n" - "Soft reconfig\n") + BGP_SOFT_STR) { return bgp_clear_vty (vty, NULL, AFI_IP6, SAFI_UNICAST, clear_external, BGP_CLEAR_SOFT_BOTH, NULL); @@ -6322,7 +7085,7 @@ ALIAS (clear_bgp_external_soft, BGP_STR "Address family\n" "Clear all external peers\n" - "Soft reconfig\n") + BGP_SOFT_STR) DEFUN (clear_ip_bgp_as_soft, clear_ip_bgp_as_soft_cmd, @@ -6331,7 +7094,7 @@ DEFUN (clear_ip_bgp_as_soft, IP_STR BGP_STR "Clear peers with the AS number\n" - "Soft reconfig\n") + BGP_SOFT_STR) { return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_UNICAST, clear_as, BGP_CLEAR_SOFT_BOTH, argv[0]); @@ -6347,7 +7110,7 @@ DEFUN (clear_ip_bgp_as_ipv4_soft, "Address family\n" "Address Family Modifier\n" "Address Family Modifier\n" - "Soft reconfig\n") + BGP_SOFT_STR) { if (strncmp (argv[1], "m", 1) == 0) return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_MULTICAST, clear_as, @@ -6366,19 +7129,34 @@ DEFUN (clear_ip_bgp_as_vpnv4_soft, "Clear peers with the AS number\n" "Address family\n" "Address Family Modifier\n" - "Soft reconfig\n") + BGP_SOFT_STR) { return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_MPLS_VPN, clear_as, BGP_CLEAR_SOFT_BOTH, argv[0]); } +DEFUN (clear_ip_bgp_as_encap_soft, + clear_ip_bgp_as_encap_soft_cmd, + "clear ip bgp " CMD_AS_RANGE " encap unicast soft", + CLEAR_STR + IP_STR + BGP_STR + "Clear peers with the AS number\n" + "Address family\n" + "Address Family Modifier\n" + "Soft reconfig\n") +{ + return bgp_clear_vty (vty, NULL, AFI_IP, SAFI_ENCAP, clear_as, + BGP_CLEAR_SOFT_BOTH, argv[0]); +} + DEFUN (clear_bgp_as_soft, clear_bgp_as_soft_cmd, "clear bgp " CMD_AS_RANGE " soft", CLEAR_STR BGP_STR "Clear peers with the AS number\n" - "Soft reconfig\n") + BGP_SOFT_STR) { return bgp_clear_vty (vty, NULL, AFI_IP6, SAFI_UNICAST, clear_as, BGP_CLEAR_SOFT_BOTH, argv[0]); @@ -6391,17 +7169,16 @@ ALIAS (clear_bgp_as_soft, BGP_STR "Address family\n" "Clear peers with the AS number\n" - "Soft reconfig\n") - + BGP_SOFT_STR) + /* RS-client soft reconfiguration. */ -#ifdef HAVE_IPV6 DEFUN (clear_bgp_all_rsclient, clear_bgp_all_rsclient_cmd, "clear bgp * rsclient", CLEAR_STR BGP_STR "Clear all peers\n" - "Soft reconfig for rsclient RIB\n") + BGP_SOFT_RSCLIENT_RIB_STR) { if (argc == 1) return bgp_clear_vty (vty, argv[0], AFI_IP6, SAFI_UNICAST, clear_all, @@ -6418,7 +7195,7 @@ ALIAS (clear_bgp_all_rsclient, BGP_STR "Address family\n" "Clear all peers\n" - "Soft reconfig for rsclient RIB\n") + BGP_SOFT_RSCLIENT_RIB_STR) ALIAS (clear_bgp_all_rsclient, clear_bgp_instance_all_rsclient_cmd, @@ -6428,7 +7205,7 @@ ALIAS (clear_bgp_all_rsclient, "BGP view\n" "view name\n" "Clear all peers\n" - "Soft reconfig for rsclient RIB\n") + BGP_SOFT_RSCLIENT_RIB_STR) ALIAS (clear_bgp_all_rsclient, clear_bgp_ipv6_instance_all_rsclient_cmd, @@ -6439,8 +7216,7 @@ ALIAS (clear_bgp_all_rsclient, "BGP view\n" "view name\n" "Clear all peers\n" - "Soft reconfig for rsclient RIB\n") -#endif /* HAVE_IPV6 */ + BGP_SOFT_RSCLIENT_RIB_STR) DEFUN (clear_ip_bgp_all_rsclient, clear_ip_bgp_all_rsclient_cmd, @@ -6449,7 +7225,7 @@ DEFUN (clear_ip_bgp_all_rsclient, IP_STR BGP_STR "Clear all peers\n" - "Soft reconfig for rsclient RIB\n") + BGP_SOFT_RSCLIENT_RIB_STR) { if (argc == 1) return bgp_clear_vty (vty, argv[0], AFI_IP, SAFI_UNICAST, clear_all, @@ -6468,9 +7244,8 @@ ALIAS (clear_ip_bgp_all_rsclient, "BGP view\n" "view name\n" "Clear all peers\n" - "Soft reconfig for rsclient RIB\n") + BGP_SOFT_RSCLIENT_RIB_STR) -#ifdef HAVE_IPV6 DEFUN (clear_bgp_peer_rsclient, clear_bgp_peer_rsclient_cmd, "clear bgp (A.B.C.D|X:X::X:X) rsclient", @@ -6478,7 +7253,7 @@ DEFUN (clear_bgp_peer_rsclient, BGP_STR "BGP neighbor IP address to clear\n" "BGP IPv6 neighbor to clear\n" - "Soft reconfig for rsclient RIB\n") + BGP_SOFT_RSCLIENT_RIB_STR) { if (argc == 2) return bgp_clear_vty (vty, argv[0], AFI_IP6, SAFI_UNICAST, clear_peer, @@ -6496,7 +7271,7 @@ ALIAS (clear_bgp_peer_rsclient, "Address family\n" "BGP neighbor IP address to clear\n" "BGP IPv6 neighbor to clear\n" - "Soft reconfig for rsclient RIB\n") + BGP_SOFT_RSCLIENT_RIB_STR) ALIAS (clear_bgp_peer_rsclient, clear_bgp_instance_peer_rsclient_cmd, @@ -6507,7 +7282,7 @@ ALIAS (clear_bgp_peer_rsclient, "view name\n" "BGP neighbor IP address to clear\n" "BGP IPv6 neighbor to clear\n" - "Soft reconfig for rsclient RIB\n") + BGP_SOFT_RSCLIENT_RIB_STR) ALIAS (clear_bgp_peer_rsclient, clear_bgp_ipv6_instance_peer_rsclient_cmd, @@ -6519,8 +7294,7 @@ ALIAS (clear_bgp_peer_rsclient, "view name\n" "BGP neighbor IP address to clear\n" "BGP IPv6 neighbor to clear\n" - "Soft reconfig for rsclient RIB\n") -#endif /* HAVE_IPV6 */ + BGP_SOFT_RSCLIENT_RIB_STR) DEFUN (clear_ip_bgp_peer_rsclient, clear_ip_bgp_peer_rsclient_cmd, @@ -6530,7 +7304,7 @@ DEFUN (clear_ip_bgp_peer_rsclient, BGP_STR "BGP neighbor IP address to clear\n" "BGP IPv6 neighbor to clear\n" - "Soft reconfig for rsclient RIB\n") + BGP_SOFT_RSCLIENT_RIB_STR) { if (argc == 2) return bgp_clear_vty (vty, argv[0], AFI_IP, SAFI_UNICAST, clear_peer, @@ -6550,7 +7324,7 @@ ALIAS (clear_ip_bgp_peer_rsclient, "view name\n" "BGP neighbor IP address to clear\n" "BGP IPv6 neighbor to clear\n" - "Soft reconfig for rsclient RIB\n") + BGP_SOFT_RSCLIENT_RIB_STR) DEFUN (show_bgp_views, show_bgp_views_cmd, @@ -6675,7 +7449,12 @@ DEFUN (show_bgp_memory, mtype_memstr (memstrbuf, sizeof (memstrbuf), count * sizeof (struct ecommunity)), VTY_NEWLINE); - + if ((count = mtype_stats_alloc (MTYPE_LCOMMUNITY))) + vty_out (vty, "%ld BGP large-community entries, using %s of memory%s", + count, + mtype_memstr (memstrbuf, sizeof (memstrbuf), + count * sizeof (struct lcommunity)), + VTY_NEWLINE); if ((count = mtype_stats_alloc (MTYPE_CLUSTER))) vty_out (vty, "%ld Cluster lists, using %s of memory%s", count, mtype_memstr (memstrbuf, sizeof (memstrbuf), @@ -6721,11 +7500,13 @@ bgp_show_summary (struct vty *vty, struct bgp *bgp, int afi, int safi) struct peer *peer; struct listnode *node, *nnode; unsigned int count = 0; + unsigned int totrcount = 0; + unsigned int totecount = 0; char timebuf[BGP_UPTIME_LEN]; 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)) { @@ -6785,14 +7566,16 @@ bgp_show_summary (struct vty *vty, struct bgp *bgp, int afi, int safi) vty_out (vty, "4 "); - vty_out (vty, "%5u %7d %7d %8d %4d %4lu ", + vty_out (vty, "%5u %7d %7d %8d %4d %4d ", peer->as, peer->open_in + peer->update_in + peer->keepalive_in + peer->notify_in + peer->refresh_in + peer->dynamic_cap_in, peer->open_out + peer->update_out + peer->keepalive_out + peer->notify_out + peer->refresh_out + peer->dynamic_cap_out, - 0, 0, (unsigned long) peer->obuf->count); + 0, 0, + peer->sync[afi][safi]->update.count + + peer->sync[afi][safi]->withdraw.count); vty_out (vty, "%8s", peer_uptime (peer->uptime, timebuf, BGP_UPTIME_LEN)); @@ -6800,6 +7583,8 @@ bgp_show_summary (struct vty *vty, struct bgp *bgp, int afi, int safi) if (peer->status == Established) { vty_out (vty, " %8ld", peer->pcount[afi][safi]); + totrcount += peer->pcount[afi][safi]; + totecount++; } else { @@ -6816,8 +7601,14 @@ bgp_show_summary (struct vty *vty, struct bgp *bgp, int afi, int safi) } if (count) - vty_out (vty, "%sTotal number of neighbors %d%s", VTY_NEWLINE, - count, VTY_NEWLINE); + { + vty_out (vty, "%sTotal number of neighbors %d%s", VTY_NEWLINE, + count, VTY_NEWLINE); + vty_out (vty, "%sTotal num. Established sessions %d%s", VTY_NEWLINE, + totecount, VTY_NEWLINE); + vty_out (vty, "Total num. of routes received %d%s", + totrcount, VTY_NEWLINE); + } else vty_out (vty, "No %s neighbor is configured%s", afi == AFI_IP ? "IPv4" : "IPv6", VTY_NEWLINE); @@ -6894,16 +7685,6 @@ DEFUN (show_ip_bgp_ipv4_summary, return bgp_show_summary_vty (vty, NULL, AFI_IP, SAFI_UNICAST); } -ALIAS (show_ip_bgp_ipv4_summary, - show_bgp_ipv4_safi_summary_cmd, - "show bgp ipv4 (unicast|multicast) summary", - SHOW_STR - BGP_STR - "Address family\n" - "Address Family modifier\n" - "Address Family modifier\n" - "Summary of BGP neighbor status\n") - DEFUN (show_ip_bgp_instance_ipv4_summary, show_ip_bgp_instance_ipv4_summary_cmd, "show ip bgp view WORD ipv4 (unicast|multicast) summary", @@ -6923,18 +7704,6 @@ DEFUN (show_ip_bgp_instance_ipv4_summary, return bgp_show_summary_vty (vty, argv[0], AFI_IP, SAFI_UNICAST); } -ALIAS (show_ip_bgp_instance_ipv4_summary, - show_bgp_instance_ipv4_safi_summary_cmd, - "show bgp view WORD ipv4 (unicast|multicast) summary", - SHOW_STR - BGP_STR - "BGP view\n" - "View name\n" - "Address family\n" - "Address Family modifier\n" - "Address Family modifier\n" - "Summary of BGP neighbor status\n") - DEFUN (show_ip_bgp_vpnv4_all_summary, show_ip_bgp_vpnv4_all_summary_cmd, "show ip bgp vpnv4 all summary", @@ -6972,15 +7741,87 @@ DEFUN (show_ip_bgp_vpnv4_rd_summary, return bgp_show_summary_vty (vty, NULL, AFI_IP, SAFI_MPLS_VPN); } -#ifdef HAVE_IPV6 -DEFUN (show_bgp_summary, - show_bgp_summary_cmd, - "show bgp summary", +DEFUN (show_bgp_ipv4_safi_summary, + show_bgp_ipv4_safi_summary_cmd, + "show bgp ipv4 (unicast|multicast) summary", SHOW_STR BGP_STR + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" "Summary of BGP neighbor status\n") { - return bgp_show_summary_vty (vty, NULL, AFI_IP6, SAFI_UNICAST); + if (strncmp (argv[0], "m", 1) == 0) + return bgp_show_summary_vty (vty, NULL, AFI_IP, SAFI_MULTICAST); + + return bgp_show_summary_vty (vty, NULL, AFI_IP, SAFI_UNICAST); +} + +DEFUN (show_bgp_instance_ipv4_safi_summary, + show_bgp_instance_ipv4_safi_summary_cmd, + "show bgp view WORD ipv4 (unicast|multicast) summary", + SHOW_STR + BGP_STR + "BGP view\n" + "View name\n" + "Address family\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Summary of BGP neighbor status\n") +{ + if (strncmp (argv[1], "m", 1) == 0) + return bgp_show_summary_vty (vty, argv[0], AFI_IP, SAFI_MULTICAST); + else + return bgp_show_summary_vty (vty, argv[0], AFI_IP, SAFI_UNICAST); +} + +DEFUN (show_bgp_ipv4_vpn_summary, + show_bgp_ipv4_vpn_summary_cmd, + "show bgp ipv4 vpn summary", + SHOW_STR + BGP_STR + "IPv4\n" + "Display VPN NLRI specific information\n" + "Summary of BGP neighbor status\n") +{ + return bgp_show_summary_vty (vty, NULL, AFI_IP, SAFI_MPLS_VPN); +} + +/* `show ip bgp summary' commands. */ +DEFUN (show_bgp_ipv6_vpn_summary, + show_bgp_ipv6_vpn_summary_cmd, + "show bgp ipv6 vpn summary", + SHOW_STR + BGP_STR + "IPv6\n" + "Display VPN NLRI specific information\n" + "Summary of BGP neighbor status\n") +{ + return bgp_show_summary_vty (vty, NULL, AFI_IP6, SAFI_MPLS_VPN); +} + +DEFUN (show_bgp_ipv4_encap_summary, + show_bgp_ipv4_encap_summary_cmd, + "show bgp ipv4 encap summary", + SHOW_STR + BGP_STR + "IPv4\n" + "Display ENCAP NLRI specific information\n" + "Summary of BGP neighbor status\n") +{ + return bgp_show_summary_vty (vty, NULL, AFI_IP, SAFI_ENCAP); +} + +DEFUN (show_bgp_ipv6_encap_summary, + show_bgp_ipv6_encap_summary_cmd, + "show bgp ipv6 encap summary", + SHOW_STR + BGP_STR + "IPv6\n" + "Display ENCAP NLRI specific information\n" + "Summary of BGP neighbor status\n") +{ + return bgp_show_summary_vty (vty, NULL, AFI_IP6, SAFI_ENCAP); } DEFUN (show_bgp_instance_summary, @@ -6992,26 +7833,90 @@ DEFUN (show_bgp_instance_summary, "View name\n" "Summary of BGP neighbor status\n") { - return bgp_show_summary_vty (vty, argv[0], AFI_IP6, SAFI_UNICAST); + vty_out(vty, "%sIPv4 Unicast Summary:%s", VTY_NEWLINE, VTY_NEWLINE); + vty_out(vty, "---------------------%s", VTY_NEWLINE); + bgp_show_summary_vty (vty, argv[0], AFI_IP, SAFI_UNICAST); + vty_out(vty, "%sIPv4 Multicast Summary:%s", VTY_NEWLINE, VTY_NEWLINE); + vty_out(vty, "-----------------------%s", VTY_NEWLINE); + bgp_show_summary_vty (vty, argv[0], AFI_IP, SAFI_MULTICAST); + vty_out(vty, "%sIPv4 VPN Summary:%s", VTY_NEWLINE, VTY_NEWLINE); + vty_out(vty, "-----------------%s", VTY_NEWLINE); + bgp_show_summary_vty (vty, argv[0], AFI_IP, SAFI_MPLS_VPN); + vty_out(vty, "%sIPv4 Encap Summary:%s", VTY_NEWLINE, VTY_NEWLINE); + vty_out(vty, "-------------------%s", VTY_NEWLINE); + bgp_show_summary_vty (vty, argv[0], AFI_IP, SAFI_ENCAP); + + vty_out(vty, "%sIPv6 Unicast Summary:%s", VTY_NEWLINE, VTY_NEWLINE); + vty_out(vty, "---------------------%s", VTY_NEWLINE); + bgp_show_summary_vty (vty, argv[0], AFI_IP6, SAFI_UNICAST); + vty_out(vty, "%sIPv6 Multicast Summary:%s", VTY_NEWLINE, VTY_NEWLINE); + vty_out(vty, "-----------------------%s", VTY_NEWLINE); + bgp_show_summary_vty (vty, argv[0], AFI_IP6, SAFI_MULTICAST); + vty_out(vty, "%sIPv6 VPN Summary:%s", VTY_NEWLINE, VTY_NEWLINE); + vty_out(vty, "-----------------%s", VTY_NEWLINE); + bgp_show_summary_vty (vty, argv[0], AFI_IP6, SAFI_MPLS_VPN); + vty_out(vty, "%sIPv6 Encap Summary:%s", VTY_NEWLINE, VTY_NEWLINE); + vty_out(vty, "-------------------%s", VTY_NEWLINE); + bgp_show_summary_vty (vty, argv[0], AFI_IP6, SAFI_ENCAP); + + return CMD_SUCCESS; } -ALIAS (show_bgp_summary, - show_bgp_ipv6_summary_cmd, - "show bgp ipv6 summary", +DEFUN (show_bgp_instance_ipv4_summary, + show_bgp_instance_ipv4_summary_cmd, + "show bgp view WORD ipv4 summary", SHOW_STR BGP_STR - "Address family\n" + IP_STR + "Address Family modifier\n" + "Address Family modifier\n" + "BGP view\n" + "View name\n" "Summary of BGP neighbor status\n") +{ + vty_out(vty, "%sIPv4 Unicast Summary:%s", VTY_NEWLINE, VTY_NEWLINE); + vty_out(vty, "---------------------%s", VTY_NEWLINE); + bgp_show_summary_vty (vty, argv[0], AFI_IP, SAFI_UNICAST); + vty_out(vty, "%sIPv4 Multicast Summary:%s", VTY_NEWLINE, VTY_NEWLINE); + vty_out(vty, "-----------------------%s", VTY_NEWLINE); + bgp_show_summary_vty (vty, argv[0], AFI_IP, SAFI_MULTICAST); + vty_out(vty, "%sIPv4 VPN Summary:%s", VTY_NEWLINE, VTY_NEWLINE); + vty_out(vty, "-----------------%s", VTY_NEWLINE); + bgp_show_summary_vty (vty, argv[0], AFI_IP, SAFI_MPLS_VPN); + vty_out(vty, "%sIPv4 Encap Summary:%s", VTY_NEWLINE, VTY_NEWLINE); + vty_out(vty, "-------------------%s", VTY_NEWLINE); + bgp_show_summary_vty (vty, argv[0], AFI_IP, SAFI_ENCAP); + + return CMD_SUCCESS; +} -ALIAS (show_bgp_instance_summary, +DEFUN (show_bgp_instance_ipv6_summary, show_bgp_instance_ipv6_summary_cmd, "show bgp view WORD ipv6 summary", SHOW_STR BGP_STR + IPV6_STR + "Address Family modifier\n" + "Address Family modifier\n" "BGP view\n" "View name\n" - "Address family\n" "Summary of BGP neighbor status\n") +{ + vty_out(vty, "%sIPv6 Unicast Summary:%s", VTY_NEWLINE, VTY_NEWLINE); + vty_out(vty, "---------------------%s", VTY_NEWLINE); + bgp_show_summary_vty (vty, argv[0], AFI_IP6, SAFI_UNICAST); + vty_out(vty, "%sIPv6 Multicast Summary:%s", VTY_NEWLINE, VTY_NEWLINE); + vty_out(vty, "-----------------------%s", VTY_NEWLINE); + bgp_show_summary_vty (vty, argv[0], AFI_IP6, SAFI_MULTICAST); + vty_out(vty, "%sIPv6 VPN Summary:%s", VTY_NEWLINE, VTY_NEWLINE); + vty_out(vty, "-----------------%s", VTY_NEWLINE); + bgp_show_summary_vty (vty, argv[0], AFI_IP6, SAFI_MPLS_VPN); + vty_out(vty, "%sIPv6 Encap Summary:%s", VTY_NEWLINE, VTY_NEWLINE); + vty_out(vty, "-------------------%s", VTY_NEWLINE); + bgp_show_summary_vty (vty, argv[0], AFI_IP6, SAFI_ENCAP); + + return CMD_SUCCESS; +} DEFUN (show_bgp_ipv6_safi_summary, show_bgp_ipv6_safi_summary_cmd, @@ -7070,8 +7975,145 @@ DEFUN (show_ipv6_mbgp_summary, { return bgp_show_summary_vty (vty, NULL, AFI_IP6, SAFI_MULTICAST); } -#endif /* HAVE_IPV6 */ - + +/* variations of show bgp [...] summary */ + +/* This one is for the 0-keyword variant */ +DEFUN (show_bgp_summary, + show_bgp_summary_cmd, + "show bgp summary", + SHOW_STR + BGP_STR + "Summary of BGP neighbor status\n") +{ + vty_out(vty, "%sIPv4 Unicast Summary:%s", VTY_NEWLINE, VTY_NEWLINE); + vty_out(vty, "---------------------%s", VTY_NEWLINE); + bgp_show_summary_vty (vty, NULL, AFI_IP, SAFI_UNICAST); + vty_out(vty, "%sIPv4 Multicast Summary:%s", VTY_NEWLINE, VTY_NEWLINE); + vty_out(vty, "-----------------------%s", VTY_NEWLINE); + bgp_show_summary_vty (vty, NULL, AFI_IP, SAFI_MULTICAST); + vty_out(vty, "%sIPv4 VPN Summary:%s", VTY_NEWLINE, VTY_NEWLINE); + vty_out(vty, "-----------------%s", VTY_NEWLINE); + bgp_show_summary_vty (vty, NULL, AFI_IP, SAFI_MPLS_VPN); + vty_out(vty, "%sIPv4 Encap Summary:%s", VTY_NEWLINE, VTY_NEWLINE); + vty_out(vty, "-------------------%s", VTY_NEWLINE); + bgp_show_summary_vty (vty, NULL, AFI_IP, SAFI_ENCAP); + + vty_out(vty, "%sIPv6 Unicast Summary:%s", VTY_NEWLINE, VTY_NEWLINE); + vty_out(vty, "---------------------%s", VTY_NEWLINE); + bgp_show_summary_vty (vty, NULL, AFI_IP6, SAFI_UNICAST); + vty_out(vty, "%sIPv6 Multicast Summary:%s", VTY_NEWLINE, VTY_NEWLINE); + vty_out(vty, "-----------------------%s", VTY_NEWLINE); + bgp_show_summary_vty (vty, NULL, AFI_IP6, SAFI_MULTICAST); + vty_out(vty, "%sIPv6 VPN Summary:%s", VTY_NEWLINE, VTY_NEWLINE); + vty_out(vty, "-----------------%s", VTY_NEWLINE); + bgp_show_summary_vty (vty, NULL, AFI_IP6, SAFI_MPLS_VPN); + vty_out(vty, "%sIPv6 Encap Summary:%s", VTY_NEWLINE, VTY_NEWLINE); + vty_out(vty, "-------------------%s", VTY_NEWLINE); + bgp_show_summary_vty (vty, NULL, AFI_IP6, SAFI_ENCAP); + + return CMD_SUCCESS; +} + +ALIAS (show_bgp_summary, + show_bgp_ipv6_summary_cmd, + "show bgp ipv6 summary", + SHOW_STR + BGP_STR + "Address family\n" + "Summary of BGP neighbor status\n") + +DEFUN (show_bgp_summary_1w, + show_bgp_summary_1w_cmd, + "show bgp (ipv4|ipv6|unicast|multicast|vpn|encap) summary", + SHOW_STR + BGP_STR + IP_STR + IP6_STR + "Address Family modifier\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Address Family modifier\n" + "Summary of BGP neighbor status\n") +{ + if (strcmp (argv[0], "ipv4") == 0) { + vty_out(vty, "%sIPv4 Unicast Summary:%s", VTY_NEWLINE, VTY_NEWLINE); + vty_out(vty, "---------------------%s", VTY_NEWLINE); + bgp_show_summary_vty (vty, NULL, AFI_IP, SAFI_UNICAST); + vty_out(vty, "%sIPv4 Multicast Summary:%s", VTY_NEWLINE, VTY_NEWLINE); + vty_out(vty, "-----------------------%s", VTY_NEWLINE); + bgp_show_summary_vty (vty, NULL, AFI_IP, SAFI_MULTICAST); + vty_out(vty, "%sIPv4 VPN Summary:%s", VTY_NEWLINE, VTY_NEWLINE); + vty_out(vty, "-----------------%s", VTY_NEWLINE); + bgp_show_summary_vty (vty, NULL, AFI_IP, SAFI_MPLS_VPN); + vty_out(vty, "%sIPv4 Encap Summary:%s", VTY_NEWLINE, VTY_NEWLINE); + vty_out(vty, "-------------------%s", VTY_NEWLINE); + bgp_show_summary_vty (vty, NULL, AFI_IP, SAFI_ENCAP); + return CMD_SUCCESS; + } + + if (strcmp (argv[0], "ipv6") == 0) { + vty_out(vty, "%sIPv6 Unicast Summary:%s", VTY_NEWLINE, VTY_NEWLINE); + vty_out(vty, "---------------------%s", VTY_NEWLINE); + bgp_show_summary_vty (vty, NULL, AFI_IP6, SAFI_UNICAST); + vty_out(vty, "%sIPv6 Multicast Summary:%s", VTY_NEWLINE, VTY_NEWLINE); + vty_out(vty, "-----------------------%s", VTY_NEWLINE); + bgp_show_summary_vty (vty, NULL, AFI_IP6, SAFI_MULTICAST); + vty_out(vty, "%sIPv6 VPN Summary:%s", VTY_NEWLINE, VTY_NEWLINE); + vty_out(vty, "-----------------%s", VTY_NEWLINE); + bgp_show_summary_vty (vty, NULL, AFI_IP6, SAFI_MPLS_VPN); + vty_out(vty, "%sIPv6 Encap Summary:%s", VTY_NEWLINE, VTY_NEWLINE); + vty_out(vty, "-------------------%s", VTY_NEWLINE); + bgp_show_summary_vty (vty, NULL, AFI_IP6, SAFI_ENCAP); + return CMD_SUCCESS; + } + + if (strcmp (argv[0], "unicast") == 0) { + vty_out(vty, "IPv4 Unicast Summary:%s", VTY_NEWLINE); + vty_out(vty, "---------------------%s", VTY_NEWLINE); + bgp_show_summary_vty (vty, NULL, AFI_IP, SAFI_UNICAST); + vty_out(vty, "%s", VTY_NEWLINE); + vty_out(vty, "IPv6 Unicast Summary:%s", VTY_NEWLINE); + vty_out(vty, "---------------------%s", VTY_NEWLINE); + bgp_show_summary_vty (vty, NULL, AFI_IP6, SAFI_UNICAST); + return CMD_SUCCESS; + } + if (strcmp (argv[0], "multicast") == 0) { + vty_out(vty, "IPv4 Multicast Summary:%s", VTY_NEWLINE); + vty_out(vty, "-----------------------%s", VTY_NEWLINE); + bgp_show_summary_vty (vty, NULL, AFI_IP, SAFI_MULTICAST); + vty_out(vty, "%s", VTY_NEWLINE); + vty_out(vty, "IPv6 Multicast Summary:%s", VTY_NEWLINE); + vty_out(vty, "-----------------------%s", VTY_NEWLINE); + bgp_show_summary_vty (vty, NULL, AFI_IP6, SAFI_MULTICAST); + return CMD_SUCCESS; + } + if (strcmp (argv[0], "vpn") == 0) { + vty_out(vty, "IPv4 VPN Summary:%s", VTY_NEWLINE); + vty_out(vty, "-----------------%s", VTY_NEWLINE); + bgp_show_summary_vty (vty, NULL, AFI_IP, SAFI_MPLS_VPN); + vty_out(vty, "%s", VTY_NEWLINE); + vty_out(vty, "IPv6 VPN Summary:%s", VTY_NEWLINE); + vty_out(vty, "-----------------%s", VTY_NEWLINE); + bgp_show_summary_vty (vty, NULL, AFI_IP6, SAFI_MPLS_VPN); + return CMD_SUCCESS; + } + if (strcmp (argv[0], "encap") == 0) { + vty_out(vty, "IPv4 Encap Summary:%s", VTY_NEWLINE); + vty_out(vty, "-------------------%s", VTY_NEWLINE); + bgp_show_summary_vty (vty, NULL, AFI_IP, SAFI_ENCAP); + vty_out(vty, "%s", VTY_NEWLINE); + vty_out(vty, "IPv6 Encap Summary:%s", VTY_NEWLINE); + vty_out(vty, "-------------------%s", VTY_NEWLINE); + bgp_show_summary_vty (vty, NULL, AFI_IP6, SAFI_ENCAP); + return CMD_SUCCESS; + } + vty_out(vty, "Unknown keyword: %s%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; +} + + + const char * afi_safi_print (afi_t afi, safi_t safi) { @@ -7080,11 +8122,17 @@ afi_safi_print (afi_t afi, safi_t safi) else if (afi == AFI_IP && safi == SAFI_MULTICAST) return "IPv4 Multicast"; else if (afi == AFI_IP && safi == SAFI_MPLS_VPN) - return "VPNv4 Unicast"; + return "VPN-IPv4 Unicast"; + else if (afi == AFI_IP && safi == SAFI_ENCAP) + return "ENCAP-IPv4 Unicast"; else if (afi == AFI_IP6 && safi == SAFI_UNICAST) return "IPv6 Unicast"; else if (afi == AFI_IP6 && safi == SAFI_MULTICAST) return "IPv6 Multicast"; + else if (afi == AFI_IP6 && safi == SAFI_MPLS_VPN) + return "VPN-IPv6 Unicast"; + else if (afi == AFI_IP6 && safi == SAFI_ENCAP) + return "ENCAP-IPv6 Unicast"; else return "Unknown"; } @@ -7214,14 +8262,18 @@ bgp_show_peer_afi (struct vty *vty, struct peer *p, afi_t afi, safi_t safi) if (CHECK_FLAG (p->af_flags[afi][safi], PEER_FLAG_MED_UNCHANGED)) vty_out (vty, " MED is propagated unchanged to this neighbor%s", VTY_NEWLINE); if (CHECK_FLAG (p->af_flags[afi][safi], PEER_FLAG_SEND_COMMUNITY) - || CHECK_FLAG (p->af_flags[afi][safi], PEER_FLAG_SEND_EXT_COMMUNITY)) + || CHECK_FLAG (p->af_flags[afi][safi], PEER_FLAG_SEND_EXT_COMMUNITY) + || CHECK_FLAG (p->af_flags[afi][safi], PEER_FLAG_SEND_LARGE_COMMUNITY)) { vty_out (vty, " Community attribute sent to this neighbor"); if (CHECK_FLAG (p->af_flags[afi][safi], PEER_FLAG_SEND_COMMUNITY) - && CHECK_FLAG (p->af_flags[afi][safi], PEER_FLAG_SEND_EXT_COMMUNITY)) - vty_out (vty, "(both)%s", VTY_NEWLINE); + && CHECK_FLAG (p->af_flags[afi][safi], PEER_FLAG_SEND_EXT_COMMUNITY) + && CHECK_FLAG (p->af_flags[afi][safi], PEER_FLAG_SEND_LARGE_COMMUNITY)) + vty_out (vty, "(all)%s", VTY_NEWLINE); else if (CHECK_FLAG (p->af_flags[afi][safi], PEER_FLAG_SEND_EXT_COMMUNITY)) vty_out (vty, "(extended)%s", VTY_NEWLINE); + else if (CHECK_FLAG (p->af_flags[afi][safi], PEER_FLAG_SEND_LARGE_COMMUNITY)) + vty_out (vty, "(large)%s", VTY_NEWLINE); else vty_out (vty, "(standard)%s", VTY_NEWLINE); } @@ -7346,16 +8398,19 @@ bgp_show_peer (struct vty *vty, struct peer *p) char timebuf[BGP_UPTIME_LEN]; afi_t afi; safi_t safi; + int ttl; bgp = p->bgp; /* 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); @@ -7420,12 +8475,16 @@ bgp_show_peer (struct vty *vty, struct peer *p) || p->afc_recv[AFI_IP][SAFI_UNICAST] || p->afc_adv[AFI_IP][SAFI_MULTICAST] || p->afc_recv[AFI_IP][SAFI_MULTICAST] -#ifdef HAVE_IPV6 || p->afc_adv[AFI_IP6][SAFI_UNICAST] || p->afc_recv[AFI_IP6][SAFI_UNICAST] || p->afc_adv[AFI_IP6][SAFI_MULTICAST] || p->afc_recv[AFI_IP6][SAFI_MULTICAST] -#endif /* HAVE_IPV6 */ + || p->afc_adv[AFI_IP6][SAFI_MPLS_VPN] + || p->afc_recv[AFI_IP6][SAFI_MPLS_VPN] + || p->afc_adv[AFI_IP6][SAFI_ENCAP] + || p->afc_recv[AFI_IP6][SAFI_ENCAP] + || p->afc_adv[AFI_IP][SAFI_ENCAP] + || p->afc_recv[AFI_IP][SAFI_ENCAP] || p->afc_adv[AFI_IP][SAFI_MPLS_VPN] || p->afc_recv[AFI_IP][SAFI_MPLS_VPN]) { @@ -7639,15 +8698,12 @@ bgp_show_peer (struct vty *vty, struct peer *p) } /* EBGP Multihop and GTSM */ - if (peer_sort (p) != BGP_PEER_IBGP) - { - if (p->gtsm_hops > 0) - vty_out (vty, " External BGP neighbor may be up to %d hops away.%s", - p->gtsm_hops, VTY_NEWLINE); - else if (p->ttl > 1) - vty_out (vty, " External BGP neighbor may be up to %d hops away.%s", - p->ttl, VTY_NEWLINE); - } + ttl = p->gtsm_hops; + if (! ttl) + ttl = peer_ttl (p); + vty_out (vty, " %s BGP neighbor may be up to %d hops away.%s", + p->sort == BGP_PEER_IBGP ? "Internal" : "External", + ttl, VTY_NEWLINE); /* Local address. */ if (p->su_local) @@ -7673,7 +8729,6 @@ bgp_show_peer (struct vty *vty, struct peer *p) vty_out (vty, "Nexthop: %s%s", inet_ntop (AF_INET, &p->nexthop.v4, buf1, BUFSIZ), VTY_NEWLINE); -#ifdef HAVE_IPV6 vty_out (vty, "Nexthop global: %s%s", inet_ntop (AF_INET6, &p->nexthop.v6_global, buf1, BUFSIZ), VTY_NEWLINE); @@ -7683,9 +8738,13 @@ bgp_show_peer (struct vty *vty, struct peer *p) vty_out (vty, "BGP connection: %s%s", p->shared_network ? "shared network" : "non shared network", VTY_NEWLINE); -#endif /* HAVE_IPV6 */ } + /* TCP metrics. */ + if (p->status == Established && p->rtt) + vty_out (vty, "Estimated round trip time: %d ms%s", + p->rtt, VTY_NEWLINE); + /* Timer information. */ if (p->t_start) vty_out (vty, "Next start timer due in %ld seconds%s", @@ -7778,8 +8837,7 @@ bgp_show_neighbor_vty (struct vty *vty, const char *name, return CMD_SUCCESS; } -/* "show ip bgp neighbors" commands. */ -DEFUN (show_ip_bgp_neighbors, +/* "show ip bgp neighbors" commands. */DEFUN (show_ip_bgp_neighbors, show_ip_bgp_neighbors_cmd, "show ip bgp neighbors", SHOW_STR @@ -7822,13 +8880,6 @@ ALIAS (show_ip_bgp_neighbors, "VPN Route Distinguisher\n" "Detailed information on TCP and BGP neighbor connections\n") -ALIAS (show_ip_bgp_neighbors, - show_bgp_neighbors_cmd, - "show bgp neighbors", - SHOW_STR - BGP_STR - "Detailed information on TCP and BGP neighbor connections\n") - ALIAS (show_ip_bgp_neighbors, show_bgp_ipv6_neighbors_cmd, "show bgp ipv6 neighbors", @@ -7885,15 +8936,6 @@ ALIAS (show_ip_bgp_neighbors_peer, "Detailed information on TCP and BGP neighbor connections\n" "Neighbor to display information about\n") -ALIAS (show_ip_bgp_neighbors_peer, - show_bgp_neighbors_peer_cmd, - "show bgp neighbors (A.B.C.D|X:X::X:X)", - SHOW_STR - BGP_STR - "Detailed information on TCP and BGP neighbor connections\n" - "Neighbor to display information about\n" - "Neighbor to display information about\n") - ALIAS (show_ip_bgp_neighbors_peer, show_bgp_ipv6_neighbors_peer_cmd, "show bgp ipv6 neighbors (A.B.C.D|X:X::X:X)", @@ -7917,15 +8959,6 @@ DEFUN (show_ip_bgp_instance_neighbors, return bgp_show_neighbor_vty (vty, argv[0], show_all, NULL); } -ALIAS (show_ip_bgp_instance_neighbors, - show_bgp_instance_neighbors_cmd, - "show bgp view WORD neighbors", - SHOW_STR - BGP_STR - "BGP view\n" - "View name\n" - "Detailed information on TCP and BGP neighbor connections\n") - ALIAS (show_ip_bgp_instance_neighbors, show_bgp_instance_ipv6_neighbors_cmd, "show bgp view WORD ipv6 neighbors", @@ -7951,29 +8984,6 @@ DEFUN (show_ip_bgp_instance_neighbors_peer, return bgp_show_neighbor_vty (vty, argv[0], show_peer, argv[1]); } -ALIAS (show_ip_bgp_instance_neighbors_peer, - show_bgp_instance_neighbors_peer_cmd, - "show bgp view WORD neighbors (A.B.C.D|X:X::X:X)", - SHOW_STR - BGP_STR - "BGP view\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") - -ALIAS (show_ip_bgp_instance_neighbors_peer, - show_bgp_instance_ipv6_neighbors_peer_cmd, - "show bgp view WORD ipv6 neighbors (A.B.C.D|X:X::X:X)", - SHOW_STR - BGP_STR - "BGP view\n" - "View name\n" - "Address family\n" - "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.*/ @@ -8006,7 +9016,82 @@ DEFUN (show_ip_bgp_ipv4_paths, return CMD_SUCCESS; } - + +DEFUN (show_bgp_neighbors, + show_bgp_neighbors_cmd, + "show bgp neighbors", + SHOW_STR + BGP_STR + "Detailed information on TCP and BGP neighbor connections\n") +{ + return bgp_show_neighbor_vty (vty, NULL, show_all, NULL); +} + +DEFUN (show_bgp_neighbors_peer, + show_bgp_neighbors_peer_cmd, + "show bgp neighbors (A.B.C.D|X:X::X:X)", + SHOW_STR + BGP_STR + "Detailed information on TCP and BGP neighbor connections\n" + "Neighbor to display information about\n" + "Neighbor to display information about\n") +{ + return bgp_show_neighbor_vty (vty, NULL, show_peer, argv[argc - 1]); +} + +DEFUN (show_bgp_instance_neighbors, + show_bgp_instance_neighbors_cmd, + "show bgp view WORD neighbors", + SHOW_STR + BGP_STR + "BGP view\n" + "View name\n" + "Detailed information on TCP and BGP neighbor connections\n") +{ + return bgp_show_neighbor_vty (vty, argv[0], show_all, NULL); +} + +DEFUN (show_bgp_instance_neighbors_peer, + show_bgp_instance_neighbors_peer_cmd, + "show bgp view WORD neighbors (A.B.C.D|X:X::X:X)", + SHOW_STR + BGP_STR + "BGP view\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") +{ + return bgp_show_neighbor_vty (vty, argv[0], show_peer, argv[1]); +} + +ALIAS (show_bgp_instance_neighbors_peer, + show_bgp_instance_ipv6_neighbors_peer_cmd, + "show bgp view WORD ipv6 neighbors (A.B.C.D|X:X::X:X)", + SHOW_STR + BGP_STR + "BGP view\n" + "View name\n" + "Address family\n" + "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.*/ +DEFUN (show_bgp_ipv4_paths, + show_bgp_ipv4_paths_cmd, + "show bgp paths", + SHOW_STR + BGP_STR + "Path information\n") +{ + vty_out (vty, "Address Refcnt Path%s", VTY_NEWLINE); + aspath_print_all_vty (vty); + return CMD_SUCCESS; +} + #include "hash.h" static void @@ -8015,7 +9100,7 @@ community_show_all_iterator (struct hash_backet *backet, struct vty *vty) struct community *com; com = (struct community *) backet->data; - vty_out (vty, "[%p] (%ld) %s%s", backet, com->refcnt, + vty_out (vty, "[%p] (%ld) %s%s", (void *)backet, com->refcnt, community_str (com), VTY_NEWLINE); } @@ -8038,18 +9123,47 @@ DEFUN (show_ip_bgp_community_info, return CMD_SUCCESS; } -DEFUN (show_ip_bgp_attr_info, - show_ip_bgp_attr_info_cmd, - "show ip bgp attribute-info", +static void +lcommunity_show_all_iterator (struct hash_backet *backet, struct vty *vty) +{ + struct lcommunity *lcom; + + lcom = (struct lcommunity *) backet->data; + vty_out (vty, "[%p] (%ld) %s%s", (void *)backet, lcom->refcnt, + lcommunity_str (lcom), VTY_NEWLINE); +} + +/* Show BGP's community internal data. */ +DEFUN (show_ip_bgp_lcommunity_info, + show_ip_bgp_lcommunity_info_cmd, + "show ip bgp large-community-info", SHOW_STR IP_STR BGP_STR - "List all bgp attribute information\n") + "List all bgp large-community information\n") { - attr_show_all (vty); + vty_out (vty, "Address Refcnt Large-community%s", VTY_NEWLINE); + + hash_iterate (lcommunity_hash (), + (void (*) (struct hash_backet *, void *)) + lcommunity_show_all_iterator, + vty); + return CMD_SUCCESS; } - + +DEFUN (show_ip_bgp_attr_info, + show_ip_bgp_attr_info_cmd, + "show ip bgp attribute-info", + SHOW_STR + IP_STR + BGP_STR + "List all bgp attribute information\n") +{ + 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) @@ -8082,7 +9196,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 ) @@ -8127,7 +9241,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)) { @@ -8289,7 +9403,6 @@ ALIAS (show_bgp_instance_ipv4_safi_rsclient_summary, "Information about Route Server Clients\n" "Summary of all Route Server Clients\n") -#ifdef HAVE_IPV6 DEFUN (show_bgp_rsclient_summary, show_bgp_rsclient_summary_cmd, "show bgp rsclient summary", @@ -8298,7 +9411,33 @@ DEFUN (show_bgp_rsclient_summary, "Information about Route Server Clients\n" "Summary of all Route Server Clients\n") { - return bgp_show_rsclient_summary_vty (vty, NULL, AFI_IP6, SAFI_UNICAST); + vty_out(vty, "%sIPv4 Unicast Summary:%s", VTY_NEWLINE, VTY_NEWLINE); + vty_out(vty, "---------------------%s", VTY_NEWLINE); + bgp_show_rsclient_summary_vty (vty, NULL, AFI_IP, SAFI_UNICAST); + vty_out(vty, "%sIPv4 Multicast Summary:%s", VTY_NEWLINE, VTY_NEWLINE); + vty_out(vty, "-----------------------%s", VTY_NEWLINE); + bgp_show_rsclient_summary_vty (vty, NULL, AFI_IP, SAFI_MULTICAST); + vty_out(vty, "%sIPv4 VPN Summary:%s", VTY_NEWLINE, VTY_NEWLINE); + vty_out(vty, "-----------------%s", VTY_NEWLINE); + bgp_show_rsclient_summary_vty (vty, NULL, AFI_IP, SAFI_MPLS_VPN); + vty_out(vty, "%sIPv4 Encap Summary:%s", VTY_NEWLINE, VTY_NEWLINE); + vty_out(vty, "-------------------%s", VTY_NEWLINE); + bgp_show_rsclient_summary_vty (vty, NULL, AFI_IP, SAFI_ENCAP); + + vty_out(vty, "%sIPv6 Unicast Summary:%s", VTY_NEWLINE, VTY_NEWLINE); + vty_out(vty, "---------------------%s", VTY_NEWLINE); + bgp_show_rsclient_summary_vty (vty, NULL, AFI_IP6, SAFI_UNICAST); + vty_out(vty, "%sIPv6 Multicast Summary:%s", VTY_NEWLINE, VTY_NEWLINE); + vty_out(vty, "-----------------------%s", VTY_NEWLINE); + bgp_show_rsclient_summary_vty (vty, NULL, AFI_IP6, SAFI_MULTICAST); + vty_out(vty, "%sIPv6 VPN Summary:%s", VTY_NEWLINE, VTY_NEWLINE); + vty_out(vty, "-----------------%s", VTY_NEWLINE); + bgp_show_rsclient_summary_vty (vty, NULL, AFI_IP6, SAFI_MPLS_VPN); + vty_out(vty, "%sIPv6 Encap Summary:%s", VTY_NEWLINE, VTY_NEWLINE); + vty_out(vty, "-------------------%s", VTY_NEWLINE); + bgp_show_rsclient_summary_vty (vty, NULL, AFI_IP6, SAFI_ENCAP); + + return CMD_SUCCESS; } DEFUN (show_bgp_instance_rsclient_summary, @@ -8311,10 +9450,36 @@ DEFUN (show_bgp_instance_rsclient_summary, "Information about Route Server Clients\n" "Summary of all Route Server Clients\n") { - return bgp_show_rsclient_summary_vty (vty, argv[0], AFI_IP6, SAFI_UNICAST); + vty_out(vty, "%sIPv4 Unicast Summary:%s", VTY_NEWLINE, VTY_NEWLINE); + vty_out(vty, "---------------------%s", VTY_NEWLINE); + bgp_show_rsclient_summary_vty (vty, argv[0], AFI_IP, SAFI_UNICAST); + vty_out(vty, "%sIPv4 Multicast Summary:%s", VTY_NEWLINE, VTY_NEWLINE); + vty_out(vty, "-----------------------%s", VTY_NEWLINE); + bgp_show_rsclient_summary_vty (vty, argv[0], AFI_IP, SAFI_MULTICAST); + vty_out(vty, "%sIPv4 VPN Summary:%s", VTY_NEWLINE, VTY_NEWLINE); + vty_out(vty, "-----------------%s", VTY_NEWLINE); + bgp_show_rsclient_summary_vty (vty, argv[0], AFI_IP, SAFI_MPLS_VPN); + vty_out(vty, "%sIPv4 Encap Summary:%s", VTY_NEWLINE, VTY_NEWLINE); + vty_out(vty, "-------------------%s", VTY_NEWLINE); + bgp_show_rsclient_summary_vty (vty, argv[0], AFI_IP, SAFI_ENCAP); + + vty_out(vty, "%sIPv6 Unicast Summary:%s", VTY_NEWLINE, VTY_NEWLINE); + vty_out(vty, "---------------------%s", VTY_NEWLINE); + bgp_show_rsclient_summary_vty (vty, argv[0], AFI_IP6, SAFI_UNICAST); + vty_out(vty, "%sIPv6 Multicast Summary:%s", VTY_NEWLINE, VTY_NEWLINE); + vty_out(vty, "-----------------------%s", VTY_NEWLINE); + bgp_show_rsclient_summary_vty (vty, argv[0], AFI_IP6, SAFI_MULTICAST); + vty_out(vty, "%sIPv6 VPN Summary:%s", VTY_NEWLINE, VTY_NEWLINE); + vty_out(vty, "-----------------%s", VTY_NEWLINE); + bgp_show_rsclient_summary_vty (vty, argv[0], AFI_IP6, SAFI_MPLS_VPN); + vty_out(vty, "%sIPv6 Encap Summary:%s", VTY_NEWLINE, VTY_NEWLINE); + vty_out(vty, "-------------------%s", VTY_NEWLINE); + bgp_show_rsclient_summary_vty (vty, argv[0], AFI_IP6, SAFI_ENCAP); + + return CMD_SUCCESS; } -ALIAS (show_bgp_rsclient_summary, +DEFUN (show_bgp_ipv6_rsclient_summary, show_bgp_ipv6_rsclient_summary_cmd, "show bgp ipv6 rsclient summary", SHOW_STR @@ -8322,8 +9487,24 @@ ALIAS (show_bgp_rsclient_summary, "Address family\n" "Information about Route Server Clients\n" "Summary of all Route Server Clients\n") +{ + vty_out(vty, "%sIPv6 Unicast Summary:%s", VTY_NEWLINE, VTY_NEWLINE); + vty_out(vty, "---------------------%s", VTY_NEWLINE); + bgp_show_rsclient_summary_vty (vty, NULL, AFI_IP6, SAFI_UNICAST); + vty_out(vty, "%sIPv6 Multicast Summary:%s", VTY_NEWLINE, VTY_NEWLINE); + vty_out(vty, "-----------------------%s", VTY_NEWLINE); + bgp_show_rsclient_summary_vty (vty, NULL, AFI_IP6, SAFI_MULTICAST); + vty_out(vty, "%sIPv6 VPN Summary:%s", VTY_NEWLINE, VTY_NEWLINE); + vty_out(vty, "-----------------%s", VTY_NEWLINE); + bgp_show_rsclient_summary_vty (vty, NULL, AFI_IP6, SAFI_MPLS_VPN); + vty_out(vty, "%sIPv6 Encap Summary:%s", VTY_NEWLINE, VTY_NEWLINE); + vty_out(vty, "-------------------%s", VTY_NEWLINE); + bgp_show_rsclient_summary_vty (vty, NULL, AFI_IP6, SAFI_ENCAP); + + return CMD_SUCCESS; +} -ALIAS (show_bgp_instance_rsclient_summary, +DEFUN (show_bgp_instance_ipv6_rsclient_summary, show_bgp_instance_ipv6_rsclient_summary_cmd, "show bgp view WORD ipv6 rsclient summary", SHOW_STR @@ -8333,6 +9514,22 @@ ALIAS (show_bgp_instance_rsclient_summary, "Address family\n" "Information about Route Server Clients\n" "Summary of all Route Server Clients\n") +{ + vty_out(vty, "%sIPv6 Unicast Summary:%s", VTY_NEWLINE, VTY_NEWLINE); + vty_out(vty, "---------------------%s", VTY_NEWLINE); + bgp_show_rsclient_summary_vty (vty, argv[0], AFI_IP6, SAFI_UNICAST); + vty_out(vty, "%sIPv6 Multicast Summary:%s", VTY_NEWLINE, VTY_NEWLINE); + vty_out(vty, "-----------------------%s", VTY_NEWLINE); + bgp_show_rsclient_summary_vty (vty, argv[0], AFI_IP6, SAFI_MULTICAST); + vty_out(vty, "%sIPv6 VPN Summary:%s", VTY_NEWLINE, VTY_NEWLINE); + vty_out(vty, "-----------------%s", VTY_NEWLINE); + bgp_show_rsclient_summary_vty (vty, argv[0], AFI_IP6, SAFI_MPLS_VPN); + vty_out(vty, "%sIPv6 Encap Summary:%s", VTY_NEWLINE, VTY_NEWLINE); + vty_out(vty, "-------------------%s", VTY_NEWLINE); + bgp_show_rsclient_summary_vty (vty, argv[0], AFI_IP6, SAFI_ENCAP); + + return CMD_SUCCESS; +} DEFUN (show_bgp_instance_ipv6_safi_rsclient_summary, show_bgp_instance_ipv6_safi_rsclient_summary_cmd, @@ -8363,14 +9560,12 @@ ALIAS (show_bgp_instance_ipv6_safi_rsclient_summary, "show bgp ipv6 (unicast|multicast) rsclient summary", SHOW_STR BGP_STR - "Address family\n" + IPV6_STR "Address Family modifier\n" "Address Family modifier\n" "Information about Route Server Clients\n" "Summary of all Route Server Clients\n") -#endif /* HAVE IPV6 */ - /* Redistribute VTY commands. */ DEFUN (bgp_redistribute_ipv4, @@ -8505,7 +9700,7 @@ DEFUN (no_bgp_redistribute_ipv4, return bgp_redistribute_unset (vty->index, AFI_IP, type); } -DEFUN (no_bgp_redistribute_ipv4_rmap, +ALIAS (no_bgp_redistribute_ipv4, no_bgp_redistribute_ipv4_rmap_cmd, "no redistribute " QUAGGA_IP_REDIST_STR_BGPD " route-map WORD", NO_STR @@ -8513,21 +9708,8 @@ DEFUN (no_bgp_redistribute_ipv4_rmap, QUAGGA_IP_REDIST_HELP_STR_BGPD "Route map reference\n" "Pointer to route-map entries\n") -{ - int type; - type = proto_redistnum (AFI_IP, argv[0]); - if (type < 0 || type == ZEBRA_ROUTE_BGP) - { - vty_out (vty, "%% Invalid route type%s", VTY_NEWLINE); - return CMD_WARNING; - } - - bgp_redistribute_routemap_unset (vty->index, AFI_IP, type); - return CMD_SUCCESS; -} - -DEFUN (no_bgp_redistribute_ipv4_metric, +ALIAS (no_bgp_redistribute_ipv4, no_bgp_redistribute_ipv4_metric_cmd, "no redistribute " QUAGGA_IP_REDIST_STR_BGPD " metric <0-4294967295>", NO_STR @@ -8535,21 +9717,8 @@ DEFUN (no_bgp_redistribute_ipv4_metric, QUAGGA_IP_REDIST_HELP_STR_BGPD "Metric for redistributed routes\n" "Default metric\n") -{ - int type; - type = proto_redistnum (AFI_IP, argv[0]); - if (type < 0 || type == ZEBRA_ROUTE_BGP) - { - vty_out (vty, "%% Invalid route type%s", VTY_NEWLINE); - return CMD_WARNING; - } - - bgp_redistribute_metric_unset (vty->index, AFI_IP, type); - return CMD_SUCCESS; -} - -DEFUN (no_bgp_redistribute_ipv4_rmap_metric, +ALIAS (no_bgp_redistribute_ipv4, no_bgp_redistribute_ipv4_rmap_metric_cmd, "no redistribute " QUAGGA_IP_REDIST_STR_BGPD " route-map WORD metric <0-4294967295>", NO_STR @@ -8559,22 +9728,8 @@ DEFUN (no_bgp_redistribute_ipv4_rmap_metric, "Pointer to route-map entries\n" "Metric for redistributed routes\n" "Default metric\n") -{ - int type; - - type = proto_redistnum (AFI_IP, argv[0]); - if (type < 0 || type == ZEBRA_ROUTE_BGP) - { - vty_out (vty, "%% Invalid route type%s", VTY_NEWLINE); - return CMD_WARNING; - } - - bgp_redistribute_metric_unset (vty->index, AFI_IP, type); - bgp_redistribute_routemap_unset (vty->index, AFI_IP, type); - return CMD_SUCCESS; -} -ALIAS (no_bgp_redistribute_ipv4_rmap_metric, +ALIAS (no_bgp_redistribute_ipv4, no_bgp_redistribute_ipv4_metric_rmap_cmd, "no redistribute " QUAGGA_IP_REDIST_STR_BGPD " metric <0-4294967295> route-map WORD", NO_STR @@ -8584,8 +9739,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, "redistribute " QUAGGA_IP6_REDIST_STR_BGPD, @@ -8719,7 +9873,7 @@ DEFUN (no_bgp_redistribute_ipv6, return bgp_redistribute_unset (vty->index, AFI_IP6, type); } -DEFUN (no_bgp_redistribute_ipv6_rmap, +ALIAS (no_bgp_redistribute_ipv6, no_bgp_redistribute_ipv6_rmap_cmd, "no redistribute " QUAGGA_IP6_REDIST_STR_BGPD " route-map WORD", NO_STR @@ -8727,21 +9881,8 @@ DEFUN (no_bgp_redistribute_ipv6_rmap, QUAGGA_IP6_REDIST_HELP_STR_BGPD "Route map reference\n" "Pointer to route-map entries\n") -{ - int type; - - type = proto_redistnum (AFI_IP6, argv[0]); - if (type < 0 || type == ZEBRA_ROUTE_BGP) - { - vty_out (vty, "%% Invalid route type%s", VTY_NEWLINE); - return CMD_WARNING; - } - - bgp_redistribute_routemap_unset (vty->index, AFI_IP6, type); - return CMD_SUCCESS; -} -DEFUN (no_bgp_redistribute_ipv6_metric, +ALIAS (no_bgp_redistribute_ipv6, no_bgp_redistribute_ipv6_metric_cmd, "no redistribute " QUAGGA_IP6_REDIST_STR_BGPD " metric <0-4294967295>", NO_STR @@ -8749,21 +9890,8 @@ DEFUN (no_bgp_redistribute_ipv6_metric, QUAGGA_IP6_REDIST_HELP_STR_BGPD "Metric for redistributed routes\n" "Default metric\n") -{ - int type; - type = proto_redistnum (AFI_IP6, argv[0]); - if (type < 0 || type == ZEBRA_ROUTE_BGP) - { - vty_out (vty, "%% Invalid route type%s", VTY_NEWLINE); - return CMD_WARNING; - } - - bgp_redistribute_metric_unset (vty->index, AFI_IP6, type); - return CMD_SUCCESS; -} - -DEFUN (no_bgp_redistribute_ipv6_rmap_metric, +ALIAS (no_bgp_redistribute_ipv6, no_bgp_redistribute_ipv6_rmap_metric_cmd, "no redistribute " QUAGGA_IP6_REDIST_STR_BGPD " route-map WORD metric <0-4294967295>", NO_STR @@ -8773,22 +9901,8 @@ DEFUN (no_bgp_redistribute_ipv6_rmap_metric, "Pointer to route-map entries\n" "Metric for redistributed routes\n" "Default metric\n") -{ - int type; - - type = proto_redistnum (AFI_IP6, argv[0]); - if (type < 0 || type == ZEBRA_ROUTE_BGP) - { - vty_out (vty, "%% Invalid route type%s", VTY_NEWLINE); - return CMD_WARNING; - } - bgp_redistribute_metric_unset (vty->index, AFI_IP6, type); - bgp_redistribute_routemap_unset (vty->index, AFI_IP6, type); - return CMD_SUCCESS; -} - -ALIAS (no_bgp_redistribute_ipv6_rmap_metric, +ALIAS (no_bgp_redistribute_ipv6, no_bgp_redistribute_ipv6_metric_rmap_cmd, "no redistribute " QUAGGA_IP6_REDIST_STR_BGPD " metric <0-4294967295> route-map WORD", NO_STR @@ -8798,8 +9912,7 @@ ALIAS (no_bgp_redistribute_ipv6_rmap_metric, "Default metric\n" "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) @@ -8822,7 +9935,7 @@ bgp_config_write_redistribute (struct vty *vty, struct bgp *bgp, afi_t afi, vty_out (vty, " redistribute %s", zebra_route_string(i)); if (bgp->redist_metric_flag[afi][i]) - vty_out (vty, " metric %d", bgp->redist_metric[afi][i]); + vty_out (vty, " metric %u", bgp->redist_metric[afi][i]); if (bgp->rmap[afi][i].name) vty_out (vty, " route-map %s", bgp->rmap[afi][i].name); @@ -8832,7 +9945,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 = { @@ -8875,7 +9988,28 @@ static struct cmd_node bgp_vpnv4_node = "%s(config-router-af)# ", 1 }; - + +static struct cmd_node bgp_vpnv6_node = +{ + BGP_VPNV6_NODE, + "%s(config-router-af-vpnv6)# ", + 1 +}; + +static struct cmd_node bgp_encap_node = +{ + BGP_ENCAP_NODE, + "%s(config-router-af-encap)# ", + 1 +}; + +static struct cmd_node bgp_encapv6_node = +{ + BGP_ENCAPV6_NODE, + "%s(config-router-af-encapv6)# ", + 1 +}; + static void community_list_vty (void); void @@ -8888,6 +10022,9 @@ bgp_vty_init (void) install_node (&bgp_ipv6_unicast_node, NULL); install_node (&bgp_ipv6_multicast_node, NULL); install_node (&bgp_vpnv4_node, NULL); + install_node (&bgp_vpnv6_node, NULL); + install_node (&bgp_encap_node, NULL); + install_node (&bgp_encapv6_node, NULL); /* Install default VTY commands to new nodes. */ install_default (BGP_NODE); @@ -8896,6 +10033,9 @@ bgp_vty_init (void) install_default (BGP_IPV6_NODE); install_default (BGP_IPV6M_NODE); install_default (BGP_VPNV4_NODE); + install_default (BGP_VPNV6_NODE); + install_default (BGP_ENCAP_NODE); + install_default (BGP_ENCAPV6_NODE); /* "bgp multiple-instance" commands. */ install_element (CONFIG_NODE, &bgp_multiple_instance_cmd); @@ -8937,6 +10077,26 @@ bgp_vty_init (void) install_element (BGP_NODE, &bgp_confederation_peers_cmd); install_element (BGP_NODE, &no_bgp_confederation_peers_cmd); + /* "maximum-paths" commands. */ + install_element (BGP_NODE, &bgp_maxpaths_cmd); + install_element (BGP_NODE, &no_bgp_maxpaths_cmd); + install_element (BGP_NODE, &no_bgp_maxpaths_arg_cmd); + install_element (BGP_IPV4_NODE, &bgp_maxpaths_cmd); + install_element (BGP_IPV4_NODE, &no_bgp_maxpaths_cmd); + install_element (BGP_IPV4_NODE, &no_bgp_maxpaths_arg_cmd); + install_element (BGP_IPV6_NODE, &bgp_maxpaths_cmd); + install_element (BGP_IPV6_NODE, &no_bgp_maxpaths_cmd); + install_element (BGP_IPV6_NODE, &no_bgp_maxpaths_arg_cmd); + install_element (BGP_NODE, &bgp_maxpaths_ibgp_cmd); + install_element (BGP_NODE, &no_bgp_maxpaths_ibgp_cmd); + install_element (BGP_NODE, &no_bgp_maxpaths_ibgp_arg_cmd); + install_element (BGP_IPV4_NODE, &bgp_maxpaths_ibgp_cmd); + install_element (BGP_IPV4_NODE, &no_bgp_maxpaths_ibgp_cmd); + install_element (BGP_IPV4_NODE, &no_bgp_maxpaths_ibgp_arg_cmd); + install_element (BGP_IPV6_NODE, &bgp_maxpaths_ibgp_cmd); + install_element (BGP_IPV6_NODE, &no_bgp_maxpaths_ibgp_cmd); + install_element (BGP_IPV6_NODE, &no_bgp_maxpaths_ibgp_arg_cmd); + /* "timers bgp" commands. */ install_element (BGP_NODE, &bgp_timers_cmd); install_element (BGP_NODE, &no_bgp_timers_cmd); @@ -8960,6 +10120,9 @@ bgp_vty_init (void) install_element (BGP_NODE, &bgp_graceful_restart_stalepath_time_cmd); install_element (BGP_NODE, &no_bgp_graceful_restart_stalepath_time_cmd); install_element (BGP_NODE, &no_bgp_graceful_restart_stalepath_time_val_cmd); + install_element (BGP_NODE, &bgp_graceful_restart_restart_time_cmd); + install_element (BGP_NODE, &no_bgp_graceful_restart_restart_time_cmd); + install_element (BGP_NODE, &no_bgp_graceful_restart_restart_time_val_cmd); /* "bgp fast-external-failover" commands */ install_element (BGP_NODE, &bgp_fast_external_failover_cmd); @@ -8981,6 +10144,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); @@ -9006,6 +10173,10 @@ bgp_vty_init (void) install_element (BGP_NODE, &no_bgp_default_local_preference_cmd); install_element (BGP_NODE, &no_bgp_default_local_preference_val_cmd); + /* bgp ibgp-allow-policy-mods command */ + install_element (BGP_NODE, &bgp_rr_allow_outbound_policy_cmd); + install_element (BGP_NODE, &no_bgp_rr_allow_outbound_policy_cmd); + /* "neighbor remote-as" commands. */ install_element (BGP_NODE, &neighbor_remote_as_cmd); install_element (BGP_NODE, &no_neighbor_cmd); @@ -9019,9 +10190,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); @@ -9034,6 +10207,9 @@ bgp_vty_init (void) install_element (BGP_IPV6_NODE, &neighbor_activate_cmd); install_element (BGP_IPV6M_NODE, &neighbor_activate_cmd); install_element (BGP_VPNV4_NODE, &neighbor_activate_cmd); + install_element (BGP_VPNV6_NODE, &neighbor_activate_cmd); + install_element (BGP_ENCAP_NODE, &neighbor_activate_cmd); + install_element (BGP_ENCAPV6_NODE, &neighbor_activate_cmd); /* "no neighbor activate" commands. */ install_element (BGP_NODE, &no_neighbor_activate_cmd); @@ -9042,6 +10218,9 @@ bgp_vty_init (void) install_element (BGP_IPV6_NODE, &no_neighbor_activate_cmd); install_element (BGP_IPV6M_NODE, &no_neighbor_activate_cmd); install_element (BGP_VPNV4_NODE, &no_neighbor_activate_cmd); + install_element (BGP_VPNV6_NODE, &no_neighbor_activate_cmd); + install_element (BGP_ENCAP_NODE, &no_neighbor_activate_cmd); + install_element (BGP_ENCAPV6_NODE, &no_neighbor_activate_cmd); /* "neighbor peer-group set" commands. */ install_element (BGP_NODE, &neighbor_set_peer_group_cmd); @@ -9050,6 +10229,9 @@ bgp_vty_init (void) install_element (BGP_IPV6_NODE, &neighbor_set_peer_group_cmd); install_element (BGP_IPV6M_NODE, &neighbor_set_peer_group_cmd); install_element (BGP_VPNV4_NODE, &neighbor_set_peer_group_cmd); + install_element (BGP_VPNV6_NODE, &neighbor_set_peer_group_cmd); + install_element (BGP_ENCAP_NODE, &neighbor_set_peer_group_cmd); + install_element (BGP_ENCAPV6_NODE, &neighbor_set_peer_group_cmd); /* "no neighbor peer-group unset" commands. */ install_element (BGP_NODE, &no_neighbor_set_peer_group_cmd); @@ -9058,6 +10240,9 @@ bgp_vty_init (void) install_element (BGP_IPV6_NODE, &no_neighbor_set_peer_group_cmd); install_element (BGP_IPV6M_NODE, &no_neighbor_set_peer_group_cmd); install_element (BGP_VPNV4_NODE, &no_neighbor_set_peer_group_cmd); + install_element (BGP_VPNV6_NODE, &no_neighbor_set_peer_group_cmd); + install_element (BGP_ENCAP_NODE, &no_neighbor_set_peer_group_cmd); + install_element (BGP_ENCAPV6_NODE, &no_neighbor_set_peer_group_cmd); /* "neighbor softreconfiguration inbound" commands.*/ install_element (BGP_NODE, &neighbor_soft_reconfiguration_cmd); @@ -9072,6 +10257,12 @@ bgp_vty_init (void) install_element (BGP_IPV6M_NODE, &no_neighbor_soft_reconfiguration_cmd); install_element (BGP_VPNV4_NODE, &neighbor_soft_reconfiguration_cmd); install_element (BGP_VPNV4_NODE, &no_neighbor_soft_reconfiguration_cmd); + install_element (BGP_VPNV6_NODE, &neighbor_soft_reconfiguration_cmd); + install_element (BGP_VPNV6_NODE, &no_neighbor_soft_reconfiguration_cmd); + install_element (BGP_ENCAP_NODE, &neighbor_soft_reconfiguration_cmd); + install_element (BGP_ENCAP_NODE, &no_neighbor_soft_reconfiguration_cmd); + install_element (BGP_ENCAPV6_NODE, &neighbor_soft_reconfiguration_cmd); + install_element (BGP_ENCAPV6_NODE, &no_neighbor_soft_reconfiguration_cmd); /* "neighbor attribute-unchanged" commands. */ install_element (BGP_NODE, &neighbor_attr_unchanged_cmd); @@ -9207,6 +10398,75 @@ bgp_vty_init (void) install_element (BGP_VPNV4_NODE, &no_neighbor_attr_unchanged9_cmd); install_element (BGP_VPNV4_NODE, &no_neighbor_attr_unchanged10_cmd); + install_element (BGP_VPNV6_NODE, &neighbor_attr_unchanged_cmd); + install_element (BGP_VPNV6_NODE, &neighbor_attr_unchanged1_cmd); + install_element (BGP_VPNV6_NODE, &neighbor_attr_unchanged2_cmd); + install_element (BGP_VPNV6_NODE, &neighbor_attr_unchanged3_cmd); + install_element (BGP_VPNV6_NODE, &neighbor_attr_unchanged4_cmd); + install_element (BGP_VPNV6_NODE, &neighbor_attr_unchanged5_cmd); + install_element (BGP_VPNV6_NODE, &neighbor_attr_unchanged6_cmd); + install_element (BGP_VPNV6_NODE, &neighbor_attr_unchanged7_cmd); + install_element (BGP_VPNV6_NODE, &neighbor_attr_unchanged8_cmd); + install_element (BGP_VPNV6_NODE, &neighbor_attr_unchanged9_cmd); + install_element (BGP_VPNV6_NODE, &neighbor_attr_unchanged10_cmd); + install_element (BGP_VPNV6_NODE, &no_neighbor_attr_unchanged_cmd); + install_element (BGP_VPNV6_NODE, &no_neighbor_attr_unchanged1_cmd); + install_element (BGP_VPNV6_NODE, &no_neighbor_attr_unchanged2_cmd); + install_element (BGP_VPNV6_NODE, &no_neighbor_attr_unchanged3_cmd); + install_element (BGP_VPNV6_NODE, &no_neighbor_attr_unchanged4_cmd); + install_element (BGP_VPNV6_NODE, &no_neighbor_attr_unchanged5_cmd); + install_element (BGP_VPNV6_NODE, &no_neighbor_attr_unchanged6_cmd); + install_element (BGP_VPNV6_NODE, &no_neighbor_attr_unchanged7_cmd); + install_element (BGP_VPNV6_NODE, &no_neighbor_attr_unchanged8_cmd); + install_element (BGP_VPNV6_NODE, &no_neighbor_attr_unchanged9_cmd); + install_element (BGP_VPNV6_NODE, &no_neighbor_attr_unchanged10_cmd); + + install_element (BGP_ENCAP_NODE, &neighbor_attr_unchanged_cmd); + install_element (BGP_ENCAP_NODE, &neighbor_attr_unchanged1_cmd); + install_element (BGP_ENCAP_NODE, &neighbor_attr_unchanged2_cmd); + install_element (BGP_ENCAP_NODE, &neighbor_attr_unchanged3_cmd); + install_element (BGP_ENCAP_NODE, &neighbor_attr_unchanged4_cmd); + install_element (BGP_ENCAP_NODE, &neighbor_attr_unchanged5_cmd); + install_element (BGP_ENCAP_NODE, &neighbor_attr_unchanged6_cmd); + install_element (BGP_ENCAP_NODE, &neighbor_attr_unchanged7_cmd); + install_element (BGP_ENCAP_NODE, &neighbor_attr_unchanged8_cmd); + install_element (BGP_ENCAP_NODE, &neighbor_attr_unchanged9_cmd); + install_element (BGP_ENCAP_NODE, &neighbor_attr_unchanged10_cmd); + install_element (BGP_ENCAP_NODE, &no_neighbor_attr_unchanged_cmd); + install_element (BGP_ENCAP_NODE, &no_neighbor_attr_unchanged1_cmd); + install_element (BGP_ENCAP_NODE, &no_neighbor_attr_unchanged2_cmd); + install_element (BGP_ENCAP_NODE, &no_neighbor_attr_unchanged3_cmd); + install_element (BGP_ENCAP_NODE, &no_neighbor_attr_unchanged4_cmd); + install_element (BGP_ENCAP_NODE, &no_neighbor_attr_unchanged5_cmd); + install_element (BGP_ENCAP_NODE, &no_neighbor_attr_unchanged6_cmd); + install_element (BGP_ENCAP_NODE, &no_neighbor_attr_unchanged7_cmd); + install_element (BGP_ENCAP_NODE, &no_neighbor_attr_unchanged8_cmd); + install_element (BGP_ENCAP_NODE, &no_neighbor_attr_unchanged9_cmd); + install_element (BGP_ENCAP_NODE, &no_neighbor_attr_unchanged10_cmd); + + install_element (BGP_ENCAPV6_NODE, &neighbor_attr_unchanged_cmd); + install_element (BGP_ENCAPV6_NODE, &neighbor_attr_unchanged1_cmd); + install_element (BGP_ENCAPV6_NODE, &neighbor_attr_unchanged2_cmd); + install_element (BGP_ENCAPV6_NODE, &neighbor_attr_unchanged3_cmd); + install_element (BGP_ENCAPV6_NODE, &neighbor_attr_unchanged4_cmd); + install_element (BGP_ENCAPV6_NODE, &neighbor_attr_unchanged5_cmd); + install_element (BGP_ENCAPV6_NODE, &neighbor_attr_unchanged6_cmd); + install_element (BGP_ENCAPV6_NODE, &neighbor_attr_unchanged7_cmd); + install_element (BGP_ENCAPV6_NODE, &neighbor_attr_unchanged8_cmd); + install_element (BGP_ENCAPV6_NODE, &neighbor_attr_unchanged9_cmd); + install_element (BGP_ENCAPV6_NODE, &neighbor_attr_unchanged10_cmd); + install_element (BGP_ENCAPV6_NODE, &no_neighbor_attr_unchanged_cmd); + install_element (BGP_ENCAPV6_NODE, &no_neighbor_attr_unchanged1_cmd); + install_element (BGP_ENCAPV6_NODE, &no_neighbor_attr_unchanged2_cmd); + install_element (BGP_ENCAPV6_NODE, &no_neighbor_attr_unchanged3_cmd); + install_element (BGP_ENCAPV6_NODE, &no_neighbor_attr_unchanged4_cmd); + install_element (BGP_ENCAPV6_NODE, &no_neighbor_attr_unchanged5_cmd); + install_element (BGP_ENCAPV6_NODE, &no_neighbor_attr_unchanged6_cmd); + install_element (BGP_ENCAPV6_NODE, &no_neighbor_attr_unchanged7_cmd); + install_element (BGP_ENCAPV6_NODE, &no_neighbor_attr_unchanged8_cmd); + install_element (BGP_ENCAPV6_NODE, &no_neighbor_attr_unchanged9_cmd); + install_element (BGP_ENCAPV6_NODE, &no_neighbor_attr_unchanged10_cmd); + /* "nexthop-local unchanged" commands */ install_element (BGP_IPV6_NODE, &neighbor_nexthop_local_unchanged_cmd); install_element (BGP_IPV6_NODE, &no_neighbor_nexthop_local_unchanged_cmd); @@ -9229,6 +10489,12 @@ bgp_vty_init (void) install_element (BGP_IPV6M_NODE, &no_neighbor_nexthop_self_cmd); install_element (BGP_VPNV4_NODE, &neighbor_nexthop_self_cmd); install_element (BGP_VPNV4_NODE, &no_neighbor_nexthop_self_cmd); + install_element (BGP_VPNV6_NODE, &neighbor_nexthop_self_cmd); + install_element (BGP_VPNV6_NODE, &no_neighbor_nexthop_self_cmd); + install_element (BGP_ENCAP_NODE, &neighbor_nexthop_self_cmd); + install_element (BGP_ENCAP_NODE, &no_neighbor_nexthop_self_cmd); + install_element (BGP_ENCAPV6_NODE, &neighbor_nexthop_self_cmd); + install_element (BGP_ENCAPV6_NODE, &no_neighbor_nexthop_self_cmd); /* "neighbor remove-private-AS" commands. */ install_element (BGP_NODE, &neighbor_remove_private_as_cmd); @@ -9243,6 +10509,12 @@ bgp_vty_init (void) install_element (BGP_IPV6M_NODE, &no_neighbor_remove_private_as_cmd); install_element (BGP_VPNV4_NODE, &neighbor_remove_private_as_cmd); install_element (BGP_VPNV4_NODE, &no_neighbor_remove_private_as_cmd); + install_element (BGP_VPNV6_NODE, &neighbor_remove_private_as_cmd); + install_element (BGP_VPNV6_NODE, &no_neighbor_remove_private_as_cmd); + install_element (BGP_ENCAP_NODE, &neighbor_remove_private_as_cmd); + install_element (BGP_ENCAP_NODE, &no_neighbor_remove_private_as_cmd); + install_element (BGP_ENCAPV6_NODE, &neighbor_remove_private_as_cmd); + install_element (BGP_ENCAPV6_NODE, &no_neighbor_remove_private_as_cmd); /* "neighbor send-community" commands.*/ install_element (BGP_NODE, &neighbor_send_community_cmd); @@ -9269,6 +10541,18 @@ bgp_vty_init (void) install_element (BGP_VPNV4_NODE, &neighbor_send_community_type_cmd); install_element (BGP_VPNV4_NODE, &no_neighbor_send_community_cmd); install_element (BGP_VPNV4_NODE, &no_neighbor_send_community_type_cmd); + install_element (BGP_VPNV6_NODE, &neighbor_send_community_cmd); + install_element (BGP_VPNV6_NODE, &neighbor_send_community_type_cmd); + install_element (BGP_VPNV6_NODE, &no_neighbor_send_community_cmd); + install_element (BGP_VPNV6_NODE, &no_neighbor_send_community_type_cmd); + install_element (BGP_ENCAP_NODE, &neighbor_send_community_cmd); + install_element (BGP_ENCAP_NODE, &neighbor_send_community_type_cmd); + install_element (BGP_ENCAP_NODE, &no_neighbor_send_community_cmd); + install_element (BGP_ENCAP_NODE, &no_neighbor_send_community_type_cmd); + install_element (BGP_ENCAPV6_NODE, &neighbor_send_community_cmd); + install_element (BGP_ENCAPV6_NODE, &neighbor_send_community_type_cmd); + install_element (BGP_ENCAPV6_NODE, &no_neighbor_send_community_cmd); + install_element (BGP_ENCAPV6_NODE, &no_neighbor_send_community_type_cmd); /* "neighbor route-reflector" commands.*/ install_element (BGP_NODE, &neighbor_route_reflector_client_cmd); @@ -9283,6 +10567,12 @@ bgp_vty_init (void) install_element (BGP_IPV6M_NODE, &no_neighbor_route_reflector_client_cmd); install_element (BGP_VPNV4_NODE, &neighbor_route_reflector_client_cmd); install_element (BGP_VPNV4_NODE, &no_neighbor_route_reflector_client_cmd); + install_element (BGP_VPNV6_NODE, &neighbor_route_reflector_client_cmd); + install_element (BGP_VPNV6_NODE, &no_neighbor_route_reflector_client_cmd); + install_element (BGP_ENCAP_NODE, &neighbor_route_reflector_client_cmd); + install_element (BGP_ENCAP_NODE, &no_neighbor_route_reflector_client_cmd); + install_element (BGP_ENCAPV6_NODE, &neighbor_route_reflector_client_cmd); + install_element (BGP_ENCAPV6_NODE, &no_neighbor_route_reflector_client_cmd); /* "neighbor route-server" commands.*/ install_element (BGP_NODE, &neighbor_route_server_client_cmd); @@ -9297,6 +10587,12 @@ bgp_vty_init (void) install_element (BGP_IPV6M_NODE, &no_neighbor_route_server_client_cmd); install_element (BGP_VPNV4_NODE, &neighbor_route_server_client_cmd); install_element (BGP_VPNV4_NODE, &no_neighbor_route_server_client_cmd); + install_element (BGP_VPNV6_NODE, &neighbor_route_server_client_cmd); + install_element (BGP_VPNV6_NODE, &no_neighbor_route_server_client_cmd); + install_element (BGP_ENCAP_NODE, &neighbor_route_server_client_cmd); + install_element (BGP_ENCAP_NODE, &no_neighbor_route_server_client_cmd); + install_element (BGP_ENCAPV6_NODE, &neighbor_route_server_client_cmd); + install_element (BGP_ENCAPV6_NODE, &no_neighbor_route_server_client_cmd); /* "neighbor passive" commands. */ install_element (BGP_NODE, &neighbor_passive_cmd); @@ -9425,6 +10721,12 @@ bgp_vty_init (void) install_element (BGP_IPV6M_NODE, &no_neighbor_distribute_list_cmd); install_element (BGP_VPNV4_NODE, &neighbor_distribute_list_cmd); install_element (BGP_VPNV4_NODE, &no_neighbor_distribute_list_cmd); + install_element (BGP_VPNV6_NODE, &neighbor_distribute_list_cmd); + install_element (BGP_VPNV6_NODE, &no_neighbor_distribute_list_cmd); + install_element (BGP_ENCAP_NODE, &neighbor_distribute_list_cmd); + install_element (BGP_ENCAP_NODE, &no_neighbor_distribute_list_cmd); + install_element (BGP_ENCAPV6_NODE, &neighbor_distribute_list_cmd); + install_element (BGP_ENCAPV6_NODE, &no_neighbor_distribute_list_cmd); /* "neighbor prefix-list" commands. */ install_element (BGP_NODE, &neighbor_prefix_list_cmd); @@ -9439,6 +10741,12 @@ bgp_vty_init (void) install_element (BGP_IPV6M_NODE, &no_neighbor_prefix_list_cmd); install_element (BGP_VPNV4_NODE, &neighbor_prefix_list_cmd); install_element (BGP_VPNV4_NODE, &no_neighbor_prefix_list_cmd); + install_element (BGP_VPNV6_NODE, &neighbor_prefix_list_cmd); + install_element (BGP_VPNV6_NODE, &no_neighbor_prefix_list_cmd); + install_element (BGP_ENCAP_NODE, &neighbor_prefix_list_cmd); + install_element (BGP_ENCAP_NODE, &no_neighbor_prefix_list_cmd); + install_element (BGP_ENCAPV6_NODE, &neighbor_prefix_list_cmd); + install_element (BGP_ENCAPV6_NODE, &no_neighbor_prefix_list_cmd); /* "neighbor filter-list" commands. */ install_element (BGP_NODE, &neighbor_filter_list_cmd); @@ -9453,6 +10761,12 @@ bgp_vty_init (void) install_element (BGP_IPV6M_NODE, &no_neighbor_filter_list_cmd); install_element (BGP_VPNV4_NODE, &neighbor_filter_list_cmd); install_element (BGP_VPNV4_NODE, &no_neighbor_filter_list_cmd); + install_element (BGP_VPNV6_NODE, &neighbor_filter_list_cmd); + install_element (BGP_VPNV6_NODE, &no_neighbor_filter_list_cmd); + install_element (BGP_ENCAP_NODE, &neighbor_filter_list_cmd); + install_element (BGP_ENCAP_NODE, &no_neighbor_filter_list_cmd); + install_element (BGP_ENCAPV6_NODE, &neighbor_filter_list_cmd); + install_element (BGP_ENCAPV6_NODE, &no_neighbor_filter_list_cmd); /* "neighbor route-map" commands. */ install_element (BGP_NODE, &neighbor_route_map_cmd); @@ -9467,6 +10781,12 @@ bgp_vty_init (void) install_element (BGP_IPV6M_NODE, &no_neighbor_route_map_cmd); install_element (BGP_VPNV4_NODE, &neighbor_route_map_cmd); install_element (BGP_VPNV4_NODE, &no_neighbor_route_map_cmd); + install_element (BGP_VPNV6_NODE, &neighbor_route_map_cmd); + install_element (BGP_VPNV6_NODE, &no_neighbor_route_map_cmd); + install_element (BGP_ENCAP_NODE, &neighbor_route_map_cmd); + install_element (BGP_ENCAP_NODE, &no_neighbor_route_map_cmd); + install_element (BGP_ENCAPV6_NODE, &neighbor_route_map_cmd); + install_element (BGP_ENCAPV6_NODE, &no_neighbor_route_map_cmd); /* "neighbor unsuppress-map" commands. */ install_element (BGP_NODE, &neighbor_unsuppress_map_cmd); @@ -9480,7 +10800,13 @@ bgp_vty_init (void) install_element (BGP_IPV6M_NODE, &neighbor_unsuppress_map_cmd); install_element (BGP_IPV6M_NODE, &no_neighbor_unsuppress_map_cmd); install_element (BGP_VPNV4_NODE, &neighbor_unsuppress_map_cmd); - install_element (BGP_VPNV4_NODE, &no_neighbor_unsuppress_map_cmd); + install_element (BGP_VPNV4_NODE, &no_neighbor_unsuppress_map_cmd); + install_element (BGP_VPNV6_NODE, &neighbor_unsuppress_map_cmd); + install_element (BGP_VPNV6_NODE, &no_neighbor_unsuppress_map_cmd); + install_element (BGP_ENCAP_NODE, &neighbor_unsuppress_map_cmd); + install_element (BGP_ENCAP_NODE, &no_neighbor_unsuppress_map_cmd); + install_element (BGP_ENCAPV6_NODE, &neighbor_unsuppress_map_cmd); + install_element (BGP_ENCAPV6_NODE, &no_neighbor_unsuppress_map_cmd); /* "neighbor maximum-prefix" commands. */ install_element (BGP_NODE, &neighbor_maximum_prefix_cmd); @@ -9562,6 +10888,48 @@ bgp_vty_init (void) install_element (BGP_VPNV4_NODE, &no_neighbor_maximum_prefix_restart_cmd); install_element (BGP_VPNV4_NODE, &no_neighbor_maximum_prefix_threshold_restart_cmd); + install_element (BGP_VPNV6_NODE, &neighbor_maximum_prefix_cmd); + install_element (BGP_VPNV6_NODE, &neighbor_maximum_prefix_threshold_cmd); + install_element (BGP_VPNV6_NODE, &neighbor_maximum_prefix_warning_cmd); + install_element (BGP_VPNV6_NODE, &neighbor_maximum_prefix_threshold_warning_cmd); + install_element (BGP_VPNV6_NODE, &neighbor_maximum_prefix_restart_cmd); + install_element (BGP_VPNV6_NODE, &neighbor_maximum_prefix_threshold_restart_cmd); + install_element (BGP_VPNV6_NODE, &no_neighbor_maximum_prefix_cmd); + install_element (BGP_VPNV6_NODE, &no_neighbor_maximum_prefix_val_cmd); + install_element (BGP_VPNV6_NODE, &no_neighbor_maximum_prefix_threshold_cmd); + install_element (BGP_VPNV6_NODE, &no_neighbor_maximum_prefix_warning_cmd); + install_element (BGP_VPNV6_NODE, &no_neighbor_maximum_prefix_threshold_warning_cmd); + install_element (BGP_VPNV6_NODE, &no_neighbor_maximum_prefix_restart_cmd); + install_element (BGP_VPNV6_NODE, &no_neighbor_maximum_prefix_threshold_restart_cmd); + + install_element (BGP_ENCAP_NODE, &neighbor_maximum_prefix_cmd); + install_element (BGP_ENCAP_NODE, &neighbor_maximum_prefix_threshold_cmd); + install_element (BGP_ENCAP_NODE, &neighbor_maximum_prefix_warning_cmd); + install_element (BGP_ENCAP_NODE, &neighbor_maximum_prefix_threshold_warning_cmd); + install_element (BGP_ENCAP_NODE, &neighbor_maximum_prefix_restart_cmd); + install_element (BGP_ENCAP_NODE, &neighbor_maximum_prefix_threshold_restart_cmd); + install_element (BGP_ENCAP_NODE, &no_neighbor_maximum_prefix_cmd); + install_element (BGP_ENCAP_NODE, &no_neighbor_maximum_prefix_val_cmd); + install_element (BGP_ENCAP_NODE, &no_neighbor_maximum_prefix_threshold_cmd); + install_element (BGP_ENCAP_NODE, &no_neighbor_maximum_prefix_warning_cmd); + install_element (BGP_ENCAP_NODE, &no_neighbor_maximum_prefix_threshold_warning_cmd); + install_element (BGP_ENCAP_NODE, &no_neighbor_maximum_prefix_restart_cmd); + install_element (BGP_ENCAP_NODE, &no_neighbor_maximum_prefix_threshold_restart_cmd); + + install_element (BGP_ENCAPV6_NODE, &neighbor_maximum_prefix_cmd); + install_element (BGP_ENCAPV6_NODE, &neighbor_maximum_prefix_threshold_cmd); + install_element (BGP_ENCAPV6_NODE, &neighbor_maximum_prefix_warning_cmd); + install_element (BGP_ENCAPV6_NODE, &neighbor_maximum_prefix_threshold_warning_cmd); + install_element (BGP_ENCAPV6_NODE, &neighbor_maximum_prefix_restart_cmd); + install_element (BGP_ENCAPV6_NODE, &neighbor_maximum_prefix_threshold_restart_cmd); + install_element (BGP_ENCAPV6_NODE, &no_neighbor_maximum_prefix_cmd); + install_element (BGP_ENCAPV6_NODE, &no_neighbor_maximum_prefix_val_cmd); + install_element (BGP_ENCAPV6_NODE, &no_neighbor_maximum_prefix_threshold_cmd); + install_element (BGP_ENCAPV6_NODE, &no_neighbor_maximum_prefix_warning_cmd); + install_element (BGP_ENCAPV6_NODE, &no_neighbor_maximum_prefix_threshold_warning_cmd); + install_element (BGP_ENCAPV6_NODE, &no_neighbor_maximum_prefix_restart_cmd); + install_element (BGP_ENCAPV6_NODE, &no_neighbor_maximum_prefix_threshold_restart_cmd); + /* "neighbor allowas-in" */ install_element (BGP_NODE, &neighbor_allowas_in_cmd); install_element (BGP_NODE, &neighbor_allowas_in_arg_cmd); @@ -9581,23 +10949,40 @@ bgp_vty_init (void) install_element (BGP_VPNV4_NODE, &neighbor_allowas_in_cmd); install_element (BGP_VPNV4_NODE, &neighbor_allowas_in_arg_cmd); install_element (BGP_VPNV4_NODE, &no_neighbor_allowas_in_cmd); + install_element (BGP_VPNV6_NODE, &neighbor_allowas_in_cmd); + install_element (BGP_VPNV6_NODE, &neighbor_allowas_in_arg_cmd); + install_element (BGP_VPNV6_NODE, &no_neighbor_allowas_in_cmd); + install_element (BGP_ENCAP_NODE, &neighbor_allowas_in_cmd); + install_element (BGP_ENCAP_NODE, &neighbor_allowas_in_arg_cmd); + install_element (BGP_ENCAP_NODE, &no_neighbor_allowas_in_cmd); + install_element (BGP_ENCAPV6_NODE, &neighbor_allowas_in_cmd); + install_element (BGP_ENCAPV6_NODE, &neighbor_allowas_in_arg_cmd); + install_element (BGP_ENCAPV6_NODE, &no_neighbor_allowas_in_cmd); /* address-family commands. */ install_element (BGP_NODE, &address_family_ipv4_cmd); install_element (BGP_NODE, &address_family_ipv4_safi_cmd); -#ifdef HAVE_IPV6 install_element (BGP_NODE, &address_family_ipv6_cmd); install_element (BGP_NODE, &address_family_ipv6_safi_cmd); -#endif /* HAVE_IPV6 */ install_element (BGP_NODE, &address_family_vpnv4_cmd); install_element (BGP_NODE, &address_family_vpnv4_unicast_cmd); + install_element (BGP_NODE, &address_family_vpnv6_cmd); + install_element (BGP_NODE, &address_family_vpnv6_unicast_cmd); + + install_element (BGP_NODE, &address_family_encap_cmd); + install_element (BGP_NODE, &address_family_encapv4_cmd); + install_element (BGP_NODE, &address_family_encapv6_cmd); + /* "exit-address-family" command. */ install_element (BGP_IPV4_NODE, &exit_address_family_cmd); install_element (BGP_IPV4M_NODE, &exit_address_family_cmd); install_element (BGP_IPV6_NODE, &exit_address_family_cmd); install_element (BGP_IPV6M_NODE, &exit_address_family_cmd); install_element (BGP_VPNV4_NODE, &exit_address_family_cmd); + install_element (BGP_VPNV6_NODE, &exit_address_family_cmd); + install_element (BGP_ENCAP_NODE, &exit_address_family_cmd); + install_element (BGP_ENCAPV6_NODE, &exit_address_family_cmd); /* "clear ip bgp commands" */ install_element (ENABLE_NODE, &clear_ip_bgp_all_cmd); @@ -9606,7 +10991,6 @@ bgp_vty_init (void) install_element (ENABLE_NODE, &clear_ip_bgp_peer_cmd); install_element (ENABLE_NODE, &clear_ip_bgp_peer_group_cmd); install_element (ENABLE_NODE, &clear_ip_bgp_external_cmd); -#ifdef HAVE_IPV6 install_element (ENABLE_NODE, &clear_bgp_all_cmd); install_element (ENABLE_NODE, &clear_bgp_instance_all_cmd); install_element (ENABLE_NODE, &clear_bgp_ipv6_all_cmd); @@ -9618,7 +11002,6 @@ bgp_vty_init (void) install_element (ENABLE_NODE, &clear_bgp_ipv6_external_cmd); install_element (ENABLE_NODE, &clear_bgp_as_cmd); install_element (ENABLE_NODE, &clear_bgp_ipv6_as_cmd); -#endif /* HAVE_IPV6 */ /* "clear ip bgp neighbor soft in" */ install_element (ENABLE_NODE, &clear_ip_bgp_all_soft_in_cmd); @@ -9661,7 +11044,12 @@ bgp_vty_init (void) install_element (ENABLE_NODE, &clear_ip_bgp_peer_vpnv4_in_cmd); install_element (ENABLE_NODE, &clear_ip_bgp_as_vpnv4_soft_in_cmd); install_element (ENABLE_NODE, &clear_ip_bgp_as_vpnv4_in_cmd); -#ifdef HAVE_IPV6 + install_element (ENABLE_NODE, &clear_ip_bgp_all_encap_soft_in_cmd); + install_element (ENABLE_NODE, &clear_ip_bgp_all_encap_in_cmd); + install_element (ENABLE_NODE, &clear_ip_bgp_peer_encap_soft_in_cmd); + install_element (ENABLE_NODE, &clear_ip_bgp_peer_encap_in_cmd); + install_element (ENABLE_NODE, &clear_ip_bgp_as_encap_soft_in_cmd); + install_element (ENABLE_NODE, &clear_ip_bgp_as_encap_in_cmd); install_element (ENABLE_NODE, &clear_bgp_all_soft_in_cmd); install_element (ENABLE_NODE, &clear_bgp_instance_all_soft_in_cmd); install_element (ENABLE_NODE, &clear_bgp_all_in_cmd); @@ -9693,7 +11081,10 @@ bgp_vty_init (void) install_element (ENABLE_NODE, &clear_bgp_ipv6_as_soft_in_cmd); install_element (ENABLE_NODE, &clear_bgp_ipv6_as_in_cmd); install_element (ENABLE_NODE, &clear_bgp_ipv6_as_in_prefix_filter_cmd); -#endif /* HAVE_IPV6 */ + + /* clear ip bgp prefix */ + install_element (ENABLE_NODE, &clear_ip_bgp_prefix_cmd); + install_element (ENABLE_NODE, &clear_bgp_ipv6_safi_prefix_cmd); /* "clear ip bgp neighbor soft out" */ install_element (ENABLE_NODE, &clear_ip_bgp_all_soft_out_cmd); @@ -9724,7 +11115,12 @@ bgp_vty_init (void) install_element (ENABLE_NODE, &clear_ip_bgp_peer_vpnv4_out_cmd); install_element (ENABLE_NODE, &clear_ip_bgp_as_vpnv4_soft_out_cmd); install_element (ENABLE_NODE, &clear_ip_bgp_as_vpnv4_out_cmd); -#ifdef HAVE_IPV6 + install_element (ENABLE_NODE, &clear_ip_bgp_all_encap_soft_out_cmd); + install_element (ENABLE_NODE, &clear_ip_bgp_all_encap_out_cmd); + install_element (ENABLE_NODE, &clear_ip_bgp_peer_encap_soft_out_cmd); + install_element (ENABLE_NODE, &clear_ip_bgp_peer_encap_out_cmd); + install_element (ENABLE_NODE, &clear_ip_bgp_as_encap_soft_out_cmd); + install_element (ENABLE_NODE, &clear_ip_bgp_as_encap_out_cmd); install_element (ENABLE_NODE, &clear_bgp_all_soft_out_cmd); install_element (ENABLE_NODE, &clear_bgp_instance_all_soft_out_cmd); install_element (ENABLE_NODE, &clear_bgp_all_out_cmd); @@ -9746,7 +11142,6 @@ bgp_vty_init (void) install_element (ENABLE_NODE, &clear_bgp_ipv6_external_out_cmd); install_element (ENABLE_NODE, &clear_bgp_ipv6_as_soft_out_cmd); install_element (ENABLE_NODE, &clear_bgp_ipv6_as_out_cmd); -#endif /* HAVE_IPV6 */ /* "clear ip bgp neighbor soft" */ install_element (ENABLE_NODE, &clear_ip_bgp_all_soft_cmd); @@ -9764,7 +11159,9 @@ bgp_vty_init (void) install_element (ENABLE_NODE, &clear_ip_bgp_all_vpnv4_soft_cmd); install_element (ENABLE_NODE, &clear_ip_bgp_peer_vpnv4_soft_cmd); install_element (ENABLE_NODE, &clear_ip_bgp_as_vpnv4_soft_cmd); -#ifdef HAVE_IPV6 + install_element (ENABLE_NODE, &clear_ip_bgp_all_encap_soft_cmd); + install_element (ENABLE_NODE, &clear_ip_bgp_peer_encap_soft_cmd); + install_element (ENABLE_NODE, &clear_ip_bgp_as_encap_soft_cmd); install_element (ENABLE_NODE, &clear_bgp_all_soft_cmd); install_element (ENABLE_NODE, &clear_bgp_instance_all_soft_cmd); install_element (ENABLE_NODE, &clear_bgp_peer_soft_cmd); @@ -9776,14 +11173,12 @@ bgp_vty_init (void) install_element (ENABLE_NODE, &clear_bgp_ipv6_peer_group_soft_cmd); install_element (ENABLE_NODE, &clear_bgp_ipv6_external_soft_cmd); install_element (ENABLE_NODE, &clear_bgp_ipv6_as_soft_cmd); -#endif /* HAVE_IPV6 */ /* "clear ip bgp neighbor rsclient" */ install_element (ENABLE_NODE, &clear_ip_bgp_all_rsclient_cmd); install_element (ENABLE_NODE, &clear_ip_bgp_instance_all_rsclient_cmd); install_element (ENABLE_NODE, &clear_ip_bgp_peer_rsclient_cmd); install_element (ENABLE_NODE, &clear_ip_bgp_instance_peer_rsclient_cmd); -#ifdef HAVE_IPV6 install_element (ENABLE_NODE, &clear_bgp_all_rsclient_cmd); install_element (ENABLE_NODE, &clear_bgp_instance_all_rsclient_cmd); install_element (ENABLE_NODE, &clear_bgp_ipv6_all_rsclient_cmd); @@ -9792,168 +11187,85 @@ bgp_vty_init (void) install_element (ENABLE_NODE, &clear_bgp_instance_peer_rsclient_cmd); install_element (ENABLE_NODE, &clear_bgp_ipv6_peer_rsclient_cmd); install_element (ENABLE_NODE, &clear_bgp_ipv6_instance_peer_rsclient_cmd); -#endif /* HAVE_IPV6 */ /* "show ip bgp summary" commands. */ - install_element (VIEW_NODE, &show_ip_bgp_summary_cmd); - install_element (VIEW_NODE, &show_ip_bgp_instance_summary_cmd); - install_element (VIEW_NODE, &show_ip_bgp_ipv4_summary_cmd); + install_element (VIEW_NODE, &show_bgp_summary_cmd); + install_element (RESTRICTED_NODE, &show_bgp_summary_cmd); + + install_element (VIEW_NODE, &show_bgp_summary_1w_cmd); + install_element (RESTRICTED_NODE, &show_bgp_summary_1w_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_safi_summary_cmd); - install_element (VIEW_NODE, &show_ip_bgp_instance_ipv4_summary_cmd); install_element (VIEW_NODE, &show_bgp_instance_ipv4_safi_summary_cmd); - install_element (VIEW_NODE, &show_ip_bgp_vpnv4_all_summary_cmd); - install_element (VIEW_NODE, &show_ip_bgp_vpnv4_rd_summary_cmd); -#ifdef HAVE_IPV6 - install_element (VIEW_NODE, &show_bgp_summary_cmd); + install_element (VIEW_NODE, &show_bgp_instance_ipv4_summary_cmd); + + install_element (VIEW_NODE, &show_bgp_ipv4_vpn_summary_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_encap_summary_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_vpn_summary_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_encap_summary_cmd); + install_element (VIEW_NODE, &show_bgp_instance_summary_cmd); - install_element (VIEW_NODE, &show_bgp_ipv6_summary_cmd); install_element (VIEW_NODE, &show_bgp_ipv6_safi_summary_cmd); install_element (VIEW_NODE, &show_bgp_instance_ipv6_summary_cmd); install_element (VIEW_NODE, &show_bgp_instance_ipv6_safi_summary_cmd); -#endif /* HAVE_IPV6 */ - install_element (RESTRICTED_NODE, &show_ip_bgp_summary_cmd); - install_element (RESTRICTED_NODE, &show_ip_bgp_instance_summary_cmd); - install_element (RESTRICTED_NODE, &show_ip_bgp_ipv4_summary_cmd); + install_element (RESTRICTED_NODE, &show_bgp_ipv4_safi_summary_cmd); - install_element (RESTRICTED_NODE, &show_ip_bgp_instance_ipv4_summary_cmd); + install_element (RESTRICTED_NODE, &show_bgp_instance_ipv4_summary_cmd); install_element (RESTRICTED_NODE, &show_bgp_instance_ipv4_safi_summary_cmd); - install_element (RESTRICTED_NODE, &show_ip_bgp_vpnv4_all_summary_cmd); - install_element (RESTRICTED_NODE, &show_ip_bgp_vpnv4_rd_summary_cmd); -#ifdef HAVE_IPV6 - install_element (RESTRICTED_NODE, &show_bgp_summary_cmd); + + install_element (RESTRICTED_NODE, &show_bgp_ipv4_vpn_summary_cmd); + install_element (RESTRICTED_NODE, &show_bgp_ipv4_encap_summary_cmd); + install_element (RESTRICTED_NODE, &show_bgp_ipv6_vpn_summary_cmd); + install_element (RESTRICTED_NODE, &show_bgp_ipv6_encap_summary_cmd); + install_element (RESTRICTED_NODE, &show_bgp_instance_summary_cmd); - install_element (RESTRICTED_NODE, &show_bgp_ipv6_summary_cmd); install_element (RESTRICTED_NODE, &show_bgp_ipv6_safi_summary_cmd); install_element (RESTRICTED_NODE, &show_bgp_instance_ipv6_summary_cmd); install_element (RESTRICTED_NODE, &show_bgp_instance_ipv6_safi_summary_cmd); -#endif /* HAVE_IPV6 */ - install_element (ENABLE_NODE, &show_ip_bgp_summary_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_instance_summary_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_ipv4_summary_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv4_safi_summary_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_instance_ipv4_summary_cmd); - install_element (ENABLE_NODE, &show_bgp_instance_ipv4_safi_summary_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_vpnv4_all_summary_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_vpnv4_rd_summary_cmd); -#ifdef HAVE_IPV6 - install_element (ENABLE_NODE, &show_bgp_summary_cmd); - install_element (ENABLE_NODE, &show_bgp_instance_summary_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv6_summary_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv6_safi_summary_cmd); - install_element (ENABLE_NODE, &show_bgp_instance_ipv6_summary_cmd); - install_element (ENABLE_NODE, &show_bgp_instance_ipv6_safi_summary_cmd); -#endif /* HAVE_IPV6 */ /* "show ip bgp neighbors" commands. */ - install_element (VIEW_NODE, &show_ip_bgp_neighbors_cmd); - install_element (VIEW_NODE, &show_ip_bgp_ipv4_neighbors_cmd); - install_element (VIEW_NODE, &show_ip_bgp_neighbors_peer_cmd); - install_element (VIEW_NODE, &show_ip_bgp_ipv4_neighbors_peer_cmd); - install_element (VIEW_NODE, &show_ip_bgp_vpnv4_all_neighbors_cmd); - install_element (VIEW_NODE, &show_ip_bgp_vpnv4_rd_neighbors_cmd); - install_element (VIEW_NODE, &show_ip_bgp_vpnv4_all_neighbors_peer_cmd); - install_element (VIEW_NODE, &show_ip_bgp_vpnv4_rd_neighbors_peer_cmd); - install_element (VIEW_NODE, &show_ip_bgp_instance_neighbors_cmd); - install_element (VIEW_NODE, &show_ip_bgp_instance_neighbors_peer_cmd); - install_element (RESTRICTED_NODE, &show_ip_bgp_neighbors_peer_cmd); - install_element (RESTRICTED_NODE, &show_ip_bgp_ipv4_neighbors_peer_cmd); - install_element (RESTRICTED_NODE, &show_ip_bgp_vpnv4_all_neighbors_peer_cmd); - install_element (RESTRICTED_NODE, &show_ip_bgp_vpnv4_rd_neighbors_peer_cmd); - install_element (RESTRICTED_NODE, &show_ip_bgp_instance_neighbors_peer_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_neighbors_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_ipv4_neighbors_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_neighbors_peer_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_ipv4_neighbors_peer_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_vpnv4_all_neighbors_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_vpnv4_rd_neighbors_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_vpnv4_all_neighbors_peer_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_vpnv4_rd_neighbors_peer_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_instance_neighbors_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_instance_neighbors_peer_cmd); - -#ifdef HAVE_IPV6 + install_element (VIEW_NODE, &show_bgp_instance_neighbors_cmd); + install_element (VIEW_NODE, &show_bgp_neighbors_cmd); - install_element (VIEW_NODE, &show_bgp_ipv6_neighbors_cmd); install_element (VIEW_NODE, &show_bgp_neighbors_peer_cmd); - install_element (VIEW_NODE, &show_bgp_ipv6_neighbors_peer_cmd); - install_element (VIEW_NODE, &show_bgp_instance_neighbors_cmd); - install_element (VIEW_NODE, &show_bgp_instance_ipv6_neighbors_cmd); install_element (VIEW_NODE, &show_bgp_instance_neighbors_peer_cmd); - install_element (VIEW_NODE, &show_bgp_instance_ipv6_neighbors_peer_cmd); install_element (RESTRICTED_NODE, &show_bgp_neighbors_peer_cmd); - install_element (RESTRICTED_NODE, &show_bgp_ipv6_neighbors_peer_cmd); install_element (RESTRICTED_NODE, &show_bgp_instance_neighbors_peer_cmd); + install_element (VIEW_NODE, &show_bgp_instance_ipv6_neighbors_cmd); + install_element (VIEW_NODE, &show_bgp_instance_ipv6_neighbors_peer_cmd); install_element (RESTRICTED_NODE, &show_bgp_instance_ipv6_neighbors_peer_cmd); - install_element (ENABLE_NODE, &show_bgp_neighbors_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv6_neighbors_cmd); - install_element (ENABLE_NODE, &show_bgp_neighbors_peer_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv6_neighbors_peer_cmd); - install_element (ENABLE_NODE, &show_bgp_instance_neighbors_cmd); - install_element (ENABLE_NODE, &show_bgp_instance_ipv6_neighbors_cmd); - install_element (ENABLE_NODE, &show_bgp_instance_neighbors_peer_cmd); - install_element (ENABLE_NODE, &show_bgp_instance_ipv6_neighbors_peer_cmd); - - /* Old commands. */ - install_element (VIEW_NODE, &show_ipv6_bgp_summary_cmd); - install_element (VIEW_NODE, &show_ipv6_mbgp_summary_cmd); - install_element (ENABLE_NODE, &show_ipv6_bgp_summary_cmd); - install_element (ENABLE_NODE, &show_ipv6_mbgp_summary_cmd); -#endif /* HAVE_IPV6 */ /* "show ip bgp rsclient" commands. */ - install_element (VIEW_NODE, &show_ip_bgp_rsclient_summary_cmd); - install_element (VIEW_NODE, &show_ip_bgp_instance_rsclient_summary_cmd); - install_element (VIEW_NODE, &show_ip_bgp_ipv4_rsclient_summary_cmd); - install_element (VIEW_NODE, &show_ip_bgp_instance_ipv4_rsclient_summary_cmd); install_element (VIEW_NODE, &show_bgp_instance_ipv4_safi_rsclient_summary_cmd); install_element (VIEW_NODE, &show_bgp_ipv4_safi_rsclient_summary_cmd); - install_element (RESTRICTED_NODE, &show_ip_bgp_rsclient_summary_cmd); - install_element (RESTRICTED_NODE, &show_ip_bgp_instance_rsclient_summary_cmd); - install_element (RESTRICTED_NODE, &show_ip_bgp_ipv4_rsclient_summary_cmd); - install_element (RESTRICTED_NODE, &show_ip_bgp_instance_ipv4_rsclient_summary_cmd); install_element (RESTRICTED_NODE, &show_bgp_instance_ipv4_safi_rsclient_summary_cmd); install_element (RESTRICTED_NODE, &show_bgp_ipv4_safi_rsclient_summary_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_rsclient_summary_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_instance_rsclient_summary_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_ipv4_rsclient_summary_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_instance_ipv4_rsclient_summary_cmd); - install_element (ENABLE_NODE, &show_bgp_instance_ipv4_safi_rsclient_summary_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv4_safi_rsclient_summary_cmd); - -#ifdef HAVE_IPV6 + install_element (VIEW_NODE, &show_bgp_rsclient_summary_cmd); - install_element (VIEW_NODE, &show_bgp_ipv6_rsclient_summary_cmd); install_element (VIEW_NODE, &show_bgp_instance_rsclient_summary_cmd); + install_element (RESTRICTED_NODE, &show_bgp_rsclient_summary_cmd); + install_element (RESTRICTED_NODE, &show_bgp_instance_rsclient_summary_cmd); + + install_element (VIEW_NODE, &show_bgp_ipv6_rsclient_summary_cmd); install_element (VIEW_NODE, &show_bgp_instance_ipv6_rsclient_summary_cmd); install_element (VIEW_NODE, &show_bgp_instance_ipv6_safi_rsclient_summary_cmd); install_element (VIEW_NODE, &show_bgp_ipv6_safi_rsclient_summary_cmd); - install_element (RESTRICTED_NODE, &show_bgp_rsclient_summary_cmd); install_element (RESTRICTED_NODE, &show_bgp_ipv6_rsclient_summary_cmd); - install_element (RESTRICTED_NODE, &show_bgp_instance_rsclient_summary_cmd); install_element (RESTRICTED_NODE, &show_bgp_instance_ipv6_rsclient_summary_cmd); install_element (RESTRICTED_NODE, &show_bgp_instance_ipv6_safi_rsclient_summary_cmd); install_element (RESTRICTED_NODE, &show_bgp_ipv6_safi_rsclient_summary_cmd); - install_element (ENABLE_NODE, &show_bgp_rsclient_summary_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv6_rsclient_summary_cmd); - install_element (ENABLE_NODE, &show_bgp_instance_rsclient_summary_cmd); - install_element (ENABLE_NODE, &show_bgp_instance_ipv6_rsclient_summary_cmd); - install_element (ENABLE_NODE, &show_bgp_instance_ipv6_safi_rsclient_summary_cmd); - install_element (ENABLE_NODE, &show_bgp_ipv6_safi_rsclient_summary_cmd); -#endif /* HAVE_IPV6 */ /* "show ip bgp paths" commands. */ - install_element (VIEW_NODE, &show_ip_bgp_paths_cmd); - install_element (VIEW_NODE, &show_ip_bgp_ipv4_paths_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_paths_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_ipv4_paths_cmd); + install_element (VIEW_NODE, &show_bgp_ipv4_paths_cmd); /* "show ip bgp community" commands. */ install_element (VIEW_NODE, &show_ip_bgp_community_info_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_community_info_cmd); + + /* "show ip bgp large-community" commands. */ + install_element (VIEW_NODE, &show_ip_bgp_lcommunity_info_cmd); /* "show ip bgp attribute-info" commands. */ install_element (VIEW_NODE, &show_ip_bgp_attr_info_cmd); - install_element (ENABLE_NODE, &show_ip_bgp_attr_info_cmd); /* "redistribute" commands. */ install_element (BGP_NODE, &bgp_redistribute_ipv4_cmd); @@ -9966,7 +11278,6 @@ bgp_vty_init (void) install_element (BGP_NODE, &bgp_redistribute_ipv4_metric_rmap_cmd); install_element (BGP_NODE, &no_bgp_redistribute_ipv4_rmap_metric_cmd); install_element (BGP_NODE, &no_bgp_redistribute_ipv4_metric_rmap_cmd); -#ifdef HAVE_IPV6 install_element (BGP_IPV6_NODE, &bgp_redistribute_ipv6_cmd); install_element (BGP_IPV6_NODE, &no_bgp_redistribute_ipv6_cmd); install_element (BGP_IPV6_NODE, &bgp_redistribute_ipv6_rmap_cmd); @@ -9977,7 +11288,6 @@ bgp_vty_init (void) install_element (BGP_IPV6_NODE, &bgp_redistribute_ipv6_metric_rmap_cmd); install_element (BGP_IPV6_NODE, &no_bgp_redistribute_ipv6_rmap_metric_cmd); install_element (BGP_IPV6_NODE, &no_bgp_redistribute_ipv6_metric_rmap_cmd); -#endif /* HAVE_IPV6 */ /* ttl_security commands */ install_element (BGP_NODE, &neighbor_ttl_security_cmd); @@ -9986,17 +11296,60 @@ bgp_vty_init (void) /* "show bgp memory" commands. */ install_element (VIEW_NODE, &show_bgp_memory_cmd); install_element (RESTRICTED_NODE, &show_bgp_memory_cmd); - install_element (ENABLE_NODE, &show_bgp_memory_cmd); /* "show bgp views" commands. */ install_element (VIEW_NODE, &show_bgp_views_cmd); install_element (RESTRICTED_NODE, &show_bgp_views_cmd); - install_element (ENABLE_NODE, &show_bgp_views_cmd); + /* non afi/safi forms of commands */ + install_element (VIEW_NODE, &show_ip_bgp_summary_cmd); + install_element (VIEW_NODE, &show_ip_bgp_instance_summary_cmd); + install_element (VIEW_NODE, &show_ip_bgp_ipv4_summary_cmd); + install_element (VIEW_NODE, &show_ip_bgp_instance_ipv4_summary_cmd); + install_element (VIEW_NODE, &show_ip_bgp_vpnv4_all_summary_cmd); + install_element (VIEW_NODE, &show_ip_bgp_vpnv4_rd_summary_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_summary_cmd); + install_element (RESTRICTED_NODE, &show_ip_bgp_summary_cmd); + install_element (RESTRICTED_NODE, &show_ip_bgp_instance_summary_cmd); + install_element (RESTRICTED_NODE, &show_ip_bgp_ipv4_summary_cmd); + install_element (RESTRICTED_NODE, &show_ip_bgp_instance_ipv4_summary_cmd); + install_element (RESTRICTED_NODE, &show_ip_bgp_vpnv4_all_summary_cmd); + install_element (RESTRICTED_NODE, &show_ip_bgp_vpnv4_rd_summary_cmd); + install_element (RESTRICTED_NODE, &show_bgp_ipv6_summary_cmd); + install_element (VIEW_NODE, &show_ip_bgp_neighbors_cmd); + install_element (VIEW_NODE, &show_ip_bgp_ipv4_neighbors_cmd); + install_element (VIEW_NODE, &show_ip_bgp_neighbors_peer_cmd); + install_element (VIEW_NODE, &show_ip_bgp_ipv4_neighbors_peer_cmd); + install_element (VIEW_NODE, &show_ip_bgp_vpnv4_all_neighbors_cmd); + install_element (VIEW_NODE, &show_ip_bgp_vpnv4_rd_neighbors_cmd); + install_element (VIEW_NODE, &show_ip_bgp_vpnv4_all_neighbors_peer_cmd); + install_element (VIEW_NODE, &show_ip_bgp_vpnv4_rd_neighbors_peer_cmd); + install_element (VIEW_NODE, &show_ip_bgp_instance_neighbors_cmd); + install_element (VIEW_NODE, &show_ip_bgp_instance_neighbors_peer_cmd); + install_element (RESTRICTED_NODE, &show_ip_bgp_neighbors_peer_cmd); + install_element (RESTRICTED_NODE, &show_ip_bgp_ipv4_neighbors_peer_cmd); + install_element (RESTRICTED_NODE, &show_ip_bgp_vpnv4_all_neighbors_peer_cmd); + install_element (RESTRICTED_NODE, &show_ip_bgp_vpnv4_rd_neighbors_peer_cmd); + install_element (RESTRICTED_NODE, &show_ip_bgp_instance_neighbors_peer_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_neighbors_cmd); + install_element (VIEW_NODE, &show_bgp_ipv6_neighbors_peer_cmd); + install_element (RESTRICTED_NODE, &show_bgp_ipv6_neighbors_peer_cmd); + install_element (VIEW_NODE, &show_ipv6_bgp_summary_cmd); + install_element (VIEW_NODE, &show_ipv6_mbgp_summary_cmd); + install_element (VIEW_NODE, &show_ip_bgp_rsclient_summary_cmd); + install_element (VIEW_NODE, &show_ip_bgp_instance_rsclient_summary_cmd); + install_element (VIEW_NODE, &show_ip_bgp_ipv4_rsclient_summary_cmd); + install_element (VIEW_NODE, &show_ip_bgp_instance_ipv4_rsclient_summary_cmd); + install_element (RESTRICTED_NODE, &show_ip_bgp_rsclient_summary_cmd); + install_element (RESTRICTED_NODE, &show_ip_bgp_instance_rsclient_summary_cmd); + install_element (RESTRICTED_NODE, &show_ip_bgp_ipv4_rsclient_summary_cmd); + install_element (RESTRICTED_NODE, &show_ip_bgp_instance_ipv4_rsclient_summary_cmd); + install_element (VIEW_NODE, &show_ip_bgp_paths_cmd); + install_element (VIEW_NODE, &show_ip_bgp_ipv4_paths_cmd); /* Community-list. */ community_list_vty (); } - + #include "memory.h" #include "bgp_regex.h" #include "bgp_clist.h" @@ -10395,7 +11748,360 @@ DEFUN (show_ip_community_list_arg, return CMD_SUCCESS; } - + +/* + * Large Community code. + */ +static int +lcommunity_list_set_vty (struct vty *vty, int argc, const char **argv, + int style, int reject_all_digit_name) +{ + int ret; + int direct; + char *str; + + /* Check the list type. */ + if (strncmp (argv[1], "p", 1) == 0) + direct = COMMUNITY_PERMIT; + else if (strncmp (argv[1], "d", 1) == 0) + direct = COMMUNITY_DENY; + else + { + vty_out (vty, "%% Matching condition must be permit or deny%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + /* All digit name check. */ + if (reject_all_digit_name && all_digit (argv[0])) + { + vty_out (vty, "%% Community name cannot have all digits%s", VTY_NEWLINE); + return CMD_WARNING; + } + + /* Concat community string argument. */ + if (argc > 1) + str = argv_concat (argv, argc, 2); + else + str = NULL; + + ret = lcommunity_list_set (bgp_clist, argv[0], str, direct, style); + + /* Free temporary community list string allocated by + argv_concat(). */ + if (str) + XFREE (MTYPE_TMP, str); + + if (ret < 0) + { + community_list_perror (vty, ret); + return CMD_WARNING; + } + return CMD_SUCCESS; +} + +static int +lcommunity_list_unset_vty (struct vty *vty, int argc, const char **argv, + int style) +{ + int ret; + int direct = 0; + char *str = NULL; + + if (argc > 1) + { + /* Check the list direct. */ + if (strncmp (argv[1], "p", 1) == 0) + direct = COMMUNITY_PERMIT; + else if (strncmp (argv[1], "d", 1) == 0) + direct = COMMUNITY_DENY; + else + { + vty_out (vty, "%% Matching condition must be permit or deny%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + /* Concat community string argument. */ + str = argv_concat (argv, argc, 2); + } + + /* Unset community list. */ + ret = lcommunity_list_unset (bgp_clist, argv[0], str, direct, style); + + /* Free temporary community list string allocated by + argv_concat(). */ + if (str) + XFREE (MTYPE_TMP, str); + + if (ret < 0) + { + community_list_perror (vty, ret); + return CMD_WARNING; + } + + return CMD_SUCCESS; +} + +/* "large-community-list" keyword help string. */ +#define LCOMMUNITY_LIST_STR "Add a large community list entry\n" +#define LCOMMUNITY_VAL_STR "large community in 'aa:bb:cc' format\n" + +DEFUN (ip_lcommunity_list_standard, + ip_lcommunity_list_standard_cmd, + "ip large-community-list <1-99> (deny|permit) .AA:BB:CC", + IP_STR + LCOMMUNITY_LIST_STR + "Large Community list number (standard)\n" + "Specify large community to reject\n" + "Specify large community to accept\n" + LCOMMUNITY_VAL_STR) +{ + return lcommunity_list_set_vty (vty, argc, argv, LARGE_COMMUNITY_LIST_STANDARD, 0); +} + +ALIAS (ip_lcommunity_list_standard, + ip_lcommunity_list_standard2_cmd, + "ip large-community-list <1-99> (deny|permit)", + IP_STR + LCOMMUNITY_LIST_STR + "Large Community list number (standard)\n" + "Specify large community to reject\n" + "Specify large community to accept\n") + +DEFUN (ip_lcommunity_list_expanded, + ip_lcommunity_list_expanded_cmd, + "ip large-community-list <100-500> (deny|permit) .LINE", + IP_STR + LCOMMUNITY_LIST_STR + "Large Community list number (expanded)\n" + "Specify large community to reject\n" + "Specify large community to accept\n" + "An ordered list as a regular-expression\n") +{ + return lcommunity_list_set_vty (vty, argc, argv, LARGE_COMMUNITY_LIST_EXPANDED, 0); +} + +DEFUN (ip_lcommunity_list_name_standard, + ip_lcommunity_list_name_standard_cmd, + "ip large-community-list standard WORD (deny|permit) .AA:BB.CC", + IP_STR + LCOMMUNITY_LIST_STR + "Specify standard large-community-list\n" + "Large Community list name\n" + "Specify large community to reject\n" + "Specify large community to accept\n" + LCOMMUNITY_VAL_STR) +{ + return lcommunity_list_set_vty (vty, argc, argv, LARGE_COMMUNITY_LIST_STANDARD, 1); +} + +ALIAS (ip_lcommunity_list_name_standard, + ip_lcommunity_list_name_standard2_cmd, + "ip large-community-list standard WORD (deny|permit)", + IP_STR + LCOMMUNITY_LIST_STR + "Specify standard large-community-list\n" + "Large Community list name\n" + "Specify large community to reject\n" + "Specify large community to accept\n") + +DEFUN (ip_lcommunity_list_name_expanded, + ip_lcommunity_list_name_expanded_cmd, + "ip large-community-list expanded WORD (deny|permit) .LINE", + IP_STR + LCOMMUNITY_LIST_STR + "Specify expanded large-community-list\n" + "Large Community list name\n" + "Specify large community to reject\n" + "Specify large community to accept\n" + "An ordered list as a regular-expression\n") +{ + return lcommunity_list_set_vty (vty, argc, argv, LARGE_COMMUNITY_LIST_EXPANDED, 1); +} + +DEFUN (no_ip_lcommunity_list_standard_all, + no_ip_lcommunity_list_standard_all_cmd, + "no ip large-community-list <1-99>", + NO_STR + IP_STR + LCOMMUNITY_LIST_STR + "Large Community list number (standard)\n") +{ + return lcommunity_list_unset_vty (vty, argc, argv, LARGE_COMMUNITY_LIST_STANDARD); +} + +DEFUN (no_ip_lcommunity_list_expanded_all, + no_ip_lcommunity_list_expanded_all_cmd, + "no ip large-community-list <100-500>", + NO_STR + IP_STR + LCOMMUNITY_LIST_STR + "Large Community list number (expanded)\n") +{ + return lcommunity_list_unset_vty (vty, argc, argv, LARGE_COMMUNITY_LIST_EXPANDED); +} + +DEFUN (no_ip_lcommunity_list_name_standard_all, + no_ip_lcommunity_list_name_standard_all_cmd, + "no ip large-community-list standard WORD", + NO_STR + IP_STR + LCOMMUNITY_LIST_STR + "Specify standard large-community-list\n" + "Large Community list name\n") +{ + return lcommunity_list_unset_vty (vty, argc, argv, LARGE_COMMUNITY_LIST_STANDARD); +} + +DEFUN (no_ip_lcommunity_list_name_expanded_all, + no_ip_lcommunity_list_name_expanded_all_cmd, + "no ip large-community-list expanded WORD", + NO_STR + IP_STR + LCOMMUNITY_LIST_STR + "Specify expanded large-community-list\n" + "Large Community list name\n") +{ + return lcommunity_list_unset_vty (vty, argc, argv, LARGE_COMMUNITY_LIST_EXPANDED); +} + +DEFUN (no_ip_lcommunity_list_standard, + no_ip_lcommunity_list_standard_cmd, + "no ip large-community-list <1-99> (deny|permit) .AA:.AA:NN", + NO_STR + IP_STR + LCOMMUNITY_LIST_STR + "Large Community list number (standard)\n" + "Specify large community to reject\n" + "Specify large community to accept\n" + LCOMMUNITY_VAL_STR) +{ + return lcommunity_list_unset_vty (vty, argc, argv, LARGE_COMMUNITY_LIST_STANDARD); +} + +DEFUN (no_ip_lcommunity_list_expanded, + no_ip_lcommunity_list_expanded_cmd, + "no ip large-community-list <100-500> (deny|permit) .LINE", + NO_STR + IP_STR + LCOMMUNITY_LIST_STR + "Large Community list number (expanded)\n" + "Specify large community to reject\n" + "Specify large community to accept\n" + "An ordered list as a regular-expression\n") +{ + return lcommunity_list_unset_vty (vty, argc, argv, LARGE_COMMUNITY_LIST_EXPANDED); +} + +DEFUN (no_ip_lcommunity_list_name_standard, + no_ip_lcommunity_list_name_standard_cmd, + "no ip large-community-list standard WORD (deny|permit) .AA:.AA:NN", + NO_STR + IP_STR + LCOMMUNITY_LIST_STR + "Specify standard large-community-list\n" + "Large Community list name\n" + "Specify large community to reject\n" + "Specify large community to accept\n" + LCOMMUNITY_VAL_STR) +{ + return lcommunity_list_unset_vty (vty, argc, argv, LARGE_COMMUNITY_LIST_STANDARD); +} + +DEFUN (no_ip_lcommunity_list_name_expanded, + no_ip_lcommunity_list_name_expanded_cmd, + "no ip large-community-list expanded WORD (deny|permit) .LINE", + NO_STR + IP_STR + LCOMMUNITY_LIST_STR + "Specify expanded large-community-list\n" + "Large community list name\n" + "Specify large community to reject\n" + "Specify large community to accept\n" + "An ordered list as a regular-expression\n") +{ + return lcommunity_list_unset_vty (vty, argc, argv, LARGE_COMMUNITY_LIST_EXPANDED); +} + +static void +lcommunity_list_show (struct vty *vty, struct community_list *list) +{ + struct community_entry *entry; + + for (entry = list->head; entry; entry = entry->next) + { + if (entry == list->head) + { + if (all_digit (list->name)) + vty_out (vty, "Large community %s list %s%s", + entry->style == EXTCOMMUNITY_LIST_STANDARD ? + "standard" : "(expanded) access", + list->name, VTY_NEWLINE); + else + vty_out (vty, "Named large community %s list %s%s", + entry->style == EXTCOMMUNITY_LIST_STANDARD ? + "standard" : "expanded", + list->name, VTY_NEWLINE); + } + if (entry->any) + vty_out (vty, " %s%s", + community_direct_str (entry->direct), VTY_NEWLINE); + else + vty_out (vty, " %s %s%s", + community_direct_str (entry->direct), + entry->style == EXTCOMMUNITY_LIST_STANDARD ? + entry->u.ecom->str : entry->config, + VTY_NEWLINE); + } +} + +DEFUN (show_ip_lcommunity_list, + show_ip_lcommunity_list_cmd, + "show ip large-community-list", + SHOW_STR + IP_STR + "List large-community list\n") +{ + struct community_list *list; + struct community_list_master *cm; + + cm = community_list_master_lookup (bgp_clist, LARGE_COMMUNITY_LIST_MASTER); + if (! cm) + return CMD_SUCCESS; + + for (list = cm->num.head; list; list = list->next) + lcommunity_list_show (vty, list); + + for (list = cm->str.head; list; list = list->next) + lcommunity_list_show (vty, list); + + return CMD_SUCCESS; +} + +DEFUN (show_ip_lcommunity_list_arg, + show_ip_lcommunity_list_arg_cmd, + "show ip large-community-list (<1-500>|WORD)", + SHOW_STR + IP_STR + "List large-community list\n" + "large-community-list number\n" + "large-community-list name\n") +{ + struct community_list *list; + + list = community_list_lookup (bgp_clist, argv[0], LARGE_COMMUNITY_LIST_MASTER); + if (! list) + { + vty_out (vty, "%% Can't find extcommunity-list%s", VTY_NEWLINE); + return CMD_WARNING; + } + + lcommunity_list_show (vty, list); + + return CMD_SUCCESS; +} + static int extcommunity_list_set_vty (struct vty *vty, int argc, const char **argv, int style, int reject_all_digit_name) @@ -10745,7 +12451,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) @@ -10818,6 +12524,30 @@ community_list_config_write (struct vty *vty) community_list_config_str (entry), VTY_NEWLINE); write++; } + + + /* lcommunity-list. */ + cm = community_list_master_lookup (bgp_clist, LARGE_COMMUNITY_LIST_MASTER); + + for (list = cm->num.head; list; list = list->next) + for (entry = list->head; entry; entry = entry->next) + { + vty_out (vty, "ip large-community-list %s %s %s%s", + list->name, community_direct_str (entry->direct), + community_list_config_str (entry), VTY_NEWLINE); + write++; + } + for (list = cm->str.head; list; list = list->next) + for (entry = list->head; entry; entry = entry->next) + { + vty_out (vty, "ip large-community-list %s %s %s %s%s", + entry->style == LARGE_COMMUNITY_LIST_STANDARD + ? "standard" : "expanded", + list->name, community_direct_str (entry->direct), + community_list_config_str (entry), VTY_NEWLINE); + write++; + } + return write; } @@ -10850,8 +12580,6 @@ community_list_vty (void) install_element (CONFIG_NODE, &no_ip_community_list_name_expanded_cmd); install_element (VIEW_NODE, &show_ip_community_list_cmd); install_element (VIEW_NODE, &show_ip_community_list_arg_cmd); - install_element (ENABLE_NODE, &show_ip_community_list_cmd); - install_element (ENABLE_NODE, &show_ip_community_list_arg_cmd); /* Extcommunity-list. */ install_element (CONFIG_NODE, &ip_extcommunity_list_standard_cmd); @@ -10870,6 +12598,22 @@ community_list_vty (void) install_element (CONFIG_NODE, &no_ip_extcommunity_list_name_expanded_cmd); install_element (VIEW_NODE, &show_ip_extcommunity_list_cmd); install_element (VIEW_NODE, &show_ip_extcommunity_list_arg_cmd); - install_element (ENABLE_NODE, &show_ip_extcommunity_list_cmd); - install_element (ENABLE_NODE, &show_ip_extcommunity_list_arg_cmd); + + /* Large Community List */ + install_element (CONFIG_NODE, &ip_lcommunity_list_standard_cmd); + install_element (CONFIG_NODE, &ip_lcommunity_list_standard2_cmd); + install_element (CONFIG_NODE, &ip_lcommunity_list_expanded_cmd); + install_element (CONFIG_NODE, &ip_lcommunity_list_name_standard_cmd); + install_element (CONFIG_NODE, &ip_lcommunity_list_name_standard2_cmd); + install_element (CONFIG_NODE, &ip_lcommunity_list_name_expanded_cmd); + install_element (CONFIG_NODE, &no_ip_lcommunity_list_standard_all_cmd); + install_element (CONFIG_NODE, &no_ip_lcommunity_list_expanded_all_cmd); + install_element (CONFIG_NODE, &no_ip_lcommunity_list_name_standard_all_cmd); + install_element (CONFIG_NODE, &no_ip_lcommunity_list_name_expanded_all_cmd); + install_element (CONFIG_NODE, &no_ip_lcommunity_list_standard_cmd); + install_element (CONFIG_NODE, &no_ip_lcommunity_list_expanded_cmd); + install_element (CONFIG_NODE, &no_ip_lcommunity_list_name_standard_cmd); + install_element (CONFIG_NODE, &no_ip_lcommunity_list_name_expanded_cmd); + install_element (VIEW_NODE, &show_ip_lcommunity_list_cmd); + install_element (VIEW_NODE, &show_ip_lcommunity_list_arg_cmd); } diff --git a/bgpd/bgp_vty.h b/bgpd/bgp_vty.h index 2df8aaa5d..7329c5fd9 100644 --- a/bgpd/bgp_vty.h +++ b/bgpd/bgp_vty.h @@ -26,4 +26,10 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA extern void bgp_vty_init (void); extern const char *afi_safi_print (afi_t, safi_t); +extern int +bgp_parse_afi(const char *str, afi_t *afi); + +extern int +bgp_parse_safi(const char *str, safi_t *safi); + #endif /* _QUAGGA_BGP_VTY_H */ diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index 20feba0f4..40ecbce01 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -29,6 +29,7 @@ Boston, MA 02111-1307, USA. */ #include "zclient.h" #include "routemap.h" #include "thread.h" +#include "filter.h" #include "bgpd/bgpd.h" #include "bgpd/bgp_route.h" @@ -37,18 +38,26 @@ Boston, MA 02111-1307, USA. */ #include "bgpd/bgp_zebra.h" #include "bgpd/bgp_fsm.h" #include "bgpd/bgp_debug.h" - +#include "bgpd/bgp_mpath.h" +#include "bgpd/bgp_nexthop.h" +#include "bgpd/bgp_nht.h" + /* All information about zebra. */ struct zclient *zclient = NULL; struct in_addr router_id_zebra; +/* Growable buffer for nexthops sent to zebra */ +struct stream *bgp_nexthop_buf = NULL; +struct stream *bgp_ifindices_buf = NULL; + +int zclient_num_connects; + /* Router-id update message from zebra. */ static int -bgp_router_id_update (int command, struct zclient *zclient, zebra_size_t length) +bgp_router_id_update (int command, struct zclient *zclient, zebra_size_t length, + vrf_id_t vrf_id) { struct prefix router_id; - struct listnode *node, *nnode; - struct bgp *bgp; zebra_router_id_update_read(zclient->ibuf,&router_id); @@ -61,22 +70,27 @@ bgp_router_id_update (int command, struct zclient *zclient, zebra_size_t length) router_id_zebra = router_id.u.prefix4; - for (ALL_LIST_ELEMENTS (bm->bgp, node, nnode, bgp)) - { - if (!bgp->router_id_static.s_addr) - bgp_router_id_set (bgp, &router_id.u.prefix4); - } + bgp_router_id_zebra_bump (); + return 0; +} +/* Nexthop update message from zebra. */ +static int +bgp_read_nexthop_update (int command, struct zclient *zclient, + zebra_size_t length, vrf_id_t vrf_id) +{ + bgp_parse_nexthop_update(); return 0; } /* Inteface addition message from zebra. */ static int -bgp_interface_add (int command, struct zclient *zclient, zebra_size_t length) +bgp_interface_add (int command, struct zclient *zclient, zebra_size_t length, + vrf_id_t vrf_id) { struct interface *ifp; - ifp = zebra_interface_add_read (zclient->ibuf); + ifp = zebra_interface_add_read (zclient->ibuf, vrf_id); if (BGP_DEBUG(zebra, ZEBRA) && ifp) zlog_debug("Zebra rcvd: interface add %s", ifp->name); @@ -86,13 +100,16 @@ bgp_interface_add (int command, struct zclient *zclient, zebra_size_t length) static int bgp_interface_delete (int command, struct zclient *zclient, - zebra_size_t length) + zebra_size_t length, vrf_id_t vrf_id) { struct stream *s; struct interface *ifp; s = zclient->ibuf; - ifp = zebra_interface_state_read (s); + ifp = zebra_interface_state_read (s, vrf_id); + if (! ifp) + return 0; + ifp->ifindex = IFINDEX_INTERNAL; if (BGP_DEBUG(zebra, ZEBRA)) @@ -102,7 +119,8 @@ bgp_interface_delete (int command, struct zclient *zclient, } static int -bgp_interface_up (int command, struct zclient *zclient, zebra_size_t length) +bgp_interface_up (int command, struct zclient *zclient, zebra_size_t length, + vrf_id_t vrf_id) { struct stream *s; struct interface *ifp; @@ -110,7 +128,7 @@ bgp_interface_up (int command, struct zclient *zclient, zebra_size_t length) struct listnode *node, *nnode; s = zclient->ibuf; - ifp = zebra_interface_state_read (s); + ifp = zebra_interface_state_read (s, vrf_id); if (! ifp) return 0; @@ -125,7 +143,8 @@ bgp_interface_up (int command, struct zclient *zclient, zebra_size_t length) } static int -bgp_interface_down (int command, struct zclient *zclient, zebra_size_t length) +bgp_interface_down (int command, struct zclient *zclient, zebra_size_t length, + vrf_id_t vrf_id) { struct stream *s; struct interface *ifp; @@ -133,7 +152,7 @@ bgp_interface_down (int command, struct zclient *zclient, zebra_size_t length) struct listnode *node, *nnode; s = zclient->ibuf; - ifp = zebra_interface_state_read (s); + ifp = zebra_interface_state_read (s, vrf_id); if (! ifp) return 0; @@ -143,12 +162,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)) { @@ -157,16 +175,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 - continue; - - if (ifp == peer_if) - BGP_EVENT_ADD (peer, BGP_Stop); + if (peer->gtsm_hops != 1 && peer_ttl (peer) != 1) + continue; + if (ifp == peer->nexthop.ifp) + BGP_EVENT_ADD (peer, BGP_Stop); } } } @@ -176,11 +188,11 @@ bgp_interface_down (int command, struct zclient *zclient, zebra_size_t length) static int bgp_interface_address_add (int command, struct zclient *zclient, - zebra_size_t length) + zebra_size_t length, vrf_id_t vrf_id) { struct connected *ifc; - ifc = zebra_interface_address_read (command, zclient->ibuf); + ifc = zebra_interface_address_read (command, zclient->ibuf, vrf_id); if (ifc == NULL) return 0; @@ -201,11 +213,11 @@ bgp_interface_address_add (int command, struct zclient *zclient, static int bgp_interface_address_delete (int command, struct zclient *zclient, - zebra_size_t length) + zebra_size_t length, vrf_id_t vrf_id) { struct connected *ifc; - ifc = zebra_interface_address_read (command, zclient->ibuf); + ifc = zebra_interface_address_read (command, zclient->ibuf, vrf_id); if (ifc == NULL) return 0; @@ -228,12 +240,14 @@ bgp_interface_address_delete (int command, struct zclient *zclient, /* Zebra route add and delete treatment. */ static int -zebra_read_ipv4 (int command, struct zclient *zclient, zebra_size_t length) +zebra_read_ipv4 (int command, struct zclient *zclient, zebra_size_t length, + vrf_id_t vrf_id) { struct stream *s; struct zapi_ipv4 api; struct in_addr nexthop; struct prefix_ipv4 p; + unsigned char plength = 0; s = zclient->ibuf; nexthop.s_addr = 0; @@ -246,7 +260,8 @@ zebra_read_ipv4 (int command, struct zclient *zclient, zebra_size_t length) /* IPv4 prefix. */ memset (&p, 0, sizeof (struct prefix_ipv4)); p.family = AF_INET; - p.prefixlen = stream_getc (s); + plength = stream_getc (s); + p.prefixlen = MIN(IPV4_MAX_PREFIXLEN, plength); stream_get (&p.prefix, s, PSIZE (p.prefixlen)); /* Nexthop, ifindex, distance, metric. */ @@ -267,20 +282,26 @@ zebra_read_ipv4 (int command, struct zclient *zclient, zebra_size_t length) else api.metric = 0; + if (CHECK_FLAG (api.message, ZAPI_MESSAGE_TAG)) + api.tag = stream_getl (s); + else + api.tag = 0; + if (command == ZEBRA_IPV4_ROUTE_ADD) { if (BGP_DEBUG(zebra, ZEBRA)) { char buf[2][INET_ADDRSTRLEN]; - zlog_debug("Zebra rcvd: IPv4 route add %s %s/%d nexthop %s metric %u", + zlog_debug("Zebra rcvd: IPv4 route add %s %s/%d nexthop %s metric %u tag %d", 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])), - api.metric); + api.metric, + api.tag); } - bgp_redistribute_add((struct prefix *)&p, &nexthop, NULL, - api.metric, api.type); + bgp_redistribute_add ((struct prefix *)&p, &nexthop, NULL, + api.metric, api.type, api.tag); } else { @@ -288,12 +309,13 @@ zebra_read_ipv4 (int command, struct zclient *zclient, zebra_size_t length) { char buf[2][INET_ADDRSTRLEN]; zlog_debug("Zebra rcvd: IPv4 route delete %s %s/%d " - "nexthop %s metric %u", + "nexthop %s metric %u tag %d", 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])), - api.metric); + api.metric, + api.tag); } bgp_redistribute_delete((struct prefix *)&p, api.type); } @@ -301,15 +323,16 @@ zebra_read_ipv4 (int command, struct zclient *zclient, zebra_size_t length) return 0; } -#ifdef HAVE_IPV6 /* Zebra route add and delete treatment. */ static int -zebra_read_ipv6 (int command, struct zclient *zclient, zebra_size_t length) +zebra_read_ipv6 (int command, struct zclient *zclient, zebra_size_t length, + vrf_id_t vrf_id) { struct stream *s; struct zapi_ipv6 api; struct in6_addr nexthop; struct prefix_ipv6 p; + unsigned char plength = 0; s = zclient->ibuf; memset (&nexthop, 0, sizeof (struct in6_addr)); @@ -322,7 +345,8 @@ zebra_read_ipv6 (int command, struct zclient *zclient, zebra_size_t length) /* IPv6 prefix. */ memset (&p, 0, sizeof (struct prefix_ipv6)); p.family = AF_INET6; - p.prefixlen = stream_getc (s); + plength = stream_getc (s); + p.prefixlen = MIN(IPV6_MAX_PREFIXLEN, plength); stream_get (&p.prefix, s, PSIZE (p.prefixlen)); /* Nexthop, ifindex, distance, metric. */ @@ -345,6 +369,11 @@ zebra_read_ipv6 (int command, struct zclient *zclient, zebra_size_t length) else api.metric = 0; + if (CHECK_FLAG (api.message, ZAPI_MESSAGE_TAG)) + api.tag = stream_getl (s); + else + api.tag = 0; + /* Simply ignore link-local address. */ if (IN6_IS_ADDR_LINKLOCAL (&p.prefix)) return 0; @@ -354,15 +383,16 @@ zebra_read_ipv6 (int command, struct zclient *zclient, zebra_size_t length) if (BGP_DEBUG(zebra, ZEBRA)) { char buf[2][INET6_ADDRSTRLEN]; - zlog_debug("Zebra rcvd: IPv6 route add %s %s/%d nexthop %s metric %u", + zlog_debug("Zebra rcvd: IPv6 route add %s %s/%d nexthop %s metric %u tag %d", zebra_route_string(api.type), inet_ntop(AF_INET6, &p.prefix, buf[0], sizeof(buf[0])), p.prefixlen, inet_ntop(AF_INET, &nexthop, buf[1], sizeof(buf[1])), - api.metric); + api.metric, + api.tag); } bgp_redistribute_add ((struct prefix *)&p, NULL, &nexthop, - api.metric, api.type); + api.metric, api.type, api.tag); } else { @@ -370,20 +400,20 @@ zebra_read_ipv6 (int command, struct zclient *zclient, zebra_size_t length) { char buf[2][INET6_ADDRSTRLEN]; zlog_debug("Zebra rcvd: IPv6 route delete %s %s/%d " - "nexthop %s metric %u", + "nexthop %s metric %u tag %d", zebra_route_string(api.type), inet_ntop(AF_INET6, &p.prefix, buf[0], sizeof(buf[0])), p.prefixlen, inet_ntop(AF_INET6, &nexthop, buf[1], sizeof(buf[1])), - api.metric); + api.metric, + api.tag); } bgp_redistribute_delete ((struct prefix *) &p, api.type); } return 0; } -#endif /* HAVE_IPV6 */ - + struct interface * if_lookup_by_ipv4 (struct in_addr *addr) { @@ -435,7 +465,6 @@ if_lookup_by_ipv4_exact (struct in_addr *addr) return NULL; } -#ifdef HAVE_IPV6 struct interface * if_lookup_by_ipv6 (struct in6_addr *addr) { @@ -528,7 +557,25 @@ if_get_ipv6_local (struct interface *ifp, struct in6_addr *addr) } return 0; } -#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, @@ -547,20 +594,23 @@ bgp_nexthop_set (union sockunion *local, union sockunion *remote, if (local->sa.sa_family == AF_INET) { nexthop->v4 = local->sin.sin_addr; - ifp = if_lookup_by_ipv4 (&local->sin.sin_addr); + if (peer->update_if) + ifp = if_lookup_by_name (peer->update_if); + else + ifp = if_lookup_by_ipv4_exact (&local->sin.sin_addr); } -#ifdef HAVE_IPV6 if (local->sa.sa_family == AF_INET6) { if (IN6_IS_ADDR_LINKLOCAL (&local->sin6.sin6_addr)) { if (peer->ifname) - ifp = if_lookup_by_index (if_nametoindex (peer->ifname)); + ifp = if_lookup_by_name (peer->ifname); } + else if (peer->update_if) + ifp = if_lookup_by_name (peer->update_if); else - ifp = if_lookup_by_ipv6 (&local->sin6.sin6_addr); + ifp = if_lookup_by_ipv6_exact (&local->sin6.sin6_addr); } -#endif /* HAVE_IPV6 */ if (!ifp) return -1; @@ -570,7 +620,6 @@ bgp_nexthop_set (union sockunion *local, union sockunion *remote, /* IPv4 connection. */ if (local->sa.sa_family == AF_INET) { -#ifdef HAVE_IPV6 /* IPv6 nexthop*/ ret = if_get_ipv6_global (ifp, &nexthop->v6_global); @@ -579,17 +628,16 @@ bgp_nexthop_set (union sockunion *local, union sockunion *remote, if_get_ipv6_local (ifp, &nexthop->v6_global); else if_get_ipv6_local (ifp, &nexthop->v6_local); -#endif /* HAVE_IPV6 */ } -#ifdef HAVE_IPV6 /* IPv6 connection. */ if (local->sa.sa_family == AF_INET6) { 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*/ @@ -638,7 +686,6 @@ bgp_nexthop_set (union sockunion *local, union sockunion *remote, SET_IN6_LINKLOCAL_IFINDEX (nexthop->v6_local, 0); } #endif /* KAME */ -#endif /* HAVE_IPV6 */ return ret; } @@ -648,44 +695,81 @@ bgp_zebra_announce (struct prefix *p, struct bgp_info *info, struct bgp *bgp, sa int flags; u_char distance; struct peer *peer; + struct bgp_info *mpinfo; + size_t oldsize, newsize; + u_int32_t nhcount; + route_tag_t tag = 0; if (zclient->sock < 0) return; - if (! zclient->redist[ZEBRA_ROUTE_BGP]) + if (! vrf_bitmap_check (zclient->redist[ZEBRA_ROUTE_BGP], VRF_DEFAULT)) return; flags = 0; peer = info->peer; - if (peer_sort (peer) == BGP_PEER_IBGP || peer_sort (peer) == BGP_PEER_CONFED) + if ((info->attr->extra) && (info->attr->extra->tag != 0)) + tag = info->attr->extra->tag; + + if (peer->sort == BGP_PEER_IBGP || peer->sort == BGP_PEER_CONFED) { SET_FLAG (flags, ZEBRA_FLAG_IBGP); SET_FLAG (flags, ZEBRA_FLAG_INTERNAL); } - if ((peer_sort (peer) == BGP_PEER_EBGP && peer->ttl != 1) + if ((peer->sort == BGP_PEER_EBGP && peer_ttl (peer) != 1) || CHECK_FLAG (peer->flags, PEER_FLAG_DISABLE_CONNECTED_CHECK)) SET_FLAG (flags, ZEBRA_FLAG_INTERNAL); + nhcount = 1 + bgp_info_mpath_count (info); + if (p->family == AF_INET) { struct zapi_ipv4 api; struct in_addr *nexthop; + /* resize nexthop buffer size if necessary */ + if ((oldsize = stream_get_size (bgp_nexthop_buf)) < + (sizeof (struct in_addr *) * nhcount)) + { + newsize = (sizeof (struct in_addr *) * nhcount); + newsize = stream_resize (bgp_nexthop_buf, newsize); + if (newsize == oldsize) + { + zlog_err ("can't resize nexthop buffer"); + return; + } + } + stream_reset (bgp_nexthop_buf); + + api.vrf_id = VRF_DEFAULT; api.flags = flags; nexthop = &info->attr->nexthop; + stream_put (bgp_nexthop_buf, &nexthop, sizeof (struct in_addr *)); + for (mpinfo = bgp_info_mpath_first (info); mpinfo; + mpinfo = bgp_info_mpath_next (mpinfo)) + { + nexthop = &mpinfo->attr->nexthop; + stream_put (bgp_nexthop_buf, &nexthop, sizeof (struct in_addr *)); + } api.type = ZEBRA_ROUTE_BGP; api.message = 0; api.safi = safi; SET_FLAG (api.message, ZAPI_MESSAGE_NEXTHOP); - api.nexthop_num = 1; - api.nexthop = &nexthop; + api.nexthop_num = nhcount; + api.nexthop = (struct in_addr **)STREAM_DATA (bgp_nexthop_buf); api.ifindex_num = 0; SET_FLAG (api.message, ZAPI_MESSAGE_METRIC); api.metric = info->attr->med; + if (tag) + { + SET_FLAG (api.message, ZAPI_MESSAGE_TAG); + api.tag = tag; + } + distance = bgp_distance_apply (p, info, bgp); if (distance) @@ -696,28 +780,63 @@ bgp_zebra_announce (struct prefix *p, struct bgp_info *info, struct bgp *bgp, sa if (BGP_DEBUG(zebra, ZEBRA)) { + int i; char buf[2][INET_ADDRSTRLEN]; - zlog_debug("Zebra send: IPv4 route add %s/%d nexthop %s metric %u", + zlog_debug("Zebra send: IPv4 route add %s/%d nexthop %s metric %u" + " tag %u count %d", inet_ntop(AF_INET, &p->u.prefix4, buf[0], sizeof(buf[0])), p->prefixlen, - inet_ntop(AF_INET, nexthop, buf[1], sizeof(buf[1])), - api.metric); + inet_ntop(AF_INET, api.nexthop[0], buf[1], sizeof(buf[1])), + api.metric, api.tag, api.nexthop_num); + for (i = 1; i < api.nexthop_num; i++) + zlog_debug("Zebra send: IPv4 route add [nexthop %d] %s", + i, inet_ntop(AF_INET, api.nexthop[i], buf[1], + sizeof(buf[1]))); } zapi_ipv4_route (ZEBRA_IPV4_ROUTE_ADD, zclient, (struct prefix_ipv4 *) p, &api); } -#ifdef HAVE_IPV6 + /* We have to think about a IPv6 link-local address curse. */ if (p->family == AF_INET6) { - unsigned int ifindex; + ifindex_t ifindex; struct in6_addr *nexthop; struct zapi_ipv6 api; + int valid_nh_count = 0; + + /* resize nexthop buffer size if necessary */ + if ((oldsize = stream_get_size (bgp_nexthop_buf)) < + (sizeof (struct in6_addr *) * nhcount)) + { + newsize = (sizeof (struct in6_addr *) * nhcount); + newsize = stream_resize (bgp_nexthop_buf, newsize); + if (newsize == oldsize) + { + zlog_err ("can't resize nexthop buffer"); + return; + } + } + stream_reset (bgp_nexthop_buf); + + /* resize ifindices buffer size if necessary */ + if ((oldsize = stream_get_size (bgp_ifindices_buf)) < + (sizeof (unsigned int) * nhcount)) + { + newsize = (sizeof (unsigned int) * nhcount); + newsize = stream_resize (bgp_ifindices_buf, newsize); + if (newsize == oldsize) + { + zlog_err ("can't resize nexthop buffer"); + return; + } + } + stream_reset (bgp_ifindices_buf); ifindex = 0; nexthop = NULL; - + assert (info->attr->extra); /* Only global address nexthop exists. */ @@ -741,42 +860,116 @@ bgp_zebra_announce (struct prefix *p, struct bgp_info *info, struct bgp *bgp, sa if (nexthop == NULL) return; - if (IN6_IS_ADDR_LINKLOCAL (nexthop) && ! ifindex) + if (!ifindex) { if (info->peer->ifname) - ifindex = if_nametoindex (info->peer->ifname); + ifindex = ifname2ifindex (info->peer->ifname); else if (info->peer->nexthop.ifp) ifindex = info->peer->nexthop.ifp->ifindex; } + stream_put (bgp_nexthop_buf, &nexthop, sizeof (struct in6_addr *)); + stream_put (bgp_ifindices_buf, &ifindex, sizeof (unsigned int)); + valid_nh_count++; + + for (mpinfo = bgp_info_mpath_first (info); mpinfo; + mpinfo = bgp_info_mpath_next (mpinfo)) + { + ifindex = 0; + + /* Only global address nexthop exists. */ + if (mpinfo->attr->extra->mp_nexthop_len == 16) + nexthop = &mpinfo->attr->extra->mp_nexthop_global; + + /* If both global and link-local address present. */ + if (mpinfo->attr->extra->mp_nexthop_len == 32) + { + /* Workaround for Cisco's nexthop bug. */ + if (IN6_IS_ADDR_UNSPECIFIED (&mpinfo->attr->extra->mp_nexthop_global) + && mpinfo->peer->su_remote->sa.sa_family == AF_INET6) + { + nexthop = &mpinfo->peer->su_remote->sin6.sin6_addr; + } + else + { + nexthop = &mpinfo->attr->extra->mp_nexthop_local; + } + + if (mpinfo->peer->nexthop.ifp) + { + ifindex = mpinfo->peer->nexthop.ifp->ifindex; + } + } + + if (nexthop == NULL) + { + continue; + } + + if (!ifindex) + { + if (mpinfo->peer->ifname) + { + ifindex = if_nametoindex (mpinfo->peer->ifname); + } + else if (mpinfo->peer->nexthop.ifp) + { + ifindex = mpinfo->peer->nexthop.ifp->ifindex; + } + } + + if (ifindex == 0) + { + continue; + } + + stream_put (bgp_nexthop_buf, &nexthop, sizeof (struct in6_addr *)); + stream_put (bgp_ifindices_buf, &ifindex, sizeof (unsigned int)); + valid_nh_count++; + } /* Make Zebra API structure. */ + api.vrf_id = VRF_DEFAULT; api.flags = flags; api.type = ZEBRA_ROUTE_BGP; api.message = 0; api.safi = safi; SET_FLAG (api.message, ZAPI_MESSAGE_NEXTHOP); - api.nexthop_num = 1; - api.nexthop = &nexthop; + api.nexthop_num = valid_nh_count; + api.nexthop = (struct in6_addr **)STREAM_DATA (bgp_nexthop_buf); SET_FLAG (api.message, ZAPI_MESSAGE_IFINDEX); - api.ifindex_num = 1; - api.ifindex = &ifindex; + api.ifindex_num = valid_nh_count; + api.ifindex = (ifindex_t *)STREAM_DATA (bgp_ifindices_buf); SET_FLAG (api.message, ZAPI_MESSAGE_METRIC); api.metric = info->attr->med; + distance = ipv6_bgp_distance_apply (p, info, bgp); + + if (distance) + { + SET_FLAG (api.message, ZAPI_MESSAGE_DISTANCE); + api.distance = distance; + } + + if (tag) + { + SET_FLAG (api.message, ZAPI_MESSAGE_TAG); + api.tag = tag; + } + if (BGP_DEBUG(zebra, ZEBRA)) { char buf[2][INET6_ADDRSTRLEN]; - zlog_debug("Zebra send: IPv6 route add %s/%d nexthop %s metric %u", + zlog_debug("Zebra send: IPv6 route add %s/%d nexthop %s metric %u" + " tag %u", inet_ntop(AF_INET6, &p->u.prefix6, buf[0], sizeof(buf[0])), p->prefixlen, inet_ntop(AF_INET6, nexthop, buf[1], sizeof(buf[1])), - api.metric); + api.metric, api.tag); } zapi_ipv6_route (ZEBRA_IPV6_ROUTE_ADD, zclient, (struct prefix_ipv6 *) p, &api); } -#endif /* HAVE_IPV6 */ } void @@ -788,114 +981,93 @@ bgp_zebra_withdraw (struct prefix *p, struct bgp_info *info, safi_t safi) if (zclient->sock < 0) return; - if (! zclient->redist[ZEBRA_ROUTE_BGP]) + if (! vrf_bitmap_check (zclient->redist[ZEBRA_ROUTE_BGP], VRF_DEFAULT)) return; peer = info->peer; flags = 0; - if (peer_sort (peer) == BGP_PEER_IBGP) + if (peer->sort == BGP_PEER_IBGP) { SET_FLAG (flags, ZEBRA_FLAG_INTERNAL); SET_FLAG (flags, ZEBRA_FLAG_IBGP); } - if ((peer_sort (peer) == BGP_PEER_EBGP && peer->ttl != 1) + if ((peer->sort == BGP_PEER_EBGP && peer_ttl (peer) != 1) || CHECK_FLAG (peer->flags, PEER_FLAG_DISABLE_CONNECTED_CHECK)) SET_FLAG (flags, ZEBRA_FLAG_INTERNAL); if (p->family == AF_INET) { struct zapi_ipv4 api; - struct in_addr *nexthop; + api.vrf_id = VRF_DEFAULT; api.flags = flags; - nexthop = &info->attr->nexthop; api.type = ZEBRA_ROUTE_BGP; api.message = 0; api.safi = safi; - SET_FLAG (api.message, ZAPI_MESSAGE_NEXTHOP); - api.nexthop_num = 1; - api.nexthop = &nexthop; + api.nexthop_num = 0; api.ifindex_num = 0; SET_FLAG (api.message, ZAPI_MESSAGE_METRIC); api.metric = info->attr->med; + if ((info->attr->extra) && (info->attr->extra->tag != 0)) + { + SET_FLAG(api.message, ZAPI_MESSAGE_TAG); + api.tag = info->attr->extra->tag; + } + if (BGP_DEBUG(zebra, ZEBRA)) { char buf[2][INET_ADDRSTRLEN]; - zlog_debug("Zebra send: IPv4 route delete %s/%d nexthop %s metric %u", + zlog_debug("Zebra send: IPv4 route delete %s/%d metric %u tag %d", inet_ntop(AF_INET, &p->u.prefix4, buf[0], sizeof(buf[0])), p->prefixlen, - inet_ntop(AF_INET, nexthop, buf[1], sizeof(buf[1])), - api.metric); + api.metric, + api.tag); } zapi_ipv4_route (ZEBRA_IPV4_ROUTE_DELETE, zclient, (struct prefix_ipv4 *) p, &api); } -#ifdef HAVE_IPV6 + /* We have to think about a IPv6 link-local address curse. */ if (p->family == AF_INET6) { struct zapi_ipv6 api; - unsigned int ifindex; - struct in6_addr *nexthop; - - assert (info->attr->extra); - ifindex = 0; - nexthop = NULL; - - /* Only global address nexthop exists. */ - if (info->attr->extra->mp_nexthop_len == 16) - nexthop = &info->attr->extra->mp_nexthop_global; - - /* If both global and link-local address present. */ - if (info->attr->extra->mp_nexthop_len == 32) - { - nexthop = &info->attr->extra->mp_nexthop_local; - if (info->peer->nexthop.ifp) - ifindex = info->peer->nexthop.ifp->ifindex; - } - - if (nexthop == NULL) - return; - - if (IN6_IS_ADDR_LINKLOCAL (nexthop) && ! ifindex) - if (info->peer->ifname) - ifindex = if_nametoindex (info->peer->ifname); - + api.vrf_id = VRF_DEFAULT; api.flags = flags; api.type = ZEBRA_ROUTE_BGP; api.message = 0; api.safi = safi; - SET_FLAG (api.message, ZAPI_MESSAGE_NEXTHOP); - api.nexthop_num = 1; - api.nexthop = &nexthop; - SET_FLAG (api.message, ZAPI_MESSAGE_IFINDEX); - api.ifindex_num = 1; - api.ifindex = &ifindex; + api.nexthop_num = 0; + api.ifindex_num = 0; SET_FLAG (api.message, ZAPI_MESSAGE_METRIC); api.metric = info->attr->med; + if ((info->attr->extra) && (info->attr->extra->tag != 0)) + { + SET_FLAG(api.message, ZAPI_MESSAGE_TAG); + api.tag = info->attr->extra->tag; + } + if (BGP_DEBUG(zebra, ZEBRA)) { char buf[2][INET6_ADDRSTRLEN]; - zlog_debug("Zebra send: IPv6 route delete %s/%d nexthop %s metric %u", + zlog_debug("Zebra send: IPv6 route delete %s/%d metric %u tag %d", inet_ntop(AF_INET6, &p->u.prefix6, buf[0], sizeof(buf[0])), p->prefixlen, - inet_ntop(AF_INET6, nexthop, buf[1], sizeof(buf[1])), - api.metric); + api.metric, + api.tag); } zapi_ipv6_route (ZEBRA_IPV6_ROUTE_DELETE, zclient, (struct prefix_ipv6 *) p, &api); } -#endif /* HAVE_IPV6 */ } - + /* Other routes redistribution into BGP. */ int bgp_redistribute_set (struct bgp *bgp, afi_t afi, int type) @@ -904,10 +1076,10 @@ bgp_redistribute_set (struct bgp *bgp, afi_t afi, int type) bgp->redist[afi][type] = 1; /* Return if already redistribute flag is set. */ - if (zclient->redist[type]) + if (vrf_bitmap_check (zclient->redist[type], VRF_DEFAULT)) return CMD_WARNING; - zclient->redist[type] = 1; + vrf_bitmap_set (zclient->redist[type], VRF_DEFAULT); /* Return if zebra connection is not established. */ if (zclient->sock < 0) @@ -917,7 +1089,7 @@ bgp_redistribute_set (struct bgp *bgp, afi_t afi, int type) zlog_debug("Zebra send: redistribute add %s", zebra_route_string(type)); /* Send distribute add message to zebra. */ - zebra_redistribute_send (ZEBRA_REDISTRIBUTE_ADD, zclient, type); + zebra_redistribute_send (ZEBRA_REDISTRIBUTE_ADD, zclient, type, VRF_DEFAULT); return CMD_SUCCESS; } @@ -972,9 +1144,9 @@ bgp_redistribute_unset (struct bgp *bgp, afi_t afi, int type) bgp->redist_metric[afi][type] = 0; /* Return if zebra connection is disabled. */ - if (! zclient->redist[type]) + if (! vrf_bitmap_check (zclient->redist[type], VRF_DEFAULT)) return CMD_WARNING; - zclient->redist[type] = 0; + vrf_bitmap_unset (zclient->redist[type], VRF_DEFAULT); if (bgp->redist[AFI_IP][type] == 0 && bgp->redist[AFI_IP6][type] == 0 @@ -984,7 +1156,8 @@ bgp_redistribute_unset (struct bgp *bgp, afi_t afi, int type) if (BGP_DEBUG(zebra, ZEBRA)) zlog_debug("Zebra send: redistribute delete %s", zebra_route_string(type)); - zebra_redistribute_send (ZEBRA_REDISTRIBUTE_DELETE, zclient, type); + zebra_redistribute_send (ZEBRA_REDISTRIBUTE_DELETE, zclient, type, + VRF_DEFAULT); } /* Withdraw redistributed routes from current BGP's routing table. */ @@ -993,47 +1166,28 @@ bgp_redistribute_unset (struct bgp *bgp, afi_t afi, int type) return CMD_SUCCESS; } -/* Unset redistribution route-map configuration. */ -int -bgp_redistribute_routemap_unset (struct bgp *bgp, afi_t afi, int type) -{ - if (! bgp->rmap[afi][type].name) - return 0; - - /* Unset route-map. */ - free (bgp->rmap[afi][type].name); - bgp->rmap[afi][type].name = NULL; - bgp->rmap[afi][type].map = NULL; - - return 1; -} - -/* Unset redistribution metric configuration. */ -int -bgp_redistribute_metric_unset (struct bgp *bgp, afi_t afi, int type) -{ - if (! bgp->redist_metric_flag[afi][type]) - return 0; - - /* Unset metric. */ - bgp->redist_metric_flag[afi][type] = 0; - bgp->redist_metric[afi][type] = 0; - - return 1; -} - void bgp_zclient_reset (void) { zclient_reset (zclient); } +static void +bgp_zebra_connected (struct zclient *zclient) +{ + zclient_num_connects++; + zclient_send_requests (zclient, VRF_DEFAULT); +} + void -bgp_zebra_init (void) +bgp_zebra_init (struct thread_master *master) { + zclient_num_connects = 0; + /* Set default values. */ - zclient = zclient_new (); + zclient = zclient_new (master); zclient_init (zclient, ZEBRA_ROUTE_BGP); + zclient->zebra_connected = bgp_zebra_connected; zclient->router_id_update = bgp_router_id_update; zclient->interface_add = bgp_interface_add; zclient->interface_delete = bgp_interface_delete; @@ -1043,11 +1197,26 @@ bgp_zebra_init (void) zclient->ipv4_route_delete = zebra_read_ipv4; zclient->interface_up = bgp_interface_up; zclient->interface_down = bgp_interface_down; -#ifdef HAVE_IPV6 zclient->ipv6_route_add = zebra_read_ipv6; zclient->ipv6_route_delete = zebra_read_ipv6; -#endif /* HAVE_IPV6 */ + zclient->nexthop_update = bgp_read_nexthop_update; + + bgp_nexthop_buf = stream_new(BGP_NEXTHOP_BUF_SIZE); + bgp_ifindices_buf = stream_new(BGP_IFINDICES_BUF_SIZE); +} - /* Interface related init. */ - if_init (); +void +bgp_zebra_destroy(void) +{ + if (zclient == NULL) + return; + zclient_stop(zclient); + zclient_free(zclient); + zclient = NULL; +} + +int +bgp_zebra_num_connects(void) +{ + return zclient_num_connects; } diff --git a/bgpd/bgp_zebra.h b/bgpd/bgp_zebra.h index 1351333f7..5d4ed6231 100644 --- a/bgpd/bgp_zebra.h +++ b/bgpd/bgp_zebra.h @@ -21,8 +21,18 @@ Boston, MA 02111-1307, USA. */ #ifndef _QUAGGA_BGP_ZEBRA_H #define _QUAGGA_BGP_ZEBRA_H -extern void bgp_zebra_init (void); +#define BGP_NEXTHOP_BUF_SIZE (8 * sizeof (struct in_addr *)) +#define BGP_IFINDICES_BUF_SIZE (8 * sizeof (unsigned int)) + +extern struct stream *bgp_nexthop_buf; +extern struct in_addr router_id_zebra; +extern struct stream *bgp_ifindices_buf; + +extern void bgp_zebra_init (struct thread_master *master); +extern void bgp_zebra_destroy (void); extern int bgp_if_update_all (void); +extern int bgp_config_write_maxpaths (struct vty *, struct bgp *, afi_t, + safi_t, int *); extern int bgp_config_write_redistribute (struct vty *, struct bgp *, afi_t, safi_t, int *); extern void bgp_zebra_announce (struct prefix *, struct bgp_info *, struct bgp *, safi_t); @@ -32,14 +42,12 @@ extern int bgp_redistribute_set (struct bgp *, afi_t, int); extern int bgp_redistribute_rmap_set (struct bgp *, afi_t, int, const char *); extern int bgp_redistribute_metric_set (struct bgp *, afi_t, int, u_int32_t); extern int bgp_redistribute_unset (struct bgp *, afi_t, int); -extern int bgp_redistribute_routemap_unset (struct bgp *, afi_t, int); -extern int bgp_redistribute_metric_unset (struct bgp *, afi_t, int); extern struct interface *if_lookup_by_ipv4 (struct in_addr *); extern struct interface *if_lookup_by_ipv4_exact (struct in_addr *); -#ifdef HAVE_IPV6 extern struct interface *if_lookup_by_ipv6 (struct in6_addr *); extern struct interface *if_lookup_by_ipv6_exact (struct in6_addr *); -#endif /* HAVE_IPV6 */ + +extern int bgp_zebra_num_connects(void); #endif /* _QUAGGA_BGP_ZEBRA_H */ diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index 5dc014be9..6aeecb13d 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -26,6 +26,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "stream.h" #include "command.h" #include "sockunion.h" +#include "sockopt.h" #include "network.h" #include "memory.h" #include "filter.h" @@ -35,6 +36,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "plist.h" #include "linklist.h" #include "workqueue.h" +#include "table.h" #include "bgpd/bgpd.h" #include "bgpd/bgp_table.h" @@ -54,13 +56,16 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "bgpd/bgp_nexthop.h" #include "bgpd/bgp_damp.h" #include "bgpd/bgp_mplsvpn.h" +#include "bgpd/bgp_encap.h" #include "bgpd/bgp_advertise.h" #include "bgpd/bgp_network.h" #include "bgpd/bgp_vty.h" +#include "bgpd/bgp_mpath.h" +#include "bgpd/bgp_nht.h" #ifdef HAVE_SNMP #include "bgpd/bgp_snmp.h" #endif /* HAVE_SNMP */ - + /* BGP process wide configuration. */ static struct bgp_master bgp_master; @@ -71,7 +76,7 @@ struct bgp_master *bm; /* BGP community-list. */ struct community_list_handler *bgp_clist; - + /* BGP global flag manipulation. */ int bgp_option_set (int flag) @@ -81,6 +86,7 @@ bgp_option_set (int flag) case BGP_OPT_NO_FIB: case BGP_OPT_MULTIPLE_INSTANCE: case BGP_OPT_CONFIG_CISCO: + case BGP_OPT_NO_LISTEN: SET_FLAG (bm->options, flag); break; default: @@ -113,7 +119,7 @@ bgp_option_check (int flag) { return CHECK_FLAG (bm->options, flag); } - + /* BGP flag manipulation. */ int bgp_flag_set (struct bgp *bgp, int flag) @@ -134,7 +140,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) @@ -153,9 +159,9 @@ bgp_config_check (struct bgp *bgp, int config) { return CHECK_FLAG (bgp->config, config); } - + /* Set BGP router identifier. */ -int +static int bgp_router_id_set (struct bgp *bgp, struct in_addr *id) { struct peer *peer; @@ -173,7 +179,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, @@ -183,6 +189,27 @@ bgp_router_id_set (struct bgp *bgp, struct in_addr *id) return 0; } +void +bgp_router_id_zebra_bump (void) +{ + struct listnode *node, *nnode; + struct bgp *bgp; + + for (ALL_LIST_ELEMENTS (bm->bgp, node, nnode, bgp)) + { + if (!bgp->router_id_static.s_addr) + bgp_router_id_set (bgp, &router_id_zebra); + } +} + +int +bgp_router_id_static_set (struct bgp *bgp, struct in_addr id) +{ + bgp->router_id_static = id; + bgp_router_id_set (bgp, id.s_addr ? &id : &router_id_zebra); + return 0; +} + /* BGP's cluster-id control. */ int bgp_cluster_id_set (struct bgp *bgp, struct in_addr *cluster_id) @@ -200,10 +227,10 @@ bgp_cluster_id_set (struct bgp *bgp, struct in_addr *cluster_id) /* Clear all IBGP peer. */ for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer)) { - if (peer_sort (peer) != BGP_PEER_IBGP) + 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, @@ -228,10 +255,10 @@ bgp_cluster_id_unset (struct bgp *bgp) /* Clear all IBGP peer. */ for (ALL_LIST_ELEMENTS (bgp->peer, node, nnode, peer)) { - if (peer_sort (peer) != BGP_PEER_IBGP) + 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, @@ -240,7 +267,7 @@ bgp_cluster_id_unset (struct bgp *bgp) } return 0; } - + /* time_t value that is monotonicly increasing * and uneffected by adjustments to system clock */ @@ -271,7 +298,7 @@ bgp_timers_unset (struct bgp *bgp) return 0; } - + /* BGP confederation configuration. */ int bgp_confederation_id_set (struct bgp *bgp, as_t as) @@ -300,7 +327,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, @@ -320,7 +347,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, @@ -349,7 +376,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, @@ -413,7 +440,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, @@ -469,7 +496,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, @@ -483,7 +510,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) @@ -506,7 +533,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. */ @@ -593,13 +620,14 @@ peer_af_flag_reset (struct peer *peer, afi_t afi, safi_t safi) /* Clear ORF info */ peer->orf_plist[afi][safi] = NULL; sprintf (orf_name, "%s.%d.%d", peer->host, afi, safi); - prefix_bgp_orf_remove_all (orf_name); + prefix_bgp_orf_remove_all (afi, orf_name); /* Set default neighbor send-community. */ if (! bgp_option_check (BGP_OPT_CONFIG_CISCO)) { SET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_SEND_COMMUNITY); SET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_SEND_EXT_COMMUNITY); + SET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_SEND_LARGE_COMMUNITY); } /* Clear neighbor default_originate_rmap */ @@ -619,7 +647,8 @@ peer_global_config_reset (struct peer *peer) { peer->weight = 0; peer->change_local_as = 0; - peer->ttl = (peer_sort (peer) == BGP_PEER_IBGP ? 255 : 1); + peer->ttl = 0; + peer->gtsm_hops = 0; if (peer->update_source) { sockunion_free (peer->update_source); @@ -644,9 +673,9 @@ peer_global_config_reset (struct peer *peer) peer->v_connect = BGP_DEFAULT_CONNECT_RETRY; } -/* Check peer's AS number and determin is this peer IBGP or EBGP */ -int -peer_sort (struct peer *peer) +/* Check peer's AS number and determines if this peer is IBGP or EBGP */ +static bgp_peer_sort_t +peer_calc_sort (struct peer *peer) { struct bgp *bgp; @@ -695,13 +724,19 @@ peer_sort (struct peer *peer) } } +/* Calculate and cache the peer "sort" */ +bgp_peer_sort_t +peer_sort (struct peer *peer) +{ + peer->sort = peer_calc_sort (peer); + return peer->sort; +} + static void peer_free (struct peer *peer) { assert (peer->status == Deleted); - bgp_unlock(peer->bgp); - /* this /ought/ to have been done already through bgp_stop earlier, * but just to be sure.. */ @@ -711,23 +746,44 @@ peer_free (struct peer *peer) BGP_EVENT_FLUSH (peer); if (peer->desc) - XFREE (MTYPE_PEER_DESC, peer->desc); + { + XFREE (MTYPE_PEER_DESC, peer->desc); + peer->desc = NULL; + } /* Free allocated host character. */ if (peer->host) - XFREE (MTYPE_BGP_PEER_HOST, peer->host); - + { + XFREE (MTYPE_BGP_PEER_HOST, peer->host); + peer->host = NULL; + } + /* Update source configuration. */ if (peer->update_source) - sockunion_free (peer->update_source); + { + sockunion_free (peer->update_source); + peer->update_source = NULL; + } if (peer->update_if) - XFREE (MTYPE_PEER_UPDATE_SOURCE, peer->update_if); + { + XFREE (MTYPE_PEER_UPDATE_SOURCE, peer->update_if); + peer->update_if = NULL; + } if (peer->clear_node_queue) - work_queue_free (peer->clear_node_queue); + { + work_queue_free(peer->clear_node_queue); + peer->clear_node_queue = NULL; + } + + if (peer->notify.data) + XFREE(MTYPE_TMP, peer->notify.data); bgp_sync_delete (peer); + + bgp_unlock(peer->bgp); + memset (peer, 0, sizeof (struct peer)); XFREE (MTYPE_BGP_PEER, peer); @@ -735,10 +791,14 @@ peer_free (struct peer *peer) /* increase reference count on a struct peer */ struct peer * -peer_lock (struct peer *peer) +peer_lock_with_caller (const char *name, struct peer *peer) { assert (peer && (peer->lock >= 0)); - + +#if 0 + zlog_debug("%s peer_lock %p %d", name, peer, peer->lock); +#endif + peer->lock++; return peer; @@ -748,30 +808,22 @@ peer_lock (struct peer *peer) * struct peer is freed and NULL returned if last reference */ struct peer * -peer_unlock (struct peer *peer) +peer_unlock_with_caller (const char *name, struct peer *peer) { assert (peer && (peer->lock > 0)); - + +#if 0 + zlog_debug("%s peer_unlock %p %d", name, peer, peer->lock); +#endif + peer->lock--; if (peer->lock == 0) { -#if 0 - zlog_debug ("unlocked and freeing"); - zlog_backtrace (LOG_DEBUG); -#endif peer_free (peer); return NULL; } -#if 0 - if (peer->lock == 1) - { - zlog_debug ("unlocked to 1"); - zlog_backtrace (LOG_DEBUG); - } -#endif - return peer; } @@ -796,7 +848,6 @@ peer_new (struct bgp *bgp) peer->fd = -1; peer->v_start = BGP_INIT_START_TIMER; peer->v_connect = BGP_DEFAULT_CONNECT_RETRY; - peer->v_asorig = BGP_DEFAULT_ASORIGINATE; peer->status = Idle; peer->ostatus = Idle; peer->weight = 0; @@ -813,6 +864,7 @@ peer_new (struct bgp *bgp) { SET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_SEND_COMMUNITY); SET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_SEND_EXT_COMMUNITY); + SET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_SEND_LARGE_COMMUNITY); } peer->orf_plist[afi][safi] = NULL; } @@ -821,7 +873,20 @@ peer_new (struct bgp *bgp) /* Create buffers. */ peer->ibuf = stream_new (BGP_MAX_PACKET_SIZE); peer->obuf = stream_fifo_new (); - peer->work = stream_new (BGP_MAX_PACKET_SIZE); + + /* We use a larger buffer for peer->work in the event that: + * - We RX a BGP_UPDATE where the attributes alone are just + * under BGP_MAX_PACKET_SIZE + * - The user configures an outbound route-map that does many as-path + * prepends or adds many communities. At most they can have CMD_ARGC_MAX + * args in a route-map so there is a finite limit on how large they can + * make the attributes. + * + * Having a buffer with BGP_MAX_PACKET_SIZE_OVERFLOW allows us to avoid bounds + * checking for every single attribute as we construct an UPDATE. + */ + peer->work = stream_new (BGP_MAX_PACKET_SIZE + BGP_MAX_PACKET_SIZE_OVERFLOW); + peer->scratch = stream_new (BGP_MAX_PACKET_SIZE); bgp_sync_init (peer); @@ -864,9 +929,6 @@ peer_create (union sockunion *su, struct bgp *bgp, as_t local_as, /* Last read and reset time set */ peer->readtime = peer->resettime = bgp_clock (); - /* Default TTL set. */ - peer->ttl = (peer_sort (peer) == BGP_PEER_IBGP ? 255 : 1); - /* Make peer's address string. */ sockunion2str (su, buf, SU_ADDRSTRLEN); peer->host = XSTRDUP (MTYPE_BGP_PEER_HOST, buf); @@ -896,12 +958,12 @@ peer_create_accept (struct bgp *bgp) static void peer_as_change (struct peer *peer, as_t as) { - int type; + struct peer *conf; /* 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, @@ -910,7 +972,6 @@ peer_as_change (struct peer *peer, as_t as) else BGP_EVENT_ADD (peer, BGP_Stop); } - type = peer_sort (peer); peer->as = as; if (bgp_config_check (peer->bgp, BGP_CONFIG_CONFEDERATION) @@ -921,16 +982,17 @@ peer_as_change (struct peer *peer, as_t as) peer->local_as = peer->bgp->as; /* Advertisement-interval reset */ - if (peer_sort (peer) == BGP_PEER_IBGP) - peer->v_routeadv = BGP_DEFAULT_IBGP_ROUTEADV; - else - peer->v_routeadv = BGP_DEFAULT_EBGP_ROUTEADV; + conf = NULL; + if (peer->group) + conf = peer->group->conf; - /* TTL reset */ - if (peer_sort (peer) == BGP_PEER_IBGP) - peer->ttl = 255; - else if (type == BGP_PEER_IBGP) - peer->ttl = 1; + if (conf && CHECK_FLAG (conf->config, PEER_CONFIG_ROUTEADV)) + peer->v_routeadv = conf->routeadv; + else + if (peer_sort (peer) == BGP_PEER_IBGP) + peer->v_routeadv = BGP_DEFAULT_IBGP_ROUTEADV; + else + peer->v_routeadv = BGP_DEFAULT_EBGP_ROUTEADV; /* reflector-client reset */ if (peer_sort (peer) != BGP_PEER_IBGP) @@ -941,10 +1003,16 @@ peer_as_change (struct peer *peer, as_t as) PEER_FLAG_REFLECTOR_CLIENT); UNSET_FLAG (peer->af_flags[AFI_IP][SAFI_MPLS_VPN], PEER_FLAG_REFLECTOR_CLIENT); + UNSET_FLAG (peer->af_flags[AFI_IP][SAFI_ENCAP], + PEER_FLAG_REFLECTOR_CLIENT); UNSET_FLAG (peer->af_flags[AFI_IP6][SAFI_UNICAST], PEER_FLAG_REFLECTOR_CLIENT); UNSET_FLAG (peer->af_flags[AFI_IP6][SAFI_MULTICAST], PEER_FLAG_REFLECTOR_CLIENT); + UNSET_FLAG (peer->af_flags[AFI_IP6][SAFI_MPLS_VPN], + PEER_FLAG_REFLECTOR_CLIENT); + UNSET_FLAG (peer->af_flags[AFI_IP6][SAFI_ENCAP], + PEER_FLAG_REFLECTOR_CLIENT); } /* local-as reset */ @@ -952,6 +1020,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); } } @@ -1016,9 +1085,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; @@ -1137,6 +1206,15 @@ peer_deactivate (struct peer *peer, afi_t afi, safi_t safi) return 0; } +int +peer_afc_set (struct peer *peer, afi_t afi, safi_t safi, int enable) +{ + if (enable) + return peer_activate (peer, afi, safi); + else + return peer_deactivate (peer, afi, safi); +} + static void peer_nsf_stop (struct peer *peer) { @@ -1212,7 +1290,10 @@ peer_delete (struct peer *peer) peer->last_reset = PEER_DOWN_NEIGHBOR_DELETE; bgp_stop (peer); bgp_fsm_change_status (peer, Deleted); - + + /* Remove from NHT */ + bgp_unlink_nexthop_by_peer (peer); + /* Password configuration */ if (peer->password) { @@ -1256,20 +1337,41 @@ peer_delete (struct peer *peer) /* Buffers. */ if (peer->ibuf) - stream_free (peer->ibuf); + { + stream_free (peer->ibuf); + peer->ibuf = NULL; + } + if (peer->obuf) - stream_fifo_free (peer->obuf); + { + stream_fifo_free (peer->obuf); + peer->obuf = NULL; + } + if (peer->work) - stream_free (peer->work); - peer->obuf = NULL; - peer->work = peer->ibuf = NULL; + { + stream_free (peer->work); + peer->work = NULL; + } + + if (peer->scratch) + { + stream_free(peer->scratch); + peer->scratch = NULL; + } /* Local and remote addresses. */ if (peer->su_local) - sockunion_free (peer->su_local); + { + sockunion_free (peer->su_local); + peer->su_local = NULL; + } + if (peer->su_remote) - sockunion_free (peer->su_remote); - peer->su_local = peer->su_remote = NULL; + { + sockunion_free (peer->su_remote); + peer->su_remote = NULL; + } /* Free filter related memory. */ for (afi = AFI_IP; afi < AFI_MAX; afi++) @@ -1280,38 +1382,54 @@ peer_delete (struct peer *peer) for (i = FILTER_IN; i < FILTER_MAX; i++) { if (filter->dlist[i].name) - free (filter->dlist[i].name); + { + free(filter->dlist[i].name); + filter->dlist[i].name = NULL; + } + if (filter->plist[i].name) - free (filter->plist[i].name); + { + free(filter->plist[i].name); + filter->plist[i].name = NULL; + } + if (filter->aslist[i].name) - free (filter->aslist[i].name); - - filter->dlist[i].name = NULL; - filter->plist[i].name = NULL; - filter->aslist[i].name = NULL; + { + free(filter->aslist[i].name); + filter->aslist[i].name = NULL; + } } + for (i = RMAP_IN; i < RMAP_MAX; i++) { if (filter->map[i].name) - free (filter->map[i].name); - filter->map[i].name = NULL; + { + free (filter->map[i].name); + filter->map[i].name = NULL; + } } if (filter->usmap.name) - free (filter->usmap.name); + { + free (filter->usmap.name); + filter->usmap.name = NULL; + } if (peer->default_rmap[afi][safi].name) - free (peer->default_rmap[afi][safi].name); - - filter->usmap.name = NULL; - peer->default_rmap[afi][safi].name = NULL; + { + free (peer->default_rmap[afi][safi].name); + peer->default_rmap[afi][safi].name = NULL; + } } + if (CHECK_FLAG(bgp->flags, BGP_FLAG_DELETING)) + bgp_peer_clear_node_queue_drain_immediate(peer); + peer_unlock (peer); /* initial reference */ return 0; } - + static int peer_group_cmp (struct peer_group *g1, struct peer_group *g2) { @@ -1325,8 +1443,11 @@ peer_group_active (struct peer *peer) if (peer->af_group[AFI_IP][SAFI_UNICAST] || peer->af_group[AFI_IP][SAFI_MULTICAST] || peer->af_group[AFI_IP][SAFI_MPLS_VPN] + || peer->af_group[AFI_IP][SAFI_ENCAP] || peer->af_group[AFI_IP6][SAFI_UNICAST] - || peer->af_group[AFI_IP6][SAFI_MULTICAST]) + || peer->af_group[AFI_IP6][SAFI_MULTICAST] + || peer->af_group[AFI_IP6][SAFI_MPLS_VPN] + || peer->af_group[AFI_IP6][SAFI_ENCAP]) return 1; return 0; } @@ -1378,7 +1499,7 @@ peer_group_get (struct bgp *bgp, const char *name) group->conf->host = XSTRDUP (MTYPE_BGP_PEER_HOST, name); group->conf->group = group; group->conf->as = 0; - group->conf->ttl = 1; + group->conf->ttl = 0; group->conf->gtsm_hops = 0; group->conf->v_routeadv = BGP_DEFAULT_EBGP_ROUTEADV; UNSET_FLAG (group->conf->config, PEER_CONFIG_TIMER); @@ -1440,19 +1561,17 @@ peer_group2peer_config_copy (struct peer_group *group, struct peer *peer, peer->v_connect = BGP_DEFAULT_CONNECT_RETRY; /* advertisement-interval reset */ - if (peer_sort (peer) == BGP_PEER_IBGP) - peer->v_routeadv = BGP_DEFAULT_IBGP_ROUTEADV; + if (CHECK_FLAG (conf->config, PEER_CONFIG_ROUTEADV)) + peer->v_routeadv = conf->routeadv; else - peer->v_routeadv = BGP_DEFAULT_EBGP_ROUTEADV; + if (peer_sort (peer) == BGP_PEER_IBGP) + peer->v_routeadv = BGP_DEFAULT_IBGP_ROUTEADV; + else + 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); @@ -1680,6 +1799,16 @@ peer_group_remote_as (struct bgp *bgp, const char *group_name, as_t *as) return 0; } +int +peer_ttl (struct peer *peer) +{ + if (peer->ttl) + return peer->ttl; + if (peer->gtsm_hops || peer->sort == BGP_PEER_IBGP) + return 255; + return 1; +} + int peer_group_delete (struct peer_group *group) { @@ -1691,7 +1820,6 @@ peer_group_delete (struct peer_group *group) for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer)) { - peer->group = NULL; peer_delete (peer); } list_delete (group->peer); @@ -1721,7 +1849,6 @@ peer_group_remote_as_delete (struct peer_group *group) for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer)) { - peer->group = NULL; peer_delete (peer); } list_delete_all_node (group->peer); @@ -1806,20 +1933,20 @@ peer_group_bind (struct bgp *bgp, union sockunion *su, if (first_member) { /* Advertisement-interval reset */ - if (peer_sort (group->conf) == BGP_PEER_IBGP) - group->conf->v_routeadv = BGP_DEFAULT_IBGP_ROUTEADV; - else - group->conf->v_routeadv = BGP_DEFAULT_EBGP_ROUTEADV; - - /* ebgp-multihop reset */ - if (peer_sort (group->conf) == BGP_PEER_IBGP) - group->conf->ttl = 255; + if (! CHECK_FLAG (group->conf->config, PEER_CONFIG_ROUTEADV)) + { + if (peer_sort (group->conf) == BGP_PEER_IBGP) + group->conf->v_routeadv = BGP_DEFAULT_IBGP_ROUTEADV; + else + group->conf->v_routeadv = BGP_DEFAULT_EBGP_ROUTEADV; + } /* local-as reset */ if (peer_sort (group->conf) != BGP_PEER_EBGP) { 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); } } @@ -1862,7 +1989,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, @@ -1905,7 +2032,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, @@ -1916,7 +2043,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) @@ -1947,6 +2086,8 @@ bgp_create (as_t *as, const char *name) bgp->route[afi][safi] = bgp_table_init (afi, safi); bgp->aggregate[afi][safi] = bgp_table_init (afi, safi); bgp->rib[afi][safi] = bgp_table_init (afi, safi); + bgp->maxpaths[afi][safi].maxpaths_ebgp = BGP_DEFAULT_MAXPATHS; + bgp->maxpaths[afi][safi].maxpaths_ibgp = BGP_DEFAULT_MAXPATHS; } bgp->default_local_pref = BGP_DEFAULT_LOCAL_PREF; @@ -1954,12 +2095,16 @@ bgp_create (as_t *as, const char *name) bgp->default_keepalive = BGP_DEFAULT_KEEPALIVE; bgp->restart_time = BGP_DEFAULT_RESTART_TIME; bgp->stalepath_time = BGP_DEFAULT_STALEPATH_TIME; + bgp_flag_set (bgp, BGP_FLAG_LOG_NEIGHBOR_CHANGES); bgp->as = *as; if (name) bgp->name = strdup (name); + THREAD_TIMER_ON (bm->master, bgp->t_startup, bgp_startup_timer_expire, + bgp, bgp->restart_time); + return bgp; } @@ -1967,7 +2112,7 @@ bgp_create (as_t *as, const char *name) struct bgp * bgp_get_default (void) { - if (bm->bgp->head) + if (bm && bm->bgp && bm->bgp->head) return (listgetdata (listhead (bm->bgp))); return NULL; } @@ -2053,7 +2198,8 @@ bgp_get (struct bgp **bgp_val, as_t *as, const char *name) *bgp_val = bgp; /* Create BGP server socket, if first instance. */ - if (list_isempty(bm->bgp)) + if (list_isempty(bm->bgp) + && !bgp_option_check (BGP_OPT_NO_LISTEN)) { if (bgp_socket (bm->port, bm->address) < 0) return BGP_ERR_INVALID_VALUE; @@ -2070,11 +2216,26 @@ bgp_delete (struct bgp *bgp) { struct peer *peer; struct peer_group *group; - struct listnode *node; - struct listnode *next; + struct listnode *node, *pnode; + struct listnode *next, *pnext; afi_t afi; int i; + SET_FLAG(bgp->flags, BGP_FLAG_DELETING); + + THREAD_OFF (bgp->t_startup); + + for (ALL_LIST_ELEMENTS (bgp->peer, node, next, peer)) + { + if (peer->status == Established || + peer->status == OpenSent || + peer->status == OpenConfirm) + { + bgp_notify_send (peer, BGP_NOTIFY_CEASE, + BGP_NOTIFY_CEASE_PEER_UNCONFIG); + } + } + /* Delete static route. */ bgp_static_delete (bgp); @@ -2085,10 +2246,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, pnode, pnext, 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); @@ -2096,7 +2275,16 @@ bgp_delete (struct bgp *bgp) peer_delete(bgp->peer_self); bgp->peer_self = NULL; } - + + /* + * Free pending deleted routes. Unfortunately, it also has to process + * all the pending activity for other instances of struct bgp. + * + * This call was added to achieve clean memory allocation at exit, + * for the sake of valgrind. + */ + bgp_process_queues_drain_immediate(); + /* Remove visibility via the master list - there may however still be * routes to be processed still referencing the struct bgp. */ @@ -2150,7 +2338,7 @@ bgp_free (struct bgp *bgp) } XFREE (MTYPE_BGP, bgp); } - + struct peer * peer_lookup (struct bgp *bgp, union sockunion *su) { @@ -2219,7 +2407,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) @@ -2227,8 +2415,11 @@ peer_active (struct peer *peer) if (peer->afc[AFI_IP][SAFI_UNICAST] || peer->afc[AFI_IP][SAFI_MULTICAST] || peer->afc[AFI_IP][SAFI_MPLS_VPN] + || peer->afc[AFI_IP][SAFI_ENCAP] || peer->afc[AFI_IP6][SAFI_UNICAST] - || peer->afc[AFI_IP6][SAFI_MULTICAST]) + || peer->afc[AFI_IP6][SAFI_MULTICAST] + || peer->afc[AFI_IP6][SAFI_MPLS_VPN] + || peer->afc[AFI_IP6][SAFI_ENCAP]) return 1; return 0; } @@ -2240,12 +2431,15 @@ peer_active_nego (struct peer *peer) if (peer->afc_nego[AFI_IP][SAFI_UNICAST] || peer->afc_nego[AFI_IP][SAFI_MULTICAST] || peer->afc_nego[AFI_IP][SAFI_MPLS_VPN] + || peer->afc_nego[AFI_IP][SAFI_ENCAP] || peer->afc_nego[AFI_IP6][SAFI_UNICAST] - || peer->afc_nego[AFI_IP6][SAFI_MULTICAST]) + || peer->afc_nego[AFI_IP6][SAFI_MULTICAST] + || peer->afc_nego[AFI_IP6][SAFI_MPLS_VPN] + || peer->afc_nego[AFI_IP6][SAFI_ENCAP]) return 1; return 0; } - + /* peer_flag_change_type. */ enum peer_change_type { @@ -2262,6 +2456,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); @@ -2310,6 +2507,7 @@ static const struct peer_flag_action peer_af_flag_action_list[] = { PEER_FLAG_NEXTHOP_SELF, 1, peer_change_reset_out }, { PEER_FLAG_SEND_COMMUNITY, 1, peer_change_reset_out }, { PEER_FLAG_SEND_EXT_COMMUNITY, 1, peer_change_reset_out }, + { PEER_FLAG_SEND_LARGE_COMMUNITY, 1, peer_change_reset_out }, { PEER_FLAG_SOFT_RECONFIG, 0, peer_change_reset_in }, { PEER_FLAG_REFLECTOR_CLIENT, 1, peer_change_reset }, { PEER_FLAG_RSERVER_CLIENT, 1, peer_change_reset }, @@ -2321,6 +2519,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 } }; @@ -2396,7 +2595,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 @@ -2408,7 +2607,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; @@ -2661,7 +2860,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) @@ -2670,30 +2869,18 @@ peer_ebgp_multihop_set (struct peer *peer, int ttl) struct listnode *node, *nnode; struct peer *peer1; - if (peer_sort (peer) == BGP_PEER_IBGP) - return 0; - - /* see comment in peer_ttl_security_hops_set() */ - if (ttl != MAXTTL) - { - if (CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) - { - group = peer->group; - if (group->conf->gtsm_hops != 0) - return BGP_ERR_NO_EBGP_MULTIHOP_WITH_TTLHACK; + if (peer->sort == BGP_PEER_IBGP) + return BGP_ERR_NO_IBGP_WITH_TTLHACK; - for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer1)) - { - if (peer_sort (peer1) == BGP_PEER_IBGP) - continue; + if (peer->gtsm_hops != 0) + return BGP_ERR_NO_EBGP_MULTIHOP_WITH_TTLHACK; - if (peer1->gtsm_hops != 0) - return BGP_ERR_NO_EBGP_MULTIHOP_WITH_TTLHACK; - } - } - else + if (CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) + { + group = peer->group; + for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer1)) { - if (peer->gtsm_hops != 0) + if (peer1->gtsm_hops != 0) return BGP_ERR_NO_EBGP_MULTIHOP_WITH_TTLHACK; } } @@ -2702,68 +2889,24 @@ peer_ebgp_multihop_set (struct peer *peer, int ttl) if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) { - if (peer->fd >= 0 && peer_sort (peer) != BGP_PEER_IBGP) - sockopt_ttl (peer->su.sa.sa_family, peer->fd, peer->ttl); + bgp_set_socket_ttl (peer, peer->fd); } else { group = peer->group; for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer)) - { - if (peer_sort (peer) == BGP_PEER_IBGP) - continue; - - peer->ttl = group->conf->ttl; - - if (peer->fd >= 0) - sockopt_ttl (peer->su.sa.sa_family, peer->fd, peer->ttl); - } - } - return 0; -} - -int -peer_ebgp_multihop_unset (struct peer *peer) -{ - struct peer_group *group; - struct listnode *node, *nnode; - - if (peer_sort (peer) == BGP_PEER_IBGP) - return 0; - - if (peer->gtsm_hops != 0 && peer->ttl != MAXTTL) - return BGP_ERR_NO_EBGP_MULTIHOP_WITH_TTLHACK; - - if (peer_group_active (peer)) - peer->ttl = peer->group->conf->ttl; - else - peer->ttl = 1; - - if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) - { - if (peer->fd >= 0 && peer_sort (peer) != BGP_PEER_IBGP) - sockopt_ttl (peer->su.sa.sa_family, peer->fd, peer->ttl); + { + peer->ttl = ttl; + bgp_set_socket_ttl (peer, peer->fd); + } } - else - { - group = peer->group; - for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer)) - { - if (peer_sort (peer) == BGP_PEER_IBGP) - continue; - peer->ttl = 1; - - if (peer->fd >= 0) - sockopt_ttl (peer->su.sa.sa_family, peer->fd, peer->ttl); - } - } return 0; } - + /* Neighbor description. */ int -peer_description_set (struct peer *peer, char *desc) +peer_description_set (struct peer *peer, const char *desc) { if (peer->desc) XFREE (MTYPE_PEER_DESC, peer->desc); @@ -2783,7 +2926,7 @@ peer_description_unset (struct peer *peer) return 0; } - + /* Neighbor update-source. */ int peer_update_source_if_set (struct peer *peer, const char *ifname) @@ -2811,7 +2954,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, @@ -2843,7 +2986,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, @@ -2856,7 +2999,7 @@ peer_update_source_if_set (struct peer *peer, const char *ifname) } int -peer_update_source_addr_set (struct peer *peer, union sockunion *su) +peer_update_source_addr_set (struct peer *peer, const union sockunion *su) { struct peer_group *group; struct listnode *node, *nnode; @@ -2880,7 +3023,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, @@ -2911,7 +3054,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, @@ -2962,7 +3105,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, @@ -2992,7 +3135,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, @@ -3003,7 +3146,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) @@ -3108,7 +3251,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) { @@ -3122,7 +3265,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) @@ -3170,7 +3313,7 @@ peer_weight_unset (struct peer *peer) } return 0; } - + int peer_timers_set (struct peer *peer, u_int32_t keepalive, u_int32_t holdtime) { @@ -3240,10 +3383,13 @@ peer_timers_unset (struct peer *peer) return 0; } - + int peer_timers_connect_set (struct peer *peer, u_int32_t connect) { + struct peer_group *group; + struct listnode *node, *nnode; + if (peer_group_active (peer)) return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER; @@ -3257,12 +3403,26 @@ peer_timers_connect_set (struct peer *peer, u_int32_t connect) /* Set value to timer setting. */ peer->v_connect = connect; + if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) + return 0; + + /* peer-group member updates. */ + group = peer->group; + for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer)) + { + SET_FLAG (peer->config, PEER_CONFIG_CONNECT); + peer->connect = connect; + peer->v_connect = connect; + } return 0; } int peer_timers_connect_unset (struct peer *peer) { + struct peer_group *group; + struct listnode *node, *nnode; + if (peer_group_active (peer)) return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER; @@ -3273,12 +3433,26 @@ peer_timers_connect_unset (struct peer *peer) /* Set timer setting to default value. */ peer->v_connect = BGP_DEFAULT_CONNECT_RETRY; - return 0; + if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) + return 0; + + /* peer-group member updates. */ + group = peer->group; + for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer)) + { + UNSET_FLAG (peer->config, PEER_CONFIG_CONNECT); + peer->connect = 0; + peer->v_connect = BGP_DEFAULT_CONNECT_RETRY; + } + return 0; } - + int peer_advertise_interval_set (struct peer *peer, u_int32_t routeadv) { + struct peer_group *group; + struct listnode *node, *nnode; + if (peer_group_active (peer)) return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER; @@ -3289,26 +3463,57 @@ peer_advertise_interval_set (struct peer *peer, u_int32_t routeadv) peer->routeadv = routeadv; peer->v_routeadv = routeadv; + if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) + return 0; + + /* peer-group member updates. */ + group = peer->group; + for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer)) + { + SET_FLAG (peer->config, PEER_CONFIG_ROUTEADV); + peer->routeadv = routeadv; + peer->v_routeadv = routeadv; + } + return 0; } int peer_advertise_interval_unset (struct peer *peer) { + struct peer_group *group; + struct listnode *node, *nnode; + if (peer_group_active (peer)) return BGP_ERR_INVALID_FOR_PEER_GROUP_MEMBER; UNSET_FLAG (peer->config, PEER_CONFIG_ROUTEADV); peer->routeadv = 0; - if (peer_sort (peer) == BGP_PEER_IBGP) + if (peer->sort == BGP_PEER_IBGP) peer->v_routeadv = BGP_DEFAULT_IBGP_ROUTEADV; else peer->v_routeadv = BGP_DEFAULT_EBGP_ROUTEADV; + + if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) + return 0; + + /* peer-group member updates. */ + group = peer->group; + for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer)) + { + UNSET_FLAG (peer->config, PEER_CONFIG_ROUTEADV); + peer->routeadv = 0; + + if (peer->sort == BGP_PEER_IBGP) + peer->v_routeadv = BGP_DEFAULT_IBGP_ROUTEADV; + else + peer->v_routeadv = BGP_DEFAULT_EBGP_ROUTEADV; + } return 0; } - + /* neighbor interface */ int peer_interface_set (struct peer *peer, const char *str) @@ -3329,7 +3534,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) @@ -3390,9 +3595,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; @@ -3408,9 +3613,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; @@ -3419,9 +3629,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, @@ -3442,7 +3657,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, @@ -3469,10 +3689,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, @@ -3489,8 +3710,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, @@ -3501,7 +3723,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) @@ -3524,8 +3746,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); @@ -3542,7 +3764,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); @@ -3570,7 +3792,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); @@ -3593,7 +3815,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); @@ -3606,7 +3828,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, @@ -3697,19 +3919,19 @@ peer_distribute_unset (struct peer *peer, afi_t afi, safi_t safi, int direct) if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) return 0; - group = peer->group; - for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer)) - { - filter = &peer->filter[afi][safi]; + group = peer->group; + for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer)) + { + filter = &peer->filter[afi][safi]; - if (! peer->af_group[afi][safi]) - continue; + if (! peer->af_group[afi][safi]) + continue; - if (filter->dlist[direct].name) - free (filter->dlist[direct].name); - filter->dlist[direct].name = NULL; - filter->dlist[direct].alist = NULL; - } + if (filter->dlist[direct].name) + free (filter->dlist[direct].name); + filter->dlist[direct].name = NULL; + filter->dlist[direct].alist = NULL; + } return 0; } @@ -3766,7 +3988,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, @@ -3925,7 +4147,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) @@ -4079,7 +4301,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, @@ -4187,7 +4409,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, @@ -4269,7 +4491,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, @@ -4290,24 +4512,33 @@ peer_maximum_prefix_set (struct peer *peer, afi_t afi, safi_t safi, else UNSET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX_WARNING); - if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) - return 0; - - group = peer->group; - for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer)) + if (CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) { - if (! peer->af_group[afi][safi]) - continue; + group = peer->group; + for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer)) + { + if (! peer->af_group[afi][safi]) + continue; - SET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX); - peer->pmax[afi][safi] = max; - peer->pmax_threshold[afi][safi] = threshold; - peer->pmax_restart[afi][safi] = restart; - if (warning) - SET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX_WARNING); - else - UNSET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX_WARNING); + SET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX); + peer->pmax[afi][safi] = max; + peer->pmax_threshold[afi][safi] = threshold; + peer->pmax_restart[afi][safi] = restart; + if (warning) + SET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX_WARNING); + else + UNSET_FLAG (peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX_WARNING); + + if ((peer->status == Established) && (peer->afc[afi][safi])) + bgp_maximum_prefix_overflow (peer, afi, safi, 1); + } } + else + { + if ((peer->status == Established) && (peer->afc[afi][safi])) + bgp_maximum_prefix_overflow (peer, afi, safi, 1); + } + return 0; } @@ -4364,7 +4595,7 @@ peer_maximum_prefix_unset (struct peer *peer, afi_t afi, safi_t safi) } return 0; } - + /* Set # of hops between us and BGP peer. */ int peer_ttl_security_hops_set (struct peer *peer, int gtsm_hops) @@ -4372,64 +4603,34 @@ 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 (peer) == BGP_PEER_IBGP) - return BGP_ERR_NO_IBGP_WITH_TTLHACK; + if (peer->ttl != 0) + return BGP_ERR_NO_EBGP_MULTIHOP_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 - and all peer-group members for any trace of ebgp-multihop configuration - before actually applying the ttl-security rules. Cisco really made a - 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 (peer_sort (peer1) == BGP_PEER_IBGP) - continue; + if (CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) + { + group = peer->group; + for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer1)) + { + if (peer1->ttl != 0) + 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; - } - peer->gtsm_hops = gtsm_hops; if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) { - if (peer->fd >= 0 && peer_sort (peer) != BGP_PEER_IBGP) - sockopt_minttl (peer->su.sa.sa_family, peer->fd, MAXTTL + 1 - gtsm_hops); + bgp_set_socket_ttl (peer, peer->fd); } else { group = peer->group; for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer)) { - if (peer_sort (peer) == BGP_PEER_IBGP) - continue; - - peer->gtsm_hops = group->conf->gtsm_hops; + peer->gtsm_hops = gtsm_hops; /* Change setting of existing peer * established then change value (may break connectivity) @@ -4438,9 +4639,7 @@ peer_ttl_security_hops_set (struct peer *peer, int gtsm_hops) */ if (peer->status == Established) { - if (peer->fd >= 0 && peer->gtsm_hops != 0) - sockopt_minttl (peer->su.sa.sa_family, peer->fd, - MAXTTL + 1 - peer->gtsm_hops); + bgp_set_socket_ttl (peer, peer->fd); } else if (peer->status < Established) { @@ -4454,48 +4653,6 @@ peer_ttl_security_hops_set (struct peer *peer, int gtsm_hops) return 0; } -int -peer_ttl_security_hops_unset (struct peer *peer) -{ - struct peer_group *group; - struct listnode *node, *nnode; - struct peer *opeer; - - zlog_debug ("peer_ttl_security_hops_unset: set gtsm_hops to zero for %s", peer->host); - - if (peer_sort (peer) == 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; - else - peer->gtsm_hops = 0; - - opeer = peer; - if (! CHECK_FLAG (peer->sflags, PEER_STATUS_GROUP)) - { - if (peer->fd >= 0 && peer_sort (peer) != BGP_PEER_IBGP) - sockopt_minttl (peer->su.sa.sa_family, peer->fd, 0); - } - else - { - group = peer->group; - for (ALL_LIST_ELEMENTS (group->peer, node, nnode, peer)) - { - if (peer_sort (peer) == BGP_PEER_IBGP) - continue; - - peer->gtsm_hops = 0; - - if (peer->fd >= 0) - sockopt_minttl (peer->su.sa.sa_family, peer->fd, 0); - } - } - - return peer_ebgp_multihop_unset (opeer); -} - int peer_clear (struct peer *peer) { @@ -4516,7 +4673,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 @@ -4535,6 +4692,8 @@ peer_clear_soft (struct peer *peer, afi_t afi, safi_t safi, if (! peer->afc[afi][safi]) return BGP_ERR_AF_UNCONFIGURED; + peer->rtt = sockopt_tcp_rtt (peer->fd); + if (stype == BGP_CLEAR_SOFT_RSCLIENT) { if (! CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_RSERVER_CLIENT)) @@ -4600,7 +4759,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 * @@ -4629,10 +4788,11 @@ peer_uptime (time_t uptime2, char *buf, size_t len) uptime1 = bgp_clock (); uptime1 -= uptime2; tm = gmtime (&uptime1); - + /* Making formatted timer strings. */ #define ONE_DAY_SECOND 60*60*24 -#define ONE_WEEK_SECOND 60*60*24*7 +#define ONE_WEEK_SECOND ONE_DAY_SECOND*7 +#define ONE_YEAR_SECOND ONE_DAY_SECOND*365 if (uptime1 < ONE_DAY_SECOND) snprintf (buf, len, "%02d:%02d:%02d", @@ -4640,12 +4800,16 @@ peer_uptime (time_t uptime2, char *buf, size_t len) else if (uptime1 < ONE_WEEK_SECOND) snprintf (buf, len, "%dd%02dh%02dm", tm->tm_yday, tm->tm_hour, tm->tm_min); - else + else if (uptime1 < ONE_YEAR_SECOND) snprintf (buf, len, "%02dw%dd%02dh", tm->tm_yday/7, tm->tm_yday - ((tm->tm_yday/7) * 7), tm->tm_hour); + else + snprintf (buf, len, "%02dy%02dw%dd", + tm->tm_year - 70, tm->tm_yday/7, + tm->tm_yday - ((tm->tm_yday/7) * 7)); return buf; } - + static void bgp_config_write_filter (struct vty *vty, struct peer *peer, afi_t afi, safi_t safi) @@ -4757,10 +4921,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) @@ -4797,19 +4963,13 @@ bgp_config_write_peer (struct vty *vty, struct bgp *bgp, ! CHECK_FLAG (g_peer->flags, PEER_FLAG_PASSIVE)) vty_out (vty, " neighbor %s passive%s", addr, VTY_NEWLINE); - /* EBGP multihop. */ - if (peer_sort (peer) != BGP_PEER_IBGP && peer->ttl != 1 && - !(peer->gtsm_hops != 0 && peer->ttl == MAXTTL)) - if (! peer_group_active (peer) || - g_peer->ttl != peer->ttl) - vty_out (vty, " neighbor %s ebgp-multihop %d%s", addr, peer->ttl, - VTY_NEWLINE); - - /* ttl-security hops */ - if (peer_sort (peer) != BGP_PEER_IBGP && 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); + /* TTL option */ + if (peer->gtsm_hops && ! peer_group_active (peer)) + vty_out (vty, " neighbor %s ttl-security hops %d%s", addr, + peer->gtsm_hops, VTY_NEWLINE); + else if (peer->ttl && ! peer_group_active (peer)) + vty_out (vty, " neighbor %s ebgp-multihop %d%s", addr, peer->ttl, + VTY_NEWLINE); /* disable-connected-check. */ if (CHECK_FLAG (peer->flags, PEER_FLAG_DISABLE_CONNECTED_CHECK)) @@ -4832,7 +4992,8 @@ bgp_config_write_peer (struct vty *vty, struct bgp *bgp, VTY_NEWLINE); /* advertisement-interval */ - if (CHECK_FLAG (peer->config, PEER_CONFIG_ROUTEADV)) + if (CHECK_FLAG (peer->config, PEER_CONFIG_ROUTEADV) && + ! peer_group_active (peer)) vty_out (vty, " neighbor %s advertisement-interval %d%s", addr, peer->v_routeadv, VTY_NEWLINE); @@ -4842,7 +5003,8 @@ bgp_config_write_peer (struct vty *vty, struct bgp *bgp, vty_out (vty, " neighbor %s timers %d %d%s", addr, peer->keepalive, peer->holdtime, VTY_NEWLINE); - if (CHECK_FLAG (peer->config, PEER_CONFIG_CONNECT)) + if (CHECK_FLAG (peer->config, PEER_CONFIG_CONNECT) && + ! peer_group_active (peer)) vty_out (vty, " neighbor %s timers connect %d%s", addr, peer->connect, VTY_NEWLINE); @@ -4881,7 +5043,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)) { @@ -4936,7 +5098,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) @@ -4950,23 +5114,31 @@ bgp_config_write_peer (struct vty *vty, struct bgp *bgp, if (bgp_option_check (BGP_OPT_CONFIG_CISCO)) { if (peer_af_flag_check (peer, afi, safi, PEER_FLAG_SEND_COMMUNITY) - && peer_af_flag_check (peer, afi, safi, PEER_FLAG_SEND_EXT_COMMUNITY)) - vty_out (vty, " neighbor %s send-community both%s", addr, VTY_NEWLINE); + && peer_af_flag_check (peer, afi, safi, PEER_FLAG_SEND_EXT_COMMUNITY) + && peer_af_flag_check(peer, afi, safi, PEER_FLAG_SEND_LARGE_COMMUNITY)) + vty_out (vty, " neighbor %s send-community all%s", addr, VTY_NEWLINE); else if (peer_af_flag_check (peer, afi, safi, PEER_FLAG_SEND_EXT_COMMUNITY)) vty_out (vty, " neighbor %s send-community extended%s", addr, VTY_NEWLINE); + else if (peer_af_flag_check (peer, afi, safi, PEER_FLAG_SEND_LARGE_COMMUNITY)) + vty_out (vty, " neighbor %s send-community large%s", + addr, VTY_NEWLINE); else if (peer_af_flag_check (peer, afi, safi, PEER_FLAG_SEND_COMMUNITY)) vty_out (vty, " neighbor %s send-community%s", addr, VTY_NEWLINE); } else { if (! peer_af_flag_check (peer, afi, safi, PEER_FLAG_SEND_COMMUNITY) - && ! peer_af_flag_check (peer, afi, safi, PEER_FLAG_SEND_EXT_COMMUNITY)) - vty_out (vty, " no neighbor %s send-community both%s", + && ! peer_af_flag_check (peer, afi, safi, PEER_FLAG_SEND_EXT_COMMUNITY) + && ! peer_af_flag_check (peer, afi, safi, PEER_FLAG_SEND_LARGE_COMMUNITY)) + vty_out (vty, " no neighbor %s send-community all%s", addr, VTY_NEWLINE); else if (! peer_af_flag_check (peer, afi, safi, PEER_FLAG_SEND_EXT_COMMUNITY)) vty_out (vty, " no neighbor %s send-community extended%s", addr, VTY_NEWLINE); + else if (! peer_af_flag_check (peer, afi, safi, PEER_FLAG_SEND_LARGE_COMMUNITY)) + vty_out (vty, " no neighbor %s send-community large%s", + addr, VTY_NEWLINE); else if (! peer_af_flag_check (peer, afi, safi, PEER_FLAG_SEND_COMMUNITY)) vty_out (vty, " no neighbor %s send-community%s", addr, VTY_NEWLINE); @@ -5073,14 +5245,22 @@ bgp_config_write_family_header (struct vty *vty, afi_t afi, safi_t safi, if (safi == SAFI_MULTICAST) vty_out (vty, "ipv4 multicast"); else if (safi == SAFI_MPLS_VPN) - vty_out (vty, "vpnv4 unicast"); + vty_out (vty, "vpnv4"); + else if (safi == SAFI_ENCAP) + vty_out (vty, "encap"); } else if (afi == AFI_IP6) { - vty_out (vty, "ipv6"); - - if (safi == SAFI_MULTICAST) - vty_out (vty, " multicast"); + if (safi == SAFI_MPLS_VPN) + vty_out (vty, "vpnv6"); + else if (safi == SAFI_ENCAP) + vty_out (vty, "encapv6"); + else + { + vty_out (vty, "ipv6"); + if (safi == SAFI_MULTICAST) + vty_out (vty, " multicast"); + } } vty_out (vty, "%s", VTY_NEWLINE); @@ -5121,6 +5301,11 @@ bgp_config_write_family (struct vty *vty, struct bgp *bgp, afi_t afi, } } } + + bgp_config_write_maxpaths (vty, bgp, afi, safi, &write); + + bgp_config_write_distance (vty, bgp, afi, safi, &write); + if (write) vty_out (vty, " exit-address-family%s", VTY_NEWLINE); @@ -5181,8 +5366,8 @@ bgp_config_write (struct vty *vty) VTY_NEWLINE); /* BGP log-neighbor-changes. */ - if (bgp_flag_check (bgp, BGP_FLAG_LOG_NEIGHBOR_CHANGES)) - vty_out (vty, " bgp log-neighbor-changes%s", VTY_NEWLINE); + if (!bgp_flag_check (bgp, BGP_FLAG_LOG_NEIGHBOR_CHANGES)) + vty_out (vty, " no bgp log-neighbor-changes%s", VTY_NEWLINE); /* BGP configuration. */ if (bgp_flag_check (bgp, BGP_FLAG_ALWAYS_COMPARE_MED)) @@ -5236,6 +5421,9 @@ bgp_config_write (struct vty *vty) if (bgp->stalepath_time != BGP_DEFAULT_STALEPATH_TIME) vty_out (vty, " bgp graceful-restart stalepath-time %d%s", bgp->stalepath_time, VTY_NEWLINE); + if (bgp->restart_time != BGP_DEFAULT_RESTART_TIME) + vty_out (vty, " bgp graceful-restart restart-time %d%s", + bgp->restart_time, VTY_NEWLINE); if (bgp_flag_check (bgp, BGP_FLAG_GRACEFUL_RESTART)) vty_out (vty, " bgp graceful-restart%s", VTY_NEWLINE); @@ -5244,6 +5432,13 @@ 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_RR_ALLOW_OUTBOUND_POLICY)) { + vty_out (vty, " bgp route-reflector allow-outbound-policy%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) @@ -5261,9 +5456,6 @@ bgp_config_write (struct vty *vty) if (bgp_flag_check (bgp, BGP_FLAG_IMPORT_CHECK)) vty_out (vty, " bgp network import-check%s", VTY_NEWLINE); - /* BGP scan interval. */ - bgp_config_write_scan_time (vty); - /* BGP flag dampening. */ if (CHECK_FLAG (bgp->af_flags[AFI_IP][SAFI_UNICAST], BGP_CONFIG_DAMPENING)) @@ -5294,8 +5486,11 @@ bgp_config_write (struct vty *vty) bgp_config_write_peer (vty, bgp, peer, AFI_IP, SAFI_UNICAST); } + /* maximum-paths */ + bgp_config_write_maxpaths (vty, bgp, AFI_IP, SAFI_UNICAST, &write); + /* Distance configuration. */ - bgp_config_write_distance (vty, bgp); + bgp_config_write_distance (vty, bgp, AFI_IP, SAFI_UNICAST, &write); /* No auto-summary */ if (bgp_option_check (BGP_OPT_CONFIG_CISCO)) @@ -5307,12 +5502,23 @@ bgp_config_write (struct vty *vty) /* IPv4 VPN configuration. */ write += bgp_config_write_family (vty, bgp, AFI_IP, SAFI_MPLS_VPN); + /* ENCAPv4 configuration. */ + write += bgp_config_write_family (vty, bgp, AFI_IP, SAFI_ENCAP); + /* IPv6 unicast configuration. */ write += bgp_config_write_family (vty, bgp, AFI_IP6, SAFI_UNICAST); /* IPv6 multicast configuration. */ write += bgp_config_write_family (vty, bgp, AFI_IP6, SAFI_MULTICAST); + /* IPv6 VPN configuration. */ + write += bgp_config_write_family (vty, bgp, AFI_IP6, SAFI_MPLS_VPN); + + /* ENCAPv6 configuration. */ + write += bgp_config_write_family (vty, bgp, AFI_IP6, SAFI_ENCAP); + + vty_out (vty, " exit%s", VTY_NEWLINE); + write++; } return write; @@ -5331,15 +5537,19 @@ bgp_master_init (void) bm->start_time = bgp_clock (); } - + void bgp_init (void) { - /* BGP VTY commands installation. */ - bgp_vty_init (); + + /* allocates some vital data structures used by peer commands in vty_init */ + bgp_scan_init (); /* Init zebra. */ - bgp_zebra_init (); + bgp_zebra_init (bm->master); + + /* BGP VTY commands installation. */ + bgp_vty_init (); /* BGP inits. */ bgp_attr_init (); @@ -5347,8 +5557,10 @@ bgp_init (void) bgp_dump_init (); bgp_route_init (); bgp_route_map_init (); - bgp_scan_init (); + bgp_address_init (); + bgp_scan_vty_init(); bgp_mplsvpn_init (); + bgp_encap_init (); /* Access list initialize. */ access_list_init (); diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index 892e7decc..03df2f6f1 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -59,6 +59,7 @@ struct bgp_master #define BGP_OPT_NO_FIB (1 << 0) #define BGP_OPT_MULTIPLE_INSTANCE (1 << 1) #define BGP_OPT_CONFIG_CISCO (1 << 2) +#define BGP_OPT_NO_LISTEN (1 << 3) }; /* BGP instance structure. */ @@ -103,8 +104,10 @@ struct bgp as_t *confed_peers; int confed_peers_cnt; + struct thread *t_startup; + /* BGP flags. */ - u_int16_t flags; + u_int32_t flags; #define BGP_FLAG_ALWAYS_COMPARE_MED (1 << 0) #define BGP_FLAG_DETERMINISTIC_MED (1 << 1) #define BGP_FLAG_MED_MISSING_AS_WORST (1 << 2) @@ -119,6 +122,9 @@ 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) +#define BGP_FLAG_DELETING (1 << 15) +#define BGP_FLAG_RR_ALLOW_OUTBOUND_POLICY (1 << 16) /* BGP Per AF flags */ u_int16_t af_flags[AFI_MAX][SAFI_MAX]; @@ -151,6 +157,11 @@ struct bgp u_char distance_ebgp; u_char distance_ibgp; u_char distance_local; + + /* BGP ipv6 distance configuration. */ + u_char ipv6_distance_ebgp; + u_char ipv6_distance_ibgp; + u_char ipv6_distance_local; /* BGP default local-preference. */ u_int32_t default_local_pref; @@ -162,6 +173,12 @@ struct bgp /* BGP graceful restart */ u_int32_t restart_time; u_int32_t stalepath_time; + + /* Maximum-paths configuration */ + struct bgp_maxpaths_cfg { + u_int16_t maxpaths_ebgp; + u_int16_t maxpaths_ibgp; + } maxpaths[AFI_MAX][SAFI_MAX]; }; /* BGP peer-group support. */ @@ -194,10 +211,8 @@ struct bgp_nexthop { struct interface *ifp; struct in_addr v4; -#ifdef HAVE_IPV6 struct in6_addr v6_global; struct in6_addr v6_local; -#endif /* HAVE_IPV6 */ }; /* BGP router distinguisher value. */ @@ -253,6 +268,18 @@ struct bgp_filter } usmap; }; +/* IBGP/EBGP identifier. We also have a CONFED peer, which is to say, + a peer who's AS is part of our Confederation. */ +typedef enum +{ + BGP_PEER_IBGP = 1, + BGP_PEER_EBGP, + BGP_PEER_INTERNAL, + BGP_PEER_CONFED, +} bgp_peer_sort_t; + +#define BGP_MAX_PACKET_SIZE_OVERFLOW 1024 + /* BGP neighbor structure. */ struct peer { @@ -276,6 +303,8 @@ struct peer /* Peer's local AS number. */ as_t local_as; + bgp_peer_sort_t sort; + /* Peer's Change local AS number. */ as_t change_local_as; @@ -293,6 +322,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; @@ -303,6 +338,7 @@ struct peer /* Peer information */ int fd; /* File descriptor */ int ttl; /* TTL of TCP connection to the peer. */ + int rtt; /* Estimated round-trip-time from TCP_INFO */ int gtsm_hops; /* minimum hopcount to peer */ char *desc; /* Description of the peer. */ unsigned short port; /* Destination port for peer */ @@ -312,7 +348,7 @@ struct peer time_t readtime; /* Last read time */ time_t resettime; /* Last reset time */ - unsigned int ifindex; /* ifindex of the BGP connection. */ + ifindex_t ifindex; /* ifindex of the BGP connection. */ char *ifname; /* bind interface name. */ char *update_if; union sockunion *update_source; @@ -340,6 +376,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]; @@ -362,6 +400,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]; @@ -385,6 +424,8 @@ 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 */ +#define PEER_FLAG_SEND_LARGE_COMMUNITY (1 << 18) /* Send large Communities */ /* MD5 password */ char *password; @@ -401,7 +442,7 @@ struct peer #define PEER_STATUS_ACCEPT_PEER (1 << 0) /* accept peer */ #define PEER_STATUS_PREFIX_OVERFLOW (1 << 1) /* prefix-overflow */ #define PEER_STATUS_CAPABILITY_OPEN (1 << 2) /* capability open send */ -#define PEER_STATUS_HAVE_ACCEPT (1 << 3) /* accept peer's parent */ +#define PEER_STATUS_OPEN_DEFERRED (1 << 3) /* deferred to open_receive */ #define PEER_STATUS_GROUP (1 << 4) /* peer-group conf */ #define PEER_STATUS_NSF_MODE (1 << 5) /* NSF aware peer */ #define PEER_STATUS_NSF_WAIT (1 << 6) /* wait comeback peer */ @@ -433,7 +474,6 @@ struct peer u_int32_t v_connect; u_int32_t v_holdtime; u_int32_t v_keepalive; - u_int32_t v_asorig; u_int32_t v_routeadv; u_int32_t v_pmax_restart; u_int32_t v_gr_restart; @@ -445,7 +485,6 @@ struct peer struct thread *t_connect; struct thread *t_holdtime; struct thread *t_keepalive; - struct thread *t_asorig; struct thread *t_routeadv; struct thread *t_pmax_restart; struct thread *t_gr_restart; @@ -616,6 +655,8 @@ struct bgp_nlri #define BGP_ATTR_AS4_PATH 17 #define BGP_ATTR_AS4_AGGREGATOR 18 #define BGP_ATTR_AS_PATHLIMIT 21 +#define BGP_ATTR_ENCAP 23 +#define BGP_ATTR_LARGE_COMMUNITIES 32 /* BGP update origin. */ #define BGP_ORIGIN_IGP 0 @@ -641,6 +682,7 @@ struct bgp_nlri #define BGP_NOTIFY_HEADER_MAX 4 /* BGP_NOTIFY_OPEN_ERR sub codes. */ +#define BGP_NOTIFY_OPEN_UNSPECIFIC 0 #define BGP_NOTIFY_OPEN_UNSUP_VERSION 1 #define BGP_NOTIFY_OPEN_BAD_PEER_AS 2 #define BGP_NOTIFY_OPEN_BAD_BGP_IDENT 3 @@ -707,18 +749,16 @@ struct bgp_nlri #define Receive_UPDATE_message 12 #define Receive_NOTIFICATION_message 13 #define Clearing_Completed 14 -#define BGP_EVENTS_MAX 15 +#define BGP_Stop_with_error 15 +#define BGP_EVENTS_MAX 16 /* BGP timers default value. */ -#define BGP_INIT_START_TIMER 5 -#define BGP_ERROR_START_TIMER 30 +#define BGP_INIT_START_TIMER 1 #define BGP_DEFAULT_HOLDTIME 180 #define BGP_DEFAULT_KEEPALIVE 60 -#define BGP_DEFAULT_ASORIGINATE 15 -#define BGP_DEFAULT_EBGP_ROUTEADV 30 -#define BGP_DEFAULT_IBGP_ROUTEADV 5 -#define BGP_CLEAR_CONNECT_RETRY 20 -#define BGP_DEFAULT_CONNECT_RETRY 120 +#define BGP_DEFAULT_EBGP_ROUTEADV 3 +#define BGP_DEFAULT_IBGP_ROUTEADV 1 +#define BGP_DEFAULT_CONNECT_RETRY 5 /* BGP default local preference. */ #define BGP_DEFAULT_LOCAL_PREF 100 @@ -743,16 +783,6 @@ struct bgp_nlri /* Check AS path loop when we send NLRI. */ /* #define BGP_SEND_ASPATH_CHECK */ -/* IBGP/EBGP identifier. We also have a CONFED peer, which is to say, - a peer who's AS is part of our Confederation. */ -enum -{ - BGP_PEER_IBGP, - BGP_PEER_EBGP, - BGP_PEER_INTERNAL, - BGP_PEER_CONFED -}; - /* Flag for peer_clear_soft(). */ enum bgp_clear_type { @@ -767,9 +797,8 @@ enum bgp_clear_type /* Macros. */ #define BGP_INPUT(P) ((P)->ibuf) #define BGP_INPUT_PNT(P) (STREAM_PNT(BGP_INPUT(P))) - -/* Count prefix size from mask length */ -#define PSIZE(a) (((a) + 7) / (8)) +#define BGP_IS_VALID_STATE_FOR_NOTIF(S)\ + (((S) == OpenSent) || ((S) == OpenConfirm) || ((S) == Established)) /* BGP error codes. */ #define BGP_SUCCESS 0 @@ -804,12 +833,11 @@ enum bgp_clear_type #define BGP_ERR_TCPSIG_FAILED -29 #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 -32 +#define BGP_ERR_MAX -33 extern struct bgp_master *bm; -extern struct thread_master *master; - /* Prototypes. */ extern void bgp_terminate (void); extern void bgp_reset (void); @@ -825,16 +853,25 @@ extern struct peer_group *peer_group_lookup (struct bgp *, const char *); extern struct peer_group *peer_group_get (struct bgp *, const char *); extern struct peer *peer_lookup_with_open (union sockunion *, as_t, struct in_addr *, int *); -extern struct peer *peer_lock (struct peer *); -extern struct peer *peer_unlock (struct peer *); -extern int peer_sort (struct peer *peer); + +/* + * Peers are incredibly easy to memory leak + * due to the various ways that they are actually used + * Provide some functionality to debug locks and unlocks + */ +extern struct peer *peer_lock_with_caller(const char *, struct peer *); +extern struct peer *peer_unlock_with_caller(const char *, struct peer *); +#define peer_unlock(A) peer_unlock_with_caller(__FUNCTION__, (A)) +#define peer_lock(B) peer_lock_with_caller(__FUNCTION__, (B)) + +extern bgp_peer_sort_t peer_sort (struct peer *peer); extern int peer_active (struct peer *); extern int peer_active_nego (struct peer *); 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); @@ -854,7 +891,8 @@ extern int bgp_flag_check (struct bgp *, int); extern void bgp_lock (struct bgp *); extern void bgp_unlock (struct bgp *); -extern int bgp_router_id_set (struct bgp *, struct in_addr *); +extern void bgp_router_id_zebra_bump (void); +extern int bgp_router_id_static_set (struct bgp *, struct in_addr); extern int bgp_cluster_id_set (struct bgp *, struct in_addr *); extern int bgp_cluster_id_unset (struct bgp *); @@ -866,7 +904,7 @@ extern int bgp_confederation_peers_check (struct bgp *, as_t); extern int bgp_confederation_peers_add (struct bgp *, as_t); extern int bgp_confederation_peers_remove (struct bgp *, as_t); -extern int bgp_timers_set (struct bgp *, u_int32_t, u_int32_t); +extern int bgp_timers_set (struct bgp *, u_int32_t keepalive, u_int32_t holdtime); extern int bgp_timers_unset (struct bgp *); extern int bgp_default_local_preference_set (struct bgp *, u_int32_t); @@ -876,12 +914,14 @@ extern int peer_rsclient_active (struct peer *); extern int peer_remote_as (struct bgp *, union sockunion *, as_t *, afi_t, safi_t); extern int peer_group_remote_as (struct bgp *, const char *, as_t *); +extern int peer_ttl (struct peer *peer); extern int peer_delete (struct peer *peer); extern int peer_group_delete (struct peer_group *); extern int peer_group_remote_as_delete (struct peer_group *); extern int peer_activate (struct peer *, afi_t, safi_t); extern int peer_deactivate (struct peer *, afi_t, safi_t); +extern int peer_afc_set (struct peer *, afi_t, safi_t, int); extern int peer_group_bind (struct bgp *, union sockunion *, struct peer_group *, afi_t, safi_t, as_t *); @@ -896,13 +936,12 @@ extern int peer_af_flag_unset (struct peer *, afi_t, safi_t, u_int32_t); extern int peer_af_flag_check (struct peer *, afi_t, safi_t, u_int32_t); extern int peer_ebgp_multihop_set (struct peer *, int); -extern int peer_ebgp_multihop_unset (struct peer *); -extern int peer_description_set (struct peer *, char *); +extern int peer_description_set (struct peer *, const char *); extern int peer_description_unset (struct peer *); extern int peer_update_source_if_set (struct peer *, const char *); -extern int peer_update_source_addr_set (struct peer *, union sockunion *); +extern int peer_update_source_addr_set (struct peer *, const union sockunion *); extern int peer_update_source_unset (struct peer *); extern int peer_default_originate_set (struct peer *, afi_t, safi_t, const char *); @@ -914,7 +953,7 @@ extern int peer_port_unset (struct peer *); extern int peer_weight_set (struct peer *, u_int16_t); extern int peer_weight_unset (struct peer *); -extern int peer_timers_set (struct peer *, u_int32_t, u_int32_t); +extern int peer_timers_set (struct peer *, u_int32_t keepalive, u_int32_t holdtime); extern int peer_timers_unset (struct peer *); extern int peer_timers_connect_set (struct peer *, u_int32_t); @@ -932,7 +971,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 *); @@ -958,6 +997,6 @@ extern int peer_clear (struct peer *); extern int peer_clear_soft (struct peer *, afi_t, safi_t, enum bgp_clear_type); extern int peer_ttl_security_hops_set (struct peer *, int); -extern int peer_ttl_security_hops_unset (struct peer *); +extern void bgp_scan_finish (void); #endif /* _QUAGGA_BGPD_H */ diff --git a/buildtest.sh b/buildtest.sh new file mode 100755 index 000000000..04fc2cc96 --- /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-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/common.am b/common.am new file mode 100644 index 000000000..ac7a3230d --- /dev/null +++ b/common.am @@ -0,0 +1,41 @@ +# +# Automake fragment intended to be shared by Makefile.am files in the +# tree. +# + +if HAVE_PROTOBUF + +# Uncomment to use an non-system version of libprotobuf-c. +# +# Q_PROTOBUF_C_CLIENT_INCLUDES = -I$(top_srcdir)/third-party/protobuf-c/src +# Q_PROTOBUF_C_CLIENT_LDOPTS = $(top_builddir)/third-party/protobuf-c/src/libprotobuf-c.la + +Q_PROTOBUF_C_CLIENT_INCLUDES= +Q_PROTOBUF_C_CLIENT_LDOPTS=-lprotobuf-c + +Q_PROTOC=protoc +Q_PROTOC_C=protoc-c + +Q_PROTOBUF_CFILES = $(filter %.pb-c.c,$(SOURCES)) + +Q_PROTOBUF_SRCS = $(Q_PROTOBUF_CFILES) $(Q_PROTOBUF_HFILES) + +# Rules +%.pb.h: %.proto + $(Q_PROTOC) $(PROTOBUF_INCLUDES) --cpp_out=$(top_srcdir) $(top_srcdir)/$(PROTOBUF_PACKAGE)/$^ + +%.pb-c.c %.pb-c.h: %.proto + $(Q_PROTOC_C) $(PROTOBUF_INCLUDES) --c_out=$(top_srcdir) $(top_srcdir)/$(PROTOBUF_PACKAGE)/$^ + +# +# Information about how to link to various libraries. +# +Q_QUAGGA_PB_CLIENT_LDOPTS = $(top_srcdir)/qpb/libquagga_pb.la $(Q_PROTOBUF_C_CLIENT_LDOPTS) + +Q_FPM_PB_CLIENT_LDOPTS = $(top_srcdir)/fpm/libfpm_pb.la $(Q_QUAGGA_PB_CLIENT_LDOPTS) + +endif # HAVE_PROTOBUF + +Q_CLEANFILES = $(Q_PROTOBUF_SRCS) + +Q_BUILT_SRCS = $(Q_PROTOBUF_SRCS) diff --git a/configure.ac b/configure.ac index 4fe70e150..ae292f118 100755 --- a/configure.ac +++ b/configure.ac @@ -5,9 +5,11 @@ ## Copyright (c) 1996, 97, 98, 99, 2000 Kunihiro Ishiguro ## Portions Copyright (c) 2003 Paul Jakma ## -AC_PREREQ(2.53) +AC_PREREQ(2.60) -AC_INIT(Quagga, 0.99.20, [https://bugzilla.quagga.net]) +AC_INIT(Quagga, 1.2.2, [https://bugzilla.quagga.net]) +CONFIG_ARGS="$*" +AC_SUBST(CONFIG_ARGS) AC_CONFIG_SRCDIR(lib/zebra.h) AC_CONFIG_MACRO_DIR([m4]) @@ -18,9 +20,15 @@ AC_CANONICAL_BUILD() AC_CANONICAL_HOST() AC_CANONICAL_TARGET() -AM_INIT_AUTOMAKE(1.6) -AM_CONFIG_HEADER(config.h) +# Disable portability warnings -- our automake code (in particular +# common.am) uses some constructs specific to gmake. +AM_INIT_AUTOMAKE([1.6 -Wno-portability]) +m4_ifndef([AM_SILENT_RULES], [m4_define([AM_SILENT_RULES],[])]) +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 +39,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 +49,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 @@ -66,13 +74,29 @@ AC_LANG([C]) AC_PROG_CC AC_PROG_CPP AM_PROG_CC_C_O +AC_PROG_RANLIB AC_PROG_EGREP +PKG_PROG_PKG_CONFIG +AC_PROG_CC_C99 + +dnl libtool prereq. +AC_USE_SYSTEM_EXTENSIONS + +dnl ------- +dnl libtool +dnl ------- +LT_INIT + +dnl create libtool now, so we can test version below for +dnl fstack-protector-strong +LT_OUTPUT dnl autoconf 2.59 appears not to support AC_PROG_SED dnl AC_PROG_SED AC_CHECK_PROG([SED],[sed],[sed],[/bin/false]) -dnl pdflatex and latexmk are needed to build HACKING.pdf +dnl Check for pdflatex and latexmk, in case someone wants to build +dnl PDFs from TeX (as used to be case for HACKING) AC_CHECK_PROG([PDFLATEX],[pdflatex],[pdflatex],[/bin/false]) AC_CHECK_PROG([LATEXMK],[latexmk],[latexmk],[/bin/false]) if test "x$PDFLATEX" = "x/bin/false" -o "x$LATEXMK" = "x/bin/false"; then @@ -81,21 +105,16 @@ else HAVE_LATEX=true fi AM_CONDITIONAL([HAVE_LATEX], [test "x$HAVE_LATEX" = "xtrue"]) - -dnl ------------------------------------------------------------------ -dnl Intel compiler check. Although Intel tries really hard to make icc -dnl look like gcc, there are some differences. It's very verbose with -dnl -Wall and it doesn't support the individual -W options. -dnl ------------------------------------------------------------------ -if test "x${GCC}" = "xyes" ; then - COMPILER="GCC" - AC_MSG_CHECKING([whether we are using the Intel compiler]) - AC_EGREP_CPP([^__INTEL_COMPILER], [__INTEL_COMPILER], - [AC_MSG_RESULT([no])], - [COMPILER="ICC" - AC_MSG_RESULT([yes])] - ) +dnl for making HACKING.pdf from HACKING.md using pandoc +AC_CHECK_PROG([PANDOC],[pandoc],[pandoc],[/bin/false]) +if test "x$PDFLATEX" = "x/bin/false" -o "x$PANDOC" = "x/bin/false"; then + AC_MSG_WARN([Will not be able to make PDF versions of MD documents]) else + HAVE_PANDOC=true +fi +AM_CONDITIONAL([HAVE_PANDOC], [test "x$HAVE_PANDOC" = "xtrue"]) + +if test "x${GCC}" != "xyes" ; then AC_MSG_CHECKING([whether we are using SunPro compiler]) AC_EGREP_CPP([^__SUNPRO_C.*0x5(7|8|9)], ["__SUNPRO_C" __SUNPRO_C], [AC_MSG_RESULT([no])], @@ -111,47 +130,114 @@ dnl already, eg "-O2 -g" for gcc, "-g" for others dnl (Wall is gcc specific... have to make sure dnl gcc is being used before setting it) dnl -dnl Intel icc 8.0 also sets __GNUC__, -dnl but doesn't support all these fancy -W options. -dnl Intel compiler warnings we ignore: -dnl 279: controlling expression is constant. -dnl 869: parameter "xxx" was never referenced - to avoid massive warnings -dnl about "self", "vty", "argc" and "argv" never referenced in DEFUN -dnl macro. -dnl 981: operands are evaluated in unspecified order. -dnl dnl Sun Studio 10 / SunPro 5.7 is also supported, dnl so lets set some sane CFLAGS for it. dnl --------------------------------------------- -AC_MSG_CHECKING([whether to set a default CFLAGS]) +AC_DEFUN([AC_C_FLAG], [{ + AC_LANG_PUSH(C) + ac_c_flag_save="$CFLAGS" + CFLAGS="$CFLAGS $1" + AC_MSG_CHECKING([[whether $CC supports $1]]) + AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[]])], + [ + AC_MSG_RESULT([yes]) + m4_if([$3], [], [], [ + CFLAGS="$ac_c_flag_save" + $3 + ]) + ], [ + CFLAGS="$ac_c_flag_save" + AC_MSG_RESULT([no]) + $2 + ]) + AC_LANG_POP(C) + }]) + +AC_MSG_CHECKING([which default CFLAGS to set]) if test "x${cflags_specified}" = "x" ; then case ${COMPILER} in - "ICC") - CFLAGS="-Os -g -Wall -wd 279,869,981" - AC_MSG_RESULT([Intel default]) - ;; - "GCC") - CFLAGS="-Os -fno-omit-frame-pointer -g -std=gnu99 -Wall" - CFLAGS="${CFLAGS} -Wsign-compare -Wpointer-arith" - CFLAGS="${CFLAGS} -Wbad-function-cast -Wwrite-strings" - CFLAGS="${CFLAGS} -Wmissing-prototypes -Wmissing-declarations" - CFLAGS="${CFLAGS} -Wchar-subscripts -Wcast-qual" - # TODO: conditionally addd -Wpacked if handled - AC_MSG_RESULT([gcc default]) - ;; "SUNPRO") - CFLAGS="-xO4 -v -g -xspace -xcode=pic32 -xstrconst -xc99" - AC_MSG_RESULT([SunPro default]) - ;; + AC_MSG_RESULT([Sun Studio]) + AC_C_FLAG([-g]) + AC_C_FLAG([-xO4]) + AC_C_FLAG([-xspace]) + AC_C_FLAG([-xstrconst]) + AC_C_FLAG([-xc99]) + AC_C_FLAG([-errfmt]) + AC_C_FLAG([-xipo]) + dnl AC_C_FLAG([-xlinkopt=2]) SPARC only dnl + dnl AC_C_FLAG([-xcode=pic32])dnl + ;; *) - AC_MSG_RESULT([unknown compiler]) + AC_MSG_RESULT([autodetecting]) + + AC_C_FLAG([-diag-error 10006]) + AC_C_FLAG([-g]) + AC_C_FLAG([-Os], [ + AC_C_FLAG([-O2]) + ]) + dnl fstack-protector-strong gives __stack_chk_fail_local + dnl being an 'Undefined symbol' on OpenIndiana hipster, with gcc 6. + dnl gcc -shared is being used to do the link, however the error is + dnl from ld. Disable. An issue with libtool < 2.4.6 dropping the + dnl -fstack-protector-strong argument from the shared link. + AC_MSG_CHECKING([whether libtool can support fstack-protector]) + if test x"$(./libtool --version \ + | awk -F '[[ \t.]]' \ + 'NR == 1 { \ + if ($(NF-2) <= 2 && $(NF-1) <= 4 && $NF < 6) \ + print 0; \ + else print 1 \ + }')" = "x1" ; then + AC_MSG_RESULT([yes]) + AC_C_FLAG([-fstack-protector-strong]) + AC_C_FLAG([--param=ssp-buffer-size=4]) + else + AC_MSG_RESULT([no]) + AC_MSG_WARN([upgrade to libtool >= 2.4.6!]) + fi + AC_C_FLAG([-D_FORTIFY_SOURCE=2]) + AC_C_FLAG([-Wformat]) + AC_C_FLAG([-Wformat-security]) + AC_C_FLAG([-fpie]) + AC_C_FLAG([-fno-omit-frame-pointer]) + AC_C_FLAG([-Wall]) + AC_C_FLAG([-Wextra]) + AC_C_FLAG([-Wmissing-prototypes]) + AC_C_FLAG([-Wmissing-declarations]) + AC_C_FLAG([-Wpointer-arith]) + AC_C_FLAG([-Wbad-function-cast]) + AC_C_FLAG([-Wwrite-strings]) + if test x"${enable_gcc_ultra_verbose}" = x"yes" ; then + AC_C_FLAG([-Wcast-qual]) + AC_C_FLAG([-Wstrict-prototypes]) + AC_C_FLAG([-Wmissing-noreturn]) + AC_C_FLAG([-Wmissing-format-attribute]) + AC_C_FLAG([-Wunreachable-code]) + AC_C_FLAG([-Wpacked]) + AC_C_FLAG([-Wpadded]) + else + AC_C_FLAG([-Wno-unused-result]) + fi + AC_C_FLAG([-Wno-unused-parameter]) + AC_C_FLAG([-Wno-missing-field-initializers]) + # ICC emits a broken warning for const char *x = a ? "b" : "c"; + # for some reason the string consts get 'promoted' to char *, + # triggering a const to non-const conversion warning. + AC_C_FLAG([-diag-disable 3179]) ;; esac else AC_MSG_RESULT([CFLAGS supplied by user]) fi +if test x"${enable_werror}" = x"yes" ; then + WERROR="-Werror" +fi +AC_SUBST(WERROR) + dnl -------------- dnl Check programs dnl -------------- @@ -181,104 +267,102 @@ dnl System extensions dnl ----------------- AC_GNU_SOURCE -dnl ------- -dnl libtool -dnl ------- -AC_PROG_LIBTOOL dnl ---------------------- dnl Packages configuration 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([--disable-vtysh], [do not build integrated vty shell for Quagga])) AC_ARG_ENABLE(doc, -[ --disable-doc do not build docs]) + 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]) -AC_ARG_ENABLE(babeld, -[ --disable-babeld do not build babeld]) + AS_HELP_STRING([--disable-ospf6d], [do not build ospf6d])) +AC_ARG_ENABLE(nhrpd, + AS_HELP_STRING([--disable-nhrpd], [do not build nhrpd])) 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([--disable-pimd], [do not 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]) -AC_ARG_WITH(crypto, -[ --without-crypto do not use libcrypto in SNMP]) + AS_HELP_STRING([--disable-bgp-announce,], [turn off BGP route announcement])) AC_ARG_ENABLE(snmp, -[ --enable-snmp enable SNMP support]) + 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]) -AC_ARG_ENABLE(opaque-lsa, - AC_HELP_STRING([--disable-opaque-lsa],[do not build OSPF Opaque-LSA with OSPFAPI support (RFC2370)])) + AS_HELP_STRING([--enable-tcp-zebra], [enable TCP/IP socket connection between zebra and protocol daemon])) 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)]) -AC_ARG_ENABLE(ospf-te, - AC_HELP_STRING([--disable-ospf-te],[disable Traffic Engineering Extension to OSPF])) + AS_HELP_STRING([--disable-ospfclient], [do not build OSPFAPI client for OSPFAPI, + (this is the default if --disable-ospfapi is set)])) 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]) - -if test x"${enable_gcc_ultra_verbose}" = x"yes" ; then - CFLAGS="${CFLAGS} -W -Wcast-qual -Wstrict-prototypes" - CFLAGS="${CFLAGS} -Wmissing-declarations -Wmissing-noreturn" - CFLAGS="${CFLAGS} -Wmissing-format-attribute -Wunreachable-code" - CFLAGS="${CFLAGS} -Wpacked -Wpadded" -fi - -if test x"${enable_gcc_rdynamic}" = x"yes" ; then - LDFLAGS="${LDFLAGS} -rdynamic" + 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])) +AC_ARG_ENABLE(werror, + AS_HELP_STRING([--enable-werror], [enable -Werror (recommended for developers only)])) +AC_ARG_ENABLE([protobuf], + AS_HELP_STRING([--enable-protobuf], [Enable experimental protobuf support])) + +AC_ARG_ENABLE([dev_build], + AS_HELP_STRING([--enable-dev-build], [build for development])) + +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 @@ -289,26 +373,59 @@ 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 - AC_DEFINE(HAVE_TCP_ZEBRA,,Use TCP for zebra communication) +if test "x${enable_dev_build}" = "xyes"; then + AC_DEFINE(DEV_BUILD,,Build for development) +fi +AM_CONDITIONAL([DEV_BUILD], [test "x$enable_dev_build" = "xyes"]) + +# +# Logic for protobuf support. +# +if test "$enable_protobuf" = "yes"; then + have_protobuf=yes + + # Check for protoc-c + AC_CHECK_PROG([PROTOC_C], [protoc-c], [protoc-c], [/bin/false]) + if test "x$PROTOC_C" = "x/bin/false"; then + have_protobuf=no + else + found_protobuf_c=no + PKG_CHECK_MODULES([PROTOBUF_C], libprotobuf-c >= 0.14, + [found_protobuf_c=yes], + [AC_MSG_RESULT([pkg-config did not find libprotobuf-c])]) + + if test "x$found_protobuf_c" = "xyes"; then + LDFLAGS="$LDFLAGS $PROTOBUF_C_LIBS" + CFLAGS="$CFLAGS $PROTOBUF_C_CFLAGS" + else + AC_CHECK_HEADER([google/protobuf-c/protobuf-c.h], [], + [have_protobuf=no; AC_MSG_RESULT([Couldn't find google/protobuf-c.h])]) + fi + fi fi -if test "${enable_opaque_lsa}" != "no"; then - AC_DEFINE(HAVE_OPAQUE_LSA,,OSPF Opaque LSA) +# Fail if the user explicity enabled protobuf support and we couldn't +# find the compiler or libraries. +if test "x$have_protobuf" = "xno" && test "x$enable_protobuf" = "xyes"; then + AC_MSG_ERROR([Protobuf enabled explicitly but can't find libraries/tools]) fi -if test "${enable_ospf_te}" != "no"; then - AC_DEFINE(HAVE_OPAQUE_LSA,,OSPF Opaque LSA) - AC_DEFINE(HAVE_OSPF_TE,,OSPF TE) +if test "x$have_protobuf" = "xyes"; then + AC_DEFINE(HAVE_PROTOBUF,, protobuf) +fi + +AM_CONDITIONAL([HAVE_PROTOBUF], [test "x$have_protobuf" = "xyes"]) + +# +# End of logic for protobuf support. +# + +if test "${enable_tcp_zebra}" = "yes"; then + AC_DEFINE(HAVE_TCP_ZEBRA,,Use TCP for zebra communication) fi if test "${enable_linux24_tcp_md5}" = "yes"; then @@ -327,9 +444,9 @@ 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./topology" + ISIS_TOPOLOGY_INCLUDES="-I\$(srcdir)/topology" ISIS_TOPOLOGY_DIR="topology" ISIS_TOPOLOGY_LIB="./topology/libtopology.a" fi @@ -338,16 +455,22 @@ AC_SUBST(ISIS_TOPOLOGY_INCLUDES) AC_SUBST(ISIS_TOPOLOGY_DIR) AC_SUBST(ISIS_TOPOLOGY_LIB) -if test "${enable_user}" = "yes" || test x"${enable_user}" = x""; then - enable_user="quagga" -elif test "${enable_user}" = "no"; then - enable_user="root" +if test x"${enable_user}" = x"no"; then + enable_user="" +else + if test x"${enable_user}" = x"yes" || test x"${enable_user}" = x""; then + enable_user="quagga" + fi + AC_DEFINE_UNQUOTED(QUAGGA_USER, "${enable_user}", Quagga User) fi -if test "${enable_group}" = "yes" || test x"${enable_group}" = x""; then - enable_group="quagga" -elif test "${enable_group}" = "no"; then - enable_group="root" +if test x"${enable_group}" = x"no"; then + enable_group="" +else + if test x"${enable_group}" = x"yes" || test x"${enable_group}" = x""; then + enable_group="quagga" + fi + AC_DEFINE_UNQUOTED(QUAGGA_GROUP, "${enable_group}", Quagga Group) fi if test x"${enable_vty_group}" = x"yes" ; then @@ -360,8 +483,6 @@ fi AC_SUBST([enable_user]) AC_SUBST([enable_group]) AC_SUBST([enable_vty_group]) -AC_DEFINE_UNQUOTED(QUAGGA_USER, "${enable_user}", Quagga User) -AC_DEFINE_UNQUOTED(QUAGGA_GROUP, "${enable_group}", Quagga Group) enable_configfile_mask=${enable_configfile_mask:-0600} AC_DEFINE_UNQUOTED(CONFIGFILE_MASK, ${enable_configfile_mask}, Mask for config files) @@ -369,11 +490,14 @@ AC_DEFINE_UNQUOTED(CONFIGFILE_MASK, ${enable_configfile_mask}, Mask for config f enable_logfile_mask=${enable_logfile_mask:-0600} AC_DEFINE_UNQUOTED(LOGFILE_MASK, ${enable_logfile_mask}, Mask for log files) -MULTIPATH_NUM=1 +MPATH_NUM=1 case "${enable_multipath}" in - [[0-9]|[1-9][0-9]]) - MULTIPATH_NUM="${enable_multipath}" + 0) + MPATH_NUM=64 + ;; + [[1-9]|[1-9][0-9]|[1-9][0-9][0-9]]) + MPATH_NUM="${enable_multipath}" ;; "") ;; @@ -382,7 +506,26 @@ case "${enable_multipath}" in ;; esac -AC_SUBST(MULTIPATH_NUM) +AC_DEFINE_UNQUOTED(MULTIPATH_NUM, $MPATH_NUM, Maximum number of paths for a route) + +dnl ----------------------------------- +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}" +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 @@ -394,11 +537,11 @@ AC_C_VOLATILE AC_HEADER_STDC AC_HEADER_TIME AC_HEADER_SYS_WAIT +AC_HEADER_STDBOOL dnl AC_TYPE_PID_T AC_TYPE_UID_T AC_TYPE_MODE_T AC_TYPE_SIZE_T -AC_TYPE_SIGNAL AC_STRUCT_TM dnl ------------------------- @@ -406,8 +549,8 @@ 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/socket.h netinet/in.h time.h sys/time.h]) + sys/cdefs.h sys/param.h limits.h signal.h \ + sys/socket.h netinet/in.h time.h sys/time.h features.h]) dnl Utility macro to avoid retyping includes all the time m4_define([QUAGGA_INCLUDES], @@ -438,6 +581,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 @@ -494,6 +640,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 @@ -536,6 +694,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]] \ @@ -554,34 +713,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 @@ -590,9 +738,9 @@ dnl --------------------- dnl Integrated VTY option dnl --------------------- case "${enable_vtysh}" in - "yes") VTYSH="vtysh"; + "no") VTYSH="";; + *) 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 @@ -605,7 +753,7 @@ dnl [TODO] on Linux, and in [TODO] on Solaris. )] )] ) - AC_CHECK_LIB(readline, main, LIBREADLINE="$LIBREADLINE -lreadline",, + AC_CHECK_LIB(readline, main, LIBREADLINE="-lreadline $LIBREADLINE",, "$LIBREADLINE") if test $ac_cv_lib_readline_main = no; then AC_MSG_ERROR([vtysh needs libreadline but was not found and usable on your system.]) @@ -621,9 +769,9 @@ dnl [TODO] on Linux, and in [TODO] on Solaris. fi ;; "no" ) VTYSH="";; - * ) ;; esac AC_SUBST(LIBREADLINE) +AM_CONDITIONAL(VTYSH, test "x$VTYSH" = "xvtysh") dnl ---------- dnl PAM module @@ -745,75 +893,34 @@ AC_CHECK_FUNCS([dup2 ftruncate getcwd gethostbyname getpagesize gettimeofday \ strtol strtoul strlcat strlcpy \ daemon snprintf vsnprintf \ if_nametoindex if_indextoname getifaddrs \ - uname fcntl]) + uname fcntl getgrouplist]) -AC_CHECK_FUNCS(setproctitle, , - [AC_CHECK_LIB(util, setproctitle, - [LIBS="$LIBS -lutil" - AC_DEFINE(HAVE_SETPROCTITLE,, Have setproctitle) - ] - ) - ] -) + +AC_CHECK_HEADER([asm-generic/unistd.h], + [AC_CHECK_DECL(__NR_setns, + AC_DEFINE(HAVE_NETNS,, Have netns),, + QUAGGA_INCLUDES [#include + ]) + AC_CHECK_FUNCS(setns, AC_DEFINE(HAVE_SETNS,, Have setns))] + ) dnl ------------------------------------ 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 @@ -887,12 +994,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";; @@ -920,9 +1026,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 @@ -942,10 +1045,15 @@ dnl figure out how to specify an interface in multicast sockets API dnl --------------------------------------------------------------- AC_CHECK_MEMBERS([struct ip_mreqn.imr_ifindex], [], [], QUAGGA_INCLUDES) +AC_CHECK_HEADERS([linux/mroute.h], [], [], +[ +#if HAVE_NETINET_IN_H +#include +#endif]) 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) +#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 @@ -978,6 +1086,16 @@ AC_CHECK_HEADER([net/if.h], QUAGGA_INCLUDES)], [], QUAGGA_INCLUDES ) +dnl --------------------------------------------------------------- +dnl Additional, newer way to check link-state using ifi_link_state. +dnl Not available in all BSD's when ifmediareq available +dnl --------------------------------------------------------------- +AC_CHECK_HEADER([net/if.h], + AC_CHECK_MEMBERS([struct if_data.ifi_link_state], + AC_DEFINE(HAVE_BSD_IFI_LINK_STATE,,[BSD ifi_link_state available]), + [], QUAGGA_INCLUDES), + ,) + dnl ------------------------ dnl TCP_MD5SIG socket option dnl ------------------------ @@ -999,21 +1117,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 ----------------------------- @@ -1034,7 +1137,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;; @@ -1049,150 +1151,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 - 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 + AC_MSG_ERROR([Failed to detect IPv6 stack]) 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 @@ -1228,73 +1217,81 @@ if test "${enable_zebra}" = "no";then else ZEBRA="zebra" fi +AM_CONDITIONAL(ZEBRA, test "x$ZEBRA" = "xzebra") if test "${enable_bgpd}" = "no";then BGPD="" else BGPD="bgpd" fi +AM_CONDITIONAL(BGPD, test "x$BGPD" = "xbgpd") if test "${enable_ripd}" = "no";then RIPD="" else RIPD="ripd" fi +AM_CONDITIONAL(RIPD, test "x$RIPD" = "xripd") if test "${enable_ospfd}" = "no";then OSPFD="" else OSPFD="ospfd" fi +AM_CONDITIONAL(OSPFD, test "x$OSPFD" = "xospfd") -if test "${enable_babeld}" = "no";then - BABELD="" +if test x"$opsys" != x"gnu-linux"; then + dnl NHRPd works currently with Linux only. + enable_nhrpd="no" +fi +if test "${enable_nhrpd}" = "no";then + NHRPD="" else - BABELD="babeld" + NHRPD="nhrpd" fi +AM_CONDITIONAL(NHRPD, test "x$NHRPD" = "xnhrpd") if test "${enable_watchquagga}" = "no";then WATCHQUAGGA="" else WATCHQUAGGA="watchquagga" fi +AM_CONDITIONAL(WATCHQUAGGA, test "x$WATCHQUAGGA" = "xwatchquagga") OSPFCLIENT="" -if test "${enable_opaque_lsa}" != "no"; then - if test "${enable_ospfapi}" != "no";then +if test "${enable_ospfapi}" != "no";then AC_DEFINE(SUPPORT_OSPF_API,,OSPFAPI) - if test "${enable_ospfclient}" != "no";then + if test "${enable_ospfclient}" != "no";then OSPFCLIENT="ospfclient" - fi fi - 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 + "no" ) PIMD="";; + * ) PIMD="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) @@ -1309,12 +1306,12 @@ AC_SUBST(RIPD) AC_SUBST(RIPNGD) AC_SUBST(OSPFD) AC_SUBST(OSPF6D) -AC_SUBST(BABELD) +AC_SUBST(NHRPD) 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) @@ -1323,13 +1320,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 --------------------------- @@ -1351,24 +1341,50 @@ fi AC_SUBST(HAVE_LIBPCREPOSIX) AC_SUBST(LIB_REGEX) +dnl ------------------ +dnl check C-Ares library +dnl ------------------ +if test "${enable_nhrpd}" != "no";then + PKG_CHECK_MODULES([CARES], [libcares]) +fi + + dnl ------------------ dnl check Net-SNMP library dnl ------------------ -if test "${enable_snmp}" = "yes"; then - if test "$with_crypto" != "no"; then - LIBS="${LIBS} -lcrypto"; - fi - AC_CHECK_LIB(netsnmp, asn_parse_int, - [AC_DEFINE(HAVE_NETSNMP,,Net SNMP) - AC_DEFINE(HAVE_SNMP,,SNMP) - LIBS="${LIBS} -lnetsnmp"], - [AC_MSG_ERROR([--enable-snmp given, but cannot find support for SNMP])]) - - AC_CHECK_HEADER([net-snmp/net-snmp-config.h], - [], - [AC_MSG_ERROR([--enable-snmp given, but cannot find net-snmp-config.h])], - QUAGGA_INCLUDES) - AC_SUBST(SNMP_INCLUDES) +if test "${enable_snmp}" != ""; then + AC_PATH_TOOL([NETSNMP_CONFIG], [net-snmp-config], [no]) + if test x"$NETSNMP_CONFIG" = x"no"; then + AC_MSG_ERROR([--enable-snmp given but unable to find net-snmp-config]) + fi + LIBS="$LIBS `${NETSNMP_CONFIG} --agent-libs`" + CFLAGS="`${NETSNMP_CONFIG} --base-cflags` $CFLAGS" + AC_MSG_CHECKING([whether we can link to Net-SNMP]) + AC_LINK_IFELSE([AC_LANG_PROGRAM([ +int main(void); +], +[ +{ + return 0; +} +])],[AC_MSG_RESULT(yes)],[ + AC_MSG_RESULT(no) + AC_MSG_ERROR([--enable-snmp given but not usable])]) + AC_DEFINE(HAVE_SNMP,,SNMP) + case "${enable_snmp}" in + yes) + SNMP_METHOD=agentx + ;; + smux|agentx) + SNMP_METHOD="${enable_snmp}" + ;; + *) + AC_MSG_ERROR([--enable-snmp given with an unknown method (${enable_snmp}). Use smux or agentx]) + ;; + esac + AH_TEMPLATE([SNMP_SMUX], [Use SNMP SMUX to interface with snmpd]) + AH_TEMPLATE([SNMP_AGENTX], [Use SNMP AgentX to interface with snmpd]) + AC_DEFINE_UNQUOTED(AS_TR_CPP(SNMP_${SNMP_METHOD}),,SNMP method to interface with snmpd) fi dnl --------------------------- @@ -1376,7 +1392,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], @@ -1385,6 +1402,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) @@ -1404,15 +1422,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 @@ -1461,15 +1519,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 @@ -1494,28 +1577,17 @@ dnl ---------- CONFDATE=`date '+%Y%m%d'` AC_SUBST(CONFDATE) -dnl Conditionally enable PIE support for GNU toolchains. -AC_ARG_ENABLE(pie, AS_HELP_STRING([--disable-pie], [Do not build tools as a Position Independent Executables])) -if test "$enable_pie" != "no"; then - AC_CACHE_CHECK([whether $CC accepts PIE flags], [ap_cv_cc_pie], [ - save_CFLAGS=$CFLAGS - save_LDFLAGS=$LDFLAGS - CFLAGS="$CFLAGS -fPIE" - LDFLAGS="$LDFLAGS -pie" - AC_LINK_IFELSE([AC_LANG_SOURCE([[static int foo[30000]; int main () { return 0; }]])], - [ap_cv_cc_pie=yes], [ap_cv_cc_pie=no] - ) - CFLAGS=$save_CFLAGS - LDFLAGS=$save_LDFLAGS - ]) - if test "$ap_cv_cc_pie" = "yes"; then - PICFLAGS="-fPIE" - PILDFLAGS="-pie" - fi +dnl ------- +dnl DejaGNU +dnl ------- +if test x"$DEJAGNU" = x +then + DEJAGNU="\$(top_srcdir)/tests/global-conf.exp" fi +RUNTESTDEFAULTFLAGS="-x --tool \$\$tool" -AC_SUBST(PICFLAGS) -AC_SUBST(PILDFLAGS) +AC_SUBST(DEJAGNU) +AC_SUBST(RUNTESTDEFAULTFLAGS) dnl ------------------------------ dnl set paths for state directory @@ -1553,8 +1625,9 @@ AC_DEFINE_UNQUOTED(PATH_RIPNGD_PID, "$quagga_statedir/ripngd.pid",ripngd PID) AC_DEFINE_UNQUOTED(PATH_BGPD_PID, "$quagga_statedir/bgpd.pid",bgpd PID) 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_NHRPD_PID, "$quagga_statedir/nhrpd.pid",nhrpd 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) @@ -1563,8 +1636,9 @@ AC_DEFINE_UNQUOTED(RIPNG_VTYSH_PATH, "$quagga_statedir/ripngd.vty",ripng vty soc AC_DEFINE_UNQUOTED(BGP_VTYSH_PATH, "$quagga_statedir/bgpd.vty",bgpd vty socket) AC_DEFINE_UNQUOTED(OSPF_VTYSH_PATH, "$quagga_statedir/ospfd.vty",ospfd vty socket) 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(NHRP_VTYSH_PATH, "$quagga_statedir/nhrpd.vty",nhrpd 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 ------------------------------- @@ -1586,15 +1660,18 @@ AC_CACHE_VAL(ac_cv_htonl_works, ) AC_MSG_RESULT($ac_cv_htonl_works) -AC_CONFIG_FILES([Makefile lib/Makefile zebra/Makefile ripd/Makefile +AC_CONFIG_FILES([Makefile lib/Makefile qpb/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 + ospf6d/Makefile isisd/Makefile vtysh/Makefile + doc/Makefile ospfclient/Makefile tests/Makefile m4/Makefile + pimd/Makefile nhrpd/Makefile + tests/bgpd.tests/Makefile + tests/libzebra.tests/Makefile + redhat/Makefile pkgsrc/Makefile + fpm/Makefile redhat/quagga.spec lib/version.h - doc/defines.texi isisd/topology/Makefile pkgsrc/bgpd.sh pkgsrc/ospf6d.sh pkgsrc/ospfd.sh pkgsrc/ripd.sh pkgsrc/ripngd.sh pkgsrc/zebra.sh]) @@ -1609,12 +1686,11 @@ echo " Quagga configuration -------------------- quagga version : ${PACKAGE_VERSION} -host operating system : ${host_os} +host operating system : ${host_os} source code location : ${srcdir} compiler : ${CC} compiler flags : ${CFLAGS} make : ${MAKE-make} -includes : ${INCLUDES} ${SNMP_INCLUDES} linker flags : ${LDFLAGS} ${LIBS} ${LIBCAP} ${LIBREADLINE} ${LIBM} state file directory : ${quagga_statedir} config file directory : `eval echo \`echo ${sysconfdir}\`` @@ -1624,6 +1700,7 @@ group to run as : ${enable_group} group for vty sockets : ${enable_vty_group} config file mask : ${enable_configfile_mask} log file mask : ${enable_logfile_mask} +zebra protobuf enabled : ${have_protobuf:-no} The above user and group must have read/write access to the state file directory and to the config files in the config file directory." diff --git a/doc/.gitignore b/doc/.gitignore index 5071f9870..459531b9f 100644 --- a/doc/.gitignore +++ b/doc/.gitignore @@ -4,9 +4,7 @@ mdate-sh draft-zebra-00.txt quagga.info-* zebra.html -defines.texi version.texi -texinfo.tex quagga.html quagga.info *.pdf diff --git a/doc/Makefile.am b/doc/Makefile.am index dde95ab30..38920c8e3 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -20,8 +20,7 @@ PNGTOPDF = $(PNGTOEPS) EPSTOPDF = epstopdf # The figure sources -figures_names_parts = -normal-processing -rs-processing \ - _topologies_full _topologies_rs +figures_names_parts = -normal-processing -rs-processing figures_sources = $(figures_names_parts:%=fig%.dia) figures_png = $(figures_names_parts:%=fig%.png) figures_pdf = $(figures_names_parts:%=fig%.pdf) @@ -34,23 +33,17 @@ figures_txt = $(figures_names_parts:%=fig%.txt) # provided by automake. If you are an automake wizard, please feel free to # compact it somehow. -# Built from defines.texi.in -BUILT_SOURCES = defines.texi +#quagga.pdf: $(info_TEXINFOS) $(quagga_TEXINFOS) +# $(TEXI2PDF) -o "$@" $< || true info_TEXINFOS = quagga.texi -# Have to manually specify the quagga.pdf rule in order to allow -# us to have a generic automatic .pdf rule to build the figure sources -# 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 "$@" $< - -quagga_TEXINFOS = appendix.texi babeld.texi basic.texi bgpd.texi filter.texi \ +quagga_TEXINFOS = appendix.texi basic.texi bgpd.texi filter.texi \ install.texi ipv6.texi kernel.texi main.texi ospf6d.texi ospfd.texi \ overview.texi protocol.texi ripd.texi ripngd.texi routemap.texi \ snmp.texi vtysh.texi routeserver.texi defines.texi $(figures_png) \ - snmptrap.texi $(figures_txt) + snmptrap.texi ospf_fundamentals.texi isisd.texi nhrpd.texi \ + $(figures_txt) .png.eps: $(PNGTOEPS) $< "$@" @@ -61,12 +54,72 @@ quagga_TEXINFOS = appendix.texi babeld.texi basic.texi bgpd.texi filter.texi \ .dia.png: $(DIATOPNG) "$@" $< -man_MANS = vtysh.1 bgpd.8 ospf6d.8 ospfd.8 ripd.8 ripngd.8 zebra.8 isisd.8 +man_MANS = + +if PIMD +man_MANS += pimd.8 +endif + +if BGPD +man_MANS += bgpd.8 +endif + +if ISISD +man_MANS += isisd.8 +endif + +if OSPF6D +man_MANS += ospf6d.8 +endif + +if OSPFCLIENT +man_MANS += ospfclient.8 +endif + +if OSPFD +man_MANS += ospfd.8 +endif + +if RIPD +man_MANS += ripd.8 +endif -EXTRA_DIST = BGP-TypeCode draft-zebra-00.ms draft-zebra-00.txt $(man_MANS) \ +if RIPNGD +man_MANS += ripngd.8 +endif + +if NHRPD +man_MANS += nhrpd.8 +endif + +if VTYSH +man_MANS += vtysh.1 +endif + +if WATCHQUAGGA +man_MANS += watchquagga.8 +endif + +if ZEBRA +man_MANS += zebra.8 +endif + +AM_MAKEINFOHTMLFLAGS = --css-include=$(srcdir)/texinfo.css + +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 nhrpd.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) + $(figures_sources) $(figures_png) $(figures_txt) \ + texinfo.tex texinfo.css draft-zebra-00.txt: draft-zebra-00.ms groff -T ascii -ms $< > $@ + +CLEANFILES = *.{fn,fns,cp,cps,ky,kys} +DISTCLEANFILES = quagga.info* + +# do nothing for DVI, so we don't have to generate or distribute EPS +# figures +dvi: # nothing diff --git a/doc/appendix.texi b/doc/appendix.texi index 87276c52f..3904c5f0c 100644 --- a/doc/appendix.texi +++ b/doc/appendix.texi @@ -28,6 +28,25 @@ don't need to change header format. | Length | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ @end group +@end example + + If `type' is PROTOCOL_BGP4MP_ET, the common header format will +contain an additional microsecond field (RFC6396 2011). + +@example +@group +0 1 2 3 +0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Time | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Type | Subtype | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Length | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +| Microsecond | ++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +@end group @end example If `type' is PROTOCOL_BGP4MP, `subtype' is BGP4MP_STATE_CHANGE, and @@ -227,7 +246,8 @@ If `type' is PROTOCOL_BGP4MP and `subtype' is BGP4MP_SNAPSHOT, @group Constants: /* type value */ - #define MSG_PROTOCOL_BGP4MP 16 + #define MSG_PROTOCOL_BGP4MP 16 + #define MSG_PROTOCOL_BGP4MP_ET 17 /* subtype value */ #define BGP4MP_STATE_CHANGE 0 #define BGP4MP_MESSAGE 1 diff --git a/doc/babeld.texi b/doc/babeld.texi deleted file mode 100644 index 1f24edd06..000000000 --- a/doc/babeld.texi +++ /dev/null @@ -1,118 +0,0 @@ -@c -*-texinfo-*- -@c This is part of the Quagga Manual. -@c @value{COPYRIGHT_STR} -@c See file quagga.texi for copying conditions. -@node Babel -@chapter Babel - -Babel is an interior gateway protocol that is suitable both for wired -networks and for wireless mesh networks. Babel has been described as -``RIP on speed'' --- it is based on the same principles as RIP, but -includes a number of refinements that make it react much faster to -topology changes without ever counting to infinity, and allow it to -perform reliable link quality estimation on wireless links. Babel is -a double-stack routing protocol, meaning that a single Babel instance -is able to perform routing for both IPv4 and IPv6. - -Quagga implements Babel as described in RFC6126. - -@menu -* Configuring babeld:: -* Babel configuration:: -* Babel redistribution:: -* Show Babel information:: -* Babel debugging commands:: -@end menu - -@node Configuring babeld, Babel configuration, Babel, Babel -@section Configuring babeld - -The @command{babeld} daemon can be invoked with any of the common -options (@pxref{Common Invocation Options}). - -The @command{zebra} daemon must be running before @command{babeld} is -invoked. Also, if @command{zebra} is restarted then @command{babeld} -must be too. - -Configuration of @command{babeld} is done in its configuration file -@file{babeld.conf}. - -@node Babel configuration, Babel redistribution, Configuring babeld, Babel -@section Babel configuration - -@deffn Command {router babel} {} -@deffnx Command {no router babel} {} -Enable or disable Babel routing. -@end deffn - -@deffn {Babel Command} {network @var{ifname}} {} -@deffnx {Babel Command} {no network @var{ifname}} {} -Enable or disable Babel on the given interface. -@end deffn - -@deffn {Interface Command} {babel wired} {} -@deffnx {Interface Command} {babel wireless} {} -Specifies whether this interface is wireless, which disables a number -of optimisations that are only correct on wired interfaces. -Specifying @code{wireless} (the default) is always correct, but may -cause slower convergence and extra routing traffic. -@end deffn - -@deffn {Interface Command} {babel split-horizon} -@deffnx {Interface Command} {no babel split-horizon} -Specifies whether to perform split-horizon on the interface. -Specifying @code{no babel split-horizon} (the default) is always -correct, while @code{babel split-horizon} is an optimisation that -should only be used on symmetric and transitive (wired) networks. -@end deffn - -@deffn {Interface Command} {babel hello-interval <20-655340>} -Specifies the time in milliseconds between two scheduled hellos. On -wired links, Babel notices a link failure within two hello intervals; -on wireless links, the link quality value is reestimated at every -hello interval. The default is 4000@dmn{ms}. -@end deffn - -@deffn {Interface Command} {babel update-interval <20-655340>} -Specifies the time in milliseconds between two scheduled updates. -Since Babel makes extensive use of triggered updates, this can be set -to fairly high values on links with little packet loss. The default -is 20000@dmn{ms}. -@end deffn - -@deffn {Babel Command} {babel resend-delay <20-655340>} -Specifies the time in milliseconds after which an ``important'' -request or update will be resent. The default is 2000@dmn{ms}. You -probably don't want to tweak this value. -@end deffn - -@node Babel redistribution, Show Babel information, Babel configuration, Babel -@section Babel redistribution - -@deffn {Babel command} {redistribute @var{kind}} -@deffnx {Babel command} {no redistribute @var{kind}} -Specify which kind of routes should be redistributed into Babel. -@end deffn - -@node Show Babel information, Babel debugging commands, Babel redistribution, Babel -@section Show Babel information - -@deffn {Command} {show babel database} {} -@deffnx {Command} {show babel interface} {} -@deffnx {Command} {show babel neighbour} {} -@deffnx {Command} {show babel parameters} {} -These commands dump various parts of @command{babeld}'s internal -state. They are mostly useful for troubleshooting. -@end deffn - -@node Babel debugging commands, , Show Babel information, Babel -@section Babel debugging commands - -@deffn {Babel Command} {debug babel @var{kind}} {} -@deffnx {Babel Command} {no debug babel @var{kind}} {} -Enable or disable debugging messages of a given kind. @var{kind} can -be one of @samp{common}, @samp{kernel}, @samp{filter}, @samp{timeout}, -@samp{interface}, @samp{route} or @samp{all}. Note that if you have -compiled with the NO_DEBUG flag, then these commands aren't available. -@end deffn - diff --git a/doc/basic.texi b/doc/basic.texi index b3b23ca99..92e8d9ebd 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 @@ -185,6 +185,13 @@ In this example, the precision is set to provide timestamps with millisecond accuracy. @end deffn +@deffn Command {log commands} {} +This command enables the logging of all commands typed by a user to +all enabled log destinations. The note that logging includes full +command lines, including passwords. Once set, command logging can only +be turned off by restarting the daemon. +@end deffn + @deffn Command {service password-encryption} {} Encrypt password. @end deffn @@ -545,6 +552,12 @@ Kill line from the beginning, erasing input. @kindex C-t Transpose character. +@item C-v +@kindex C-v +Interpret following character literally. Do not treat it specially. +This can be used to, e.g., type in a literal @kbd{?} rather than do +help completion. + @end table @node CLI Advanced Commands @@ -580,10 +593,13 @@ 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. +To enter an actual @kbd{?} character rather show completions, e.g. to +enter into a regexp, use @kbd{@key{C}-v ?}. + @end table diff --git a/doc/bgpd.8 b/doc/bgpd.8 index 8daaefa27..e680ddb23 100644 --- a/doc/bgpd.8 +++ b/doc/bgpd.8 @@ -6,7 +6,7 @@ software .SH SYNOPSIS .B bgpd [ -.B \-dhrv +.B \-dhrSv ] [ .B \-f .I config-file @@ -74,6 +74,9 @@ Specify the user to run as. Default is \fIquagga\fR. \fB\-r\fR, \fB\-\-retain\fR When the program terminates, retain routes added by \fBbgpd\fR. .TP +\fB\-S\fR, \fB\-\-skip_runas\fR +Skip setting the process effective user and group. +.TP \fB\-v\fR, \fB\-\-version\fR Print the version and exit. .SH FILES @@ -106,6 +109,7 @@ debugging options, see the Info file, or the source for details. .BR ospfd (8), .BR ospf6d (8), .BR isisd (8), +.BR nhrpd (8), .BR zebra (8), .BR vtysh (1) .SH BUGS diff --git a/doc/bgpd.texi b/doc/bgpd.texi index 63834600a..d5aa30c4a 100644 --- a/doc/bgpd.texi +++ b/doc/bgpd.texi @@ -1,6 +1,8 @@ @c -*-texinfo-*- @c This is part of the Quagga Manual. @c @value{COPYRIGHT_STR} +@c Portions: +@c Copyright @copyright{} 2015 Hewlett Packard Enterprise Development LP @c See file quagga.texi for copying conditions. @node BGP @chapter BGP @@ -18,6 +20,7 @@ BGP-4. @menu * Starting BGP:: * BGP router:: +* BGP MED:: * BGP network:: * BGP Peer:: * BGP Peer Group:: @@ -53,6 +56,13 @@ Set the bgp protocol's port number. @item -r @itemx --retain When program terminates, retain BGP routes added by zebra. + +@item -l +@itemx --listenon +Specify a specific IP address for bgpd to listen on, rather than its +default of INADDR_ANY / IN6ADDR_ANY. This can be useful to constrain bgpd +to an internal address, or to run multiple bgpd processes on one host. + @end table @node BGP router @@ -104,18 +114,65 @@ This command set distance value to @node BGP decision process @subsection BGP decision process +The decision process Quagga BGP uses to select routes is as follows: + @table @asis @item 1. Weight check +prefer higher local weight routes to lower routes. -@item 2. Local preference check. +@item 2. Local preference check +prefer higher local preference routes to lower. + +@item 3. Local route check +Prefer local routes (statics, aggregates, redistributed) to received routes. + +@item 4. AS path length check +Prefer shortest hop-count AS_PATHs. + +@item 5. Origin check +Prefer the lowest origin type route. That is, prefer IGP origin routes to +EGP, to Incomplete routes. + +@item 6. MED check +Where routes with a MED were received from the same AS, +prefer the route with the lowest MED. @xref{BGP MED}. + +@item 7. External check +Prefer the route received from an external, eBGP peer +over routes received from other types of peers. -@item 3. Local route check. +@item 8. IGP cost check +Prefer the route with the lower IGP cost. -@item 4. AS path length check. +@item 9. Multi-path check +If multi-pathing is enabled, then check whether +the routes not yet distinguished in preference may be considered equal. If +@ref{bgp bestpath as-path multipath-relax} is set, all such routes are +considered equal, otherwise routes received via iBGP with identical AS_PATHs +or routes received from eBGP neighbours in the same AS are considered equal. -@item 5. Origin check. +@item 10 Already-selected external check + +Where both routes were received from eBGP peers, then prefer the route which +is already selected. Note that this check is not applied if @ref{bgp +bestpath compare-routerid} is configured. This check can prevent some cases +of oscillation. + +@item 11. Router-ID check +Prefer the route with the lowest @w{router-ID}. If the +route has an @w{ORIGINATOR_ID} attribute, through iBGP reflection, then that +router ID is used, otherwise the @w{router-ID} of the peer the route was +received from is used. + +@item 12. Cluster-List length check +The route with the shortest cluster-list +length is used. The cluster-list reflects the iBGP reflection path the +route has taken. + +@item 13. Peer address +Prefer the route received from the peer with the higher +transport layer address, as a last-resort tie-breaker. -@item 6. MED check. @end table @deffn {BGP} {bgp bestpath as-path confed} {} @@ -124,6 +181,37 @@ sequences should should be taken into account during the BGP best path decision process. @end deffn +@deffn {BGP} {bgp bestpath as-path multipath-relax} {} +@anchor{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 + +@deffn {BGP} {bgp bestpath compare-routerid} {} +@anchor{bgp bestpath compare-routerid} + +Ensure that when comparing routes where both are equal on most metrics, +including local-pref, AS_PATH length, IGP cost, MED, that the tie is broken +based on router-ID. + +If this option is enabled, then the already-selected check, where +already selected eBGP routes are preferred, is skipped. + +If a route has an @w{ORIGINATOR_ID} attribute because it has been reflected, +that @w{ORIGINATOR_ID} will be used. Otherwise, the router-ID of the peer the +route was received from will be used. + +The advantage of this is that the route-selection (at this point) will be +more deterministic. The disadvantage is that a few or even one lowest-ID +router may attract all trafic to otherwise-equal paths because of this +check. It may increase the possibility of MED or IGP oscillation, unless +other measures were taken to avoid these. The exact behaviour will be +sensitive to the iBGP and reflection topology. + +@end deffn + + @node BGP route flap dampening @subsection BGP route flap dampening @@ -145,6 +233,282 @@ The route-flap damping algorithm is compatible with @cite{RFC2439}. The use of t is not recommended nowadays, see @uref{http://www.ripe.net/ripe/docs/ripe-378,,RIPE-378}. @end deffn +@node BGP MED +@section BGP MED + +The BGP MED (Multi_Exit_Discriminator) attribute has properties which can +cause subtle convergence problems in BGP. These properties and problems +have proven to be hard to understand, at least historically, and may still +not be widely understood. The following attempts to collect together and +present what is known about MED, to help operators and Quagga users in +designing and configuring their networks. + +The BGP @acronym{MED, Multi_Exit_Discriminator} attribute is intended to +allow one AS to indicate its preferences for its ingress points to another +AS. The MED attribute will not be propagated on to another AS by the +receiving AS - it is `non-transitive' in the BGP sense. + +E.g., if AS X and AS Y have 2 different BGP peering points, then AS X +might set a MED of 100 on routes advertised at one and a MED of 200 at the +other. When AS Y selects between otherwise equal routes to or via +AS X, AS Y should prefer to take the path via the lower MED peering of 100 with +AS X. Setting the MED allows an AS to influence the routing taken to it +within another, neighbouring AS. + +In this use of MED it is not really meaningful to compare the MED value on +routes where the next AS on the paths differs. E.g., if AS Y also had a +route for some destination via AS Z in addition to the routes from AS X, and +AS Z had also set a MED, it wouldn't make sense for AS Y to compare AS Z's +MED values to those of AS X. The MED values have been set by different +administrators, with different frames of reference. + +The default behaviour of BGP therefore is to not compare MED values across +routes received from different neighbouring ASes. In Quagga this is done by +comparing the neighbouring, left-most AS in the received AS_PATHs of the +routes and only comparing MED if those are the same. + +@c TeXInfo uses the old, non-UTF-8 capable, pdftex, and so +@c doesn't render TeX the unicode precedes character correctly in PDF, etc. +@c Using a TeX code on the other hand doesn't work for non-TeX outputs +@c (plaintext, e.g.). So, use an output-conditional macro. + +@iftex +@macro mprec{} +@math{\\prec} +@end macro +@end iftex + +@ifnottex +@macro mprec{} +@math{≺} +@end macro +@end ifnottex + +Unfortunately, this behaviour of MED, of sometimes being compared across +routes and sometimes not, depending on the properties of those other routes, +means MED can cause the order of preference over all the routes to be +undefined. That is, given routes A, B, and C, if A is preferred to B, and B +is preferred to C, then a well-defined order should mean the preference is +transitive (in the sense of orders @footnote{For some set of objects to have +an order, there @emph{must} be some binary ordering relation that is defined +for @emph{every} combination of those objects, and that relation @emph{must} +be transitive. I.e.@:, if the relation operator is @mprec{}, and if +a @mprec{} b and b @mprec{} c then that relation must carry over +and it @emph{must} be that a @mprec{} c for the objects to have an +order. The ordering relation may allow for equality, i.e. +a @mprec{} b and b @mprec{} a may both be true amd imply that +a and b are equal in the order and not distinguished by it, in +which case the set has a partial order. Otherwise, if there is an order, +all the objects have a distinct place in the order and the set has a total +order.}) and that A would be preferred to C. + +However, when MED is involved this need not be the case. With MED it is +possible that C is actually preferred over A. So A is preferred to B, B is +preferred to C, but C is preferred to A. This can be true even where BGP +defines a deterministic ``most preferred'' route out of the full set of +A,B,C. With MED, for any given set of routes there may be a +deterministically preferred route, but there need not be any way to arrange +them into any order of preference. With unmodified MED, the order of +preference of routes literally becomes undefined. + +That MED can induce non-transitive preferences over routes can cause issues. +Firstly, it may be perceived to cause routing table churn locally at +speakers; secondly, and more seriously, it may cause routing instability in +iBGP topologies, where sets of speakers continually oscillate between +different paths. + +The first issue arises from how speakers often implement routing decisions. +Though BGP defines a selection process that will deterministically select +the same route as best at any given speaker, even with MED, that process +requires evaluating all routes together. For performance and ease of +implementation reasons, many implementations evaluate route preferences in a +pair-wise fashion instead. Given there is no well-defined order when MED is +involved, the best route that will be chosen becomes subject to +implementation details, such as the order the routes are stored in. That +may be (locally) non-deterministic, e.g.@: it may be the order the routes +were received in. + +This indeterminism may be considered undesirable, though it need not cause +problems. It may mean additional routing churn is perceived, as sometimes +more updates may be produced than at other times in reaction to some event . + +This first issue can be fixed with a more deterministic route selection that +ensures routes are ordered by the neighbouring AS during selection. +@xref{bgp deterministic-med}. This may reduce the number of updates as +routes are received, and may in some cases reduce routing churn. Though, it +could equally deterministically produce the largest possible set of updates +in response to the most common sequence of received updates. + +A deterministic order of evaluation tends to imply an additional overhead of +sorting over any set of n routes to a destination. The implementation of +deterministic MED in Quagga scales significantly worse than most sorting +algorithms at present, with the number of paths to a given destination. +That number is often low enough to not cause any issues, but where there are +many paths, the deterministic comparison may quickly become increasingly +expensive in terms of CPU. + +Deterministic local evaluation can @emph{not} fix the second, more major, +issue of MED however. Which is that the non-transitive preference of routes +MED can cause may lead to routing instability or oscillation across multiple +speakers in iBGP topologies. This can occur with full-mesh iBGP, but is +particularly problematic in non-full-mesh iBGP topologies that further +reduce the routing information known to each speaker. This has primarily +been documented with iBGP route-reflection topologies. However, any +route-hiding technologies potentially could also exacerbate oscillation with +MED. + +This second issue occurs where speakers each have only a subset of routes, +and there are cycles in the preferences between different combinations of +routes - as the undefined order of preference of MED allows - and the routes +are distributed in a way that causes the BGP speakers to 'chase' those +cycles. This can occur even if all speakers use a deterministic order of +evaluation in route selection. + +E.g., speaker 4 in AS A might receive a route from speaker 2 in AS X, and +from speaker 3 in AS Y; while speaker 5 in AS A might receive that route +from speaker 1 in AS Y. AS Y might set a MED of 200 at speaker 1, and 100 +at speaker 3. I.e, using ASN:ID:MED to label the speakers: + +@example + + /---------------\ + X:2------|--A:4-------A:5--|-Y:1:200 + Y:3:100--|-/ | + \---------------/ + +@end example + +Assuming all other metrics are equal (AS_PATH, ORIGIN, 0 IGP costs), then +based on the RFC4271 decision process speaker 4 will choose X:2 over +Y:3:100, based on the lower ID of 2. Speaker 4 advertises X:2 to speaker 5. +Speaker 5 will continue to prefer Y:1:200 based on the ID, and advertise +this to speaker 4. Speaker 4 will now have the full set of routes, and the +Y:1:200 it receives from 5 will beat X:2, but when speaker 4 compares +Y:1:200 to Y:3:100 the MED check now becomes active as the ASes match, and +now Y:3:100 is preferred. Speaker 4 therefore now advertises Y:3:100 to 5, +which will also agrees that Y:3:100 is preferred to Y:1:200, and so +withdraws the latter route from 4. Speaker 4 now has only X:2 and Y:3:100, +and X:2 beats Y:3:100, and so speaker 4 implicitly updates its route to +speaker 5 to X:2. Speaker 5 sees that Y:1:200 beats X:2 based on the ID, +and advertises Y:1:200 to speaker 4, and the cycle continues. + +The root cause is the lack of a clear order of preference caused by how MED +sometimes is and sometimes is not compared, leading to this cycle in the +preferences between the routes: + +@example + + /---> X:2 ---beats---> Y:3:100 --\ + | | + | | + \---beats--- Y:1:200 <---beats---/ + +@end example + +This particular type of oscillation in full-mesh iBGP topologies can be +avoided by speakers preferring already selected, external routes rather than +choosing to update to new a route based on a post-MED metric (e.g. +router-ID), at the cost of a non-deterministic selection process. Quagga +implements this, as do many other implementations, so long as it is not +overridden by setting @ref{bgp bestpath compare-routerid}, and see also +@ref{BGP decision process}, . + +However, more complex and insidious cycles of oscillation are possible with +iBGP route-reflection, which are not so easily avoided. These have been +documented in various places. See, e.g., @cite{McPherson, D. and Gill, V. +and Walton, D., "Border Gateway Protocol (BGP) Persistent Route Oscillation +Condition", IETF RFC3345}, and @cite{Flavel, A. and M. Roughan, "Stable +and flexible iBGP", ACM SIGCOMM 2009}, and @cite{Griffin, T. and G. Wilfong, +"On the correctness of IBGP configuration", ACM SIGCOMM 2002} for concrete +examples and further references. + +There is as of this writing @emph{no} known way to use MED for its original +purpose; @emph{and} reduce routing information in iBGP topologies; +@emph{and} be sure to avoid the instability problems of MED due the +non-transitive routing preferences it can induce; in general on arbitrary +networks. + +There may be iBGP topology specific ways to reduce the instability risks, +even while using MED, e.g.@: by constraining the reflection topology and by +tuning IGP costs between route-reflector clusters, see RFC3345 for details. +In the near future, the Add-Path extension to BGP may also solve MED +oscillation while still allowing MED to be used as intended, by distributing +"best-paths per neighbour AS". This would be at the cost of distributing at +least as many routes to all speakers as a full-mesh iBGP would, if not more, +while also imposing similar CPU overheads as the "Deterministic MED" feature +at each Add-Path reflector. + +More generally, the instability problems that MED can introduce on more +complex, non-full-mesh, iBGP topologies may be avoided either by: + +@itemize + +@item +Setting @ref{bgp always-compare-med}, however this allows MED to be compared +across values set by different neighbour ASes, which may not produce +coherent desirable results, of itself. + +@item +Effectively ignoring MED by setting MED to the same value (e.g.@: 0) using +@ref{routemap set metric} on all received routes, in combination with +setting @ref{bgp always-compare-med} on all speakers. This is the simplest +and most performant way to avoid MED oscillation issues, where an AS is happy +not to allow neighbours to inject this problematic metric. + +@end itemize + +As MED is evaluated after the AS_PATH length check, another possible use for +MED is for intra-AS steering of routes with equal AS_PATH length, as an +extension of the last case above. As MED is evaluated before IGP metric, +this can allow cold-potato routing to be implemented to send traffic to +preferred hand-offs with neighbours, rather than the closest hand-off +according to the IGP metric. + +Note that even if action is taken to address the MED non-transitivity +issues, other oscillations may still be possible. E.g., on IGP cost if +iBGP and IGP topologies are at cross-purposes with each other - see the +Flavel and Roughan paper above for an example. Hence the guideline that the +iBGP topology should follow the IGP topology. + +@deffn {BGP} {bgp deterministic-med} {} +@anchor{bgp deterministic-med} + +Carry out route-selection in way that produces deterministic answers +locally, even in the face of MED and the lack of a well-defined order of +preference it can induce on routes. Without this option the preferred route +with MED may be determined largely by the order that routes were received +in. + +Setting this option will have a performance cost that may be noticeable when +there are many routes for each destination. Currently in Quagga it is +implemented in a way that scales poorly as the number of routes per +destination increases. + +The default is that this option is not set. +@end deffn + +Note that there are other sources of indeterminism in the route selection +process, specifically, the preference for older and already selected routes +from eBGP peers, @xref{BGP decision process}. + +@deffn {BGP} {bgp always-compare-med} {} +@anchor{bgp always-compare-med} + +Always compare the MED on routes, even when they were received from +different neighbouring ASes. Setting this option makes the order of +preference of routes more defined, and should eliminate MED induced +oscillations. + +If using this option, it may also be desirable to use @ref{routemap set +metric} to set MED to 0 on routes received from external neighbours. + +This option can be used, together with @ref{routemap set metric} to use MED +as an intra-AS metric to steer equal-length AS_PATH routes to, e.g., desired +exit points. +@end deffn + + + @node BGP network @section BGP network @@ -182,7 +546,7 @@ This command specifies an aggregate address. @end deffn @deffn {BGP} {aggregate-address @var{A.B.C.D/M} as-set} {} -This command specifies an aggregate address. Resulting routes inlucde +This command specifies an aggregate address. Resulting routes include AS set. @end deffn @@ -293,10 +657,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 +705,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 @@ -358,6 +753,12 @@ Apply a route-map on the neighbor. @var{direct} must be @code{in} or @code{out}. @end deffn +@deffn {BGP} {bgp route-reflector allow-outbound-policy} {} +By default, attribute modification via route-map policy out is not reflected +on reflected routes. This option allows the modifications to be reflected as +well. Once enabled, it affects all reflected routes. +@end deffn + @c ----------------------------------------------------------------------- @node BGP Peer Group @section BGP Peer Group @@ -373,6 +774,31 @@ This command bind specific peer to peer group @var{word}. @node BGP Address Family @section BGP Address Family +Multiprotocol BGP enables BGP to carry routing information for multiple +Network Layer protocols. BGP supports multiple Address Family +Identifier (AFI), namely IPv4 and IPv6. Support is also provided for +multiple sets of per-AFI information via Subsequent Address Family +Identifiers (SAFI). In addition to unicast information, VPN information +@cite{RFC4364} and @cite{RFC4659}, and Encapsulation information +@cite{RFC5512} is supported. + +@deffn {Command} {show ip bgp vpnv4 all} {} +@deffnx {Command} {show ipv6 bgp vpn all} {} +Print active IPV4 or IPV6 routes advertised via the VPN SAFI. +@end deffn + +@deffn {Command} {show ip bgp encap all} {} +@deffnx {Command} {show ipv6 bgp encap all} {} +Print active IPV4 or IPV6 routes advertised via the Encapsulation SAFI. +@end deffn + +@deffn {Command} {show bgp ipv4 encap summary} {} +@deffnx {Command} {show bgp ipv4 vpn summary} {} +@deffnx {Command} {show bgp ipv6 encap summary} {} +@deffnx {Command} {show bgp ipv6 vpn summary} {} +Print a summary of neighbor connections for the specified AFI/SAFI combination. +@end deffn + @c ----------------------------------------------------------------------- @node Autonomous System @section Autonomous System @@ -459,6 +885,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 @@ -1284,21 +1715,36 @@ log file bgpd.log @node Dump BGP packets and table @section Dump BGP packets and table -@deffn Command {dump bgp all @var{path}} {} -@deffnx Command {dump bgp all @var{path} @var{interval}} {} +@deffn Command {dump bgp all @var{path} [@var{interval}]} {} +@deffnx Command {dump bgp all-et @var{path} [@var{interval}]} {} +@deffnx Command {no dump bgp all [@var{path}] [@var{interval}]} {} Dump all BGP packet and events to @var{path} file. +If @var{interval} is set, a new file will be created for echo @var{interval} of seconds. +The path @var{path} can be set with date and time formatting (strftime). +The type ‘all-et’ enables support for Extended Timestamp Header (@pxref{Packet Binary Dump Format}). +(@pxref{Packet Binary Dump Format}) @end deffn -@deffn Command {dump bgp updates @var{path}} {} -@deffnx Command {dump bgp updates @var{path} @var{interval}} {} -Dump BGP updates to @var{path} file. +@deffn Command {dump bgp updates @var{path} [@var{interval}]} {} +@deffnx Command {dump bgp updates-et @var{path} [@var{interval}]} {} +@deffnx Command {no dump bgp updates [@var{path}] [@var{interval}]} {} +Dump only BGP updates messages to @var{path} file. +If @var{interval} is set, a new file will be created for echo @var{interval} of seconds. +The path @var{path} can be set with date and time formatting (strftime). +The type ‘updates-et’ enables support for Extended Timestamp Header (@pxref{Packet Binary Dump Format}). @end deffn -@deffn Command {dump bgp routes @var{path}} {} -@deffnx Command {dump bgp routes @var{path}} {} +@deffn Command {dump bgp routes-mrt @var{path}} {} +@deffnx Command {dump bgp routes-mrt @var{path} @var{interval}} {} +@deffnx Command {no dump bgp route-mrt [@var{path}] [@var{interval}]} {} Dump whole BGP routing table to @var{path}. This is heavy process. +The path @var{path} can be set with date and time formatting (strftime). +If @var{interval} is set, a new file will be created for echo @var{interval} of seconds. @end deffn +Note: the interval variable can also be set using hours and minutes: 04h20m00. + + @node BGP Configuration Examples @section BGP Configuration Examples diff --git a/doc/defines.texi.in b/doc/defines.texi similarity index 72% rename from doc/defines.texi.in rename to doc/defines.texi index a4badef0e..b7b529ec3 100644 --- a/doc/defines.texi.in +++ b/doc/defines.texi @@ -1,10 +1,9 @@ @c -*- texinfo -*- -@c @configure_input@ @c Set variables -@set PACKAGE_NAME @PACKAGE_NAME@ -@set PACKAGE_TARNAME @PACKAGE_TARNAME@ -@set PACKAGE_STRING @PACKAGE_STRING@ +@set PACKAGE_NAME Quagga +@set PACKAGE_TARNAME quagga +@set PACKAGE_STRING Quagga 1.2.0 @set AUTHORS Kunihiro Ishiguro, et al. @set COPYRIGHT_YEAR 1999-2005 @set COPYRIGHT_STR Copyright @copyright{} @value{COPYRIGHT_YEAR} @value{AUTHORS} diff --git a/doc/fig-rs-processing.dia b/doc/fig-rs-processing.dia index b2bf213da..de7d79f69 100644 --- a/doc/fig-rs-processing.dia +++ b/doc/fig-rs-processing.dia @@ -62,7 +62,7 @@ - + @@ -115,12 +115,12 @@ - + - + @@ -146,6 +146,9 @@ Selection# + + + @@ -153,7 +156,7 @@ Selection# - + @@ -202,12 +205,12 @@ Selection# - + - + @@ -232,6 +235,9 @@ Loc-RIB# + + + @@ -241,7 +247,7 @@ Loc-RIB# - + @@ -274,7 +280,7 @@ Loc-RIB# - + @@ -300,7 +306,7 @@ Loc-RIB# - + @@ -308,7 +314,7 @@ Loc-RIB# - + @@ -342,7 +348,7 @@ Loc-RIB# - + @@ -376,7 +382,7 @@ Loc-RIB# - + @@ -409,7 +415,7 @@ Loc-RIB# - + @@ -442,7 +448,7 @@ Loc-RIB# - + @@ -476,7 +482,7 @@ Loc-RIB# - + @@ -510,7 +516,7 @@ Loc-RIB# - + @@ -538,12 +544,12 @@ Loc-RIB# - + - + - + @@ -557,7 +563,7 @@ Loc-RIB# - + @@ -567,13 +573,16 @@ Loc-RIB# + + + - + - + - + @@ -587,7 +596,7 @@ Loc-RIB# - + @@ -597,6 +606,9 @@ Loc-RIB# + + + @@ -625,12 +637,12 @@ Loc-RIB# - + - + @@ -654,6 +666,9 @@ Loc-RIB# + + + @@ -683,12 +698,12 @@ Loc-RIB# - + - + @@ -712,6 +727,9 @@ Loc-RIB# + + + @@ -741,12 +759,12 @@ Loc-RIB# - + - + @@ -770,6 +788,9 @@ Loc-RIB# + + + @@ -799,12 +820,12 @@ Loc-RIB# - + - + @@ -828,272 +849,17 @@ Loc-RIB# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #X# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #“Out” Filter -for Peer X# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ## - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #“In” Filter -for Peer X# - - - - - - - - - - - - - - - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #X# - - - - - - - - - - - - - - - - - - - - - + - + - + @@ -1107,7 +873,7 @@ for Peer X# - + @@ -1117,13 +883,16 @@ for Peer X# + + + - + - + - + @@ -1137,7 +906,7 @@ for Peer X# - + @@ -1147,9 +916,12 @@ for Peer X# + + + - + @@ -1173,7 +945,7 @@ for Peer X# - + @@ -1199,12 +971,12 @@ for Peer X# - + - + @@ -1230,9 +1002,12 @@ Selection# + + + - + @@ -1258,12 +1033,12 @@ Selection# - + - + @@ -1288,13 +1063,16 @@ For C# + + + - + - + @@ -1316,12 +1094,12 @@ For C# - + - + @@ -1345,7 +1123,7 @@ For C# - + @@ -1371,12 +1149,12 @@ For C# - + - + @@ -1402,9 +1180,12 @@ Selection# + + + - + @@ -1430,12 +1211,12 @@ Selection# - + - + @@ -1460,13 +1241,16 @@ For D# + + + - + - + @@ -1488,12 +1272,12 @@ For D# - + - + @@ -1517,7 +1301,7 @@ For D# - + @@ -1543,12 +1327,12 @@ For D# - + - + @@ -1574,9 +1358,12 @@ Selection# + + + - + @@ -1602,12 +1389,12 @@ Selection# - + - + @@ -1632,13 +1419,16 @@ For B# + + + - + - + @@ -1661,12 +1451,12 @@ For B# - + - + @@ -1677,6 +1467,9 @@ For B# + + + @@ -1694,11 +1487,11 @@ For B# - + - + @@ -1724,12 +1517,12 @@ For B# - + - + @@ -1753,14 +1546,17 @@ For B# + + + - + - + @@ -1785,12 +1581,12 @@ For B# - - + + - + @@ -1816,12 +1612,12 @@ For B# - + - + @@ -1845,14 +1641,17 @@ For B# + + + - + - + @@ -1877,12 +1676,12 @@ For B# - - + + - + @@ -1908,12 +1707,12 @@ For B# - + - + @@ -1937,10 +1736,13 @@ For B# + + + - + @@ -1966,12 +1768,12 @@ For B# - + - + @@ -1995,10 +1797,13 @@ For B# + + + - + @@ -2024,12 +1829,12 @@ For B# - + - + @@ -2053,10 +1858,13 @@ For B# + + + - + @@ -2082,12 +1890,12 @@ For B# - + - + @@ -2111,10 +1919,13 @@ For B# + + + - + @@ -2140,12 +1951,12 @@ For B# - + - + @@ -2169,10 +1980,13 @@ For B# + + + - + @@ -2198,12 +2012,12 @@ For B# - + - + @@ -2227,10 +2041,13 @@ For B# + + + - + @@ -2256,12 +2073,12 @@ For B# - + - + @@ -2285,10 +2102,13 @@ For B# + + + - + @@ -2314,12 +2134,12 @@ For B# - + - + @@ -2343,10 +2163,13 @@ For B# + + + - + @@ -2372,12 +2195,12 @@ For B# - + - + @@ -2401,10 +2224,13 @@ For B# + + + - + @@ -2430,12 +2256,12 @@ For B# - + - + @@ -2459,10 +2285,13 @@ For B# + + + - + @@ -2488,12 +2317,12 @@ For B# - + - + @@ -2517,10 +2346,13 @@ For B# + + + - + @@ -2546,12 +2378,12 @@ For B# - + - + @@ -2575,10 +2407,13 @@ For B# + + + - + @@ -2604,12 +2439,12 @@ For B# - + - + @@ -2633,14 +2468,17 @@ For B# + + + - + - + @@ -2651,6 +2489,9 @@ For B# + + + @@ -2668,15 +2509,15 @@ For B# - + - + - + @@ -2687,6 +2528,9 @@ For B# + + + @@ -2704,15 +2548,15 @@ For B# - + - + - + @@ -2725,6 +2569,9 @@ For B# + + + @@ -2741,15 +2588,15 @@ For B# - + - + - + @@ -2762,6 +2609,9 @@ For B# + + + @@ -2778,15 +2628,15 @@ For B# - + - + - + @@ -2797,6 +2647,9 @@ For B# + + + @@ -2814,15 +2667,15 @@ For B# - + - + - + @@ -2833,6 +2686,9 @@ For B# + + + @@ -2850,15 +2706,15 @@ For B# - + - + - + @@ -2869,6 +2725,9 @@ For B# + + + @@ -2886,15 +2745,15 @@ For B# - + - + - + @@ -2905,6 +2764,9 @@ For B# + + + @@ -2922,15 +2784,15 @@ For B# - + - + - + @@ -2955,16 +2817,16 @@ For B# - - + + - + - + @@ -2989,16 +2851,16 @@ For B# - - + + - + - + @@ -3023,16 +2885,16 @@ For B# - - + + - + - + @@ -3057,16 +2919,16 @@ For B# - - + + - + - + @@ -3091,16 +2953,16 @@ For B# - - + + - + - + @@ -3125,16 +2987,16 @@ For B# - - + + - + - + @@ -3159,16 +3021,16 @@ For B# - - + + - + - + @@ -3193,16 +3055,16 @@ For B# - - + + - + - + @@ -3227,16 +3089,16 @@ For B# - - + + - + - + @@ -3261,16 +3123,16 @@ For B# - - + + - + - + @@ -3295,16 +3157,16 @@ For B# - - + + - + - + @@ -3329,16 +3191,16 @@ For B# - - + + - + - + @@ -3363,16 +3225,16 @@ For B# - - + + - + - + @@ -3397,16 +3259,16 @@ For B# - - + + - + - + @@ -3431,16 +3293,16 @@ For B# - - + + - + - + @@ -3465,54 +3327,61 @@ For B# - - + + - + - + - + - - + + + - - + + - - + + - - + + - - + + + + + + + + - + - + - + - ## + #To RS-Client B# - + - + @@ -3522,226 +3391,496 @@ For B# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #B# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #C# + + + + + + + + + + + + + + + + + + + + + + + + - + - + - #Export Policy -of RS-Client X# + #To RS-Client C# - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #X# - - - - - - - - - - - - - - - - - + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #A# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #To Peer A# + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - - - - - + + + - - + + - - + + - - + + - - - - + + - - + + - - - - ## - - - - - - - - - - - - - - - - - + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #D# + + + + + + + + + + + + + + + + + + + + + + + + - + - + - #Import Policy -of RS-Client X# + #To RS-Client D# - + - + - + + + + - + + + - + - + - + - + - + - + - - - - + - + - + - #X# + ## - + - + @@ -3751,79 +3890,54 @@ of RS-Client X# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - + - + - + - #To RS-Client B# + #“In” Filter +for Peer X# - + - + - + + + + - + - + - + - + @@ -3832,7 +3946,7 @@ of RS-Client X# - + @@ -3841,17 +3955,17 @@ of RS-Client X# - + - + - + - #B# + #X# @@ -3860,7 +3974,7 @@ of RS-Client X# - + @@ -3870,56 +3984,52 @@ of RS-Client X# + + + - + - + - - - - - - - - + - - + + - - + + - - + + - - + + - - + + - + - + - + - + - + - + @@ -3931,17 +4041,17 @@ of RS-Client X# - + - + - + - #C# + #X# @@ -3950,7 +4060,7 @@ of RS-Client X# - + @@ -3960,28 +4070,90 @@ of RS-Client X# + + + - + + + + + + + + + + + #“Out” Filter +for Peer X# + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + - + - #To RS-Client C# + ## - + - + @@ -3991,28 +4163,63 @@ of RS-Client X# + + + + + + + + + + + + + + + #Export Policy +of RS-Client X# + + + + + + + + + + + + + + + + + + + + + - - - + - + - + - + - + - + @@ -4021,17 +4228,17 @@ of RS-Client X# - + - + - + - #A# + #X# @@ -4040,131 +4247,135 @@ of RS-Client X# - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #To Peer A# - - - - - - - - - - - - - - - - - + + + + + - + - + - + - - - + + - - + + - - + + - - + + - - + + - - + + + + - - + + + + + + + ## + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Import Policy +of RS-Client X# + + + + + + + + + + + + + + + + + + + + - + - + - + - + - + - + - + @@ -4173,17 +4384,17 @@ of RS-Client X# - + - + - + - #D# + #X# @@ -4192,48 +4403,24 @@ of RS-Client X# - + - + + + + + + + - - - - - - - - - - - #To RS-Client D# - - - - - - - - - - - - - - - - - - - diff --git a/doc/fig-rs-processing.png b/doc/fig-rs-processing.png index 1f77263b1..c0cac5c9a 100644 Binary files a/doc/fig-rs-processing.png and b/doc/fig-rs-processing.png differ diff --git a/doc/fig_topologies_full.dia b/doc/fig_topologies_full.dia deleted file mode 100644 index 7ec3398f5..000000000 --- a/doc/fig_topologies_full.dia +++ /dev/null @@ -1,533 +0,0 @@ - - - - - - - - - - - - - #A4# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #RF2# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #RF4# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #RF3# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #RF1# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/doc/fig_topologies_full.png b/doc/fig_topologies_full.png deleted file mode 100644 index d39e5e242..000000000 Binary files a/doc/fig_topologies_full.png and /dev/null differ diff --git a/doc/fig_topologies_rs.dia b/doc/fig_topologies_rs.dia deleted file mode 100644 index f8aa18d67..000000000 --- a/doc/fig_topologies_rs.dia +++ /dev/null @@ -1,499 +0,0 @@ - - - - - - - - - - - - - #A4# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #RS# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #RF2# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #RF4# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #RF3# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #RF1# - - - - - - - - - - - - - - - - - - - - - - diff --git a/doc/fig_topologies_rs.png b/doc/fig_topologies_rs.png deleted file mode 100644 index 014225c8f..000000000 Binary files a/doc/fig_topologies_rs.png and /dev/null differ diff --git a/doc/install.texi b/doc/install.texi index 16e29c99e..4ad282a3f 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 @@ -79,21 +75,40 @@ file does not match to the current running kernel, configure script will not turn on netlink support. @item --enable-snmp Enable SNMP support. By default, SNMP support is disabled. -@item --enable-opaque-lsa -Enable support for Opaque LSAs (RFC2370) in ospfd. +@item --disable-opaque-lsa +Disable support for Opaque LSAs (RFC2370) in ospfd. @item --disable-ospfapi Disable support for OSPF-API, an API to interface directly with ospfd. OSPF-API is enabled if --enable-opaque-lsa is set. @item --disable-ospfclient Disable building of the example OSPF-API client. -@item --enable-ospf-te -Enable support for OSPF Traffic Engineering Extension (internet-draft) this +@item --disable-ospf-te +Disable support for OSPF Traffic Engineering Extension (RFC3630) this requires support for Opaque LSAs. +@item --disable-ospf-ri +Disable support for OSPF Router Information (RFC4970 & RFC5088) this +requires support for Opaque LSAs and Traffic Engineering. +@item --enable-isisd +Build isisd. +@item --enable-isis-topology +Enable IS-IS topology generator. +@item --enable-isis-te +Enable Traffic Engineering Extension for ISIS (RFC5305) @item --enable-multipath=@var{ARG} 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 --enable-rtadv -Enable support IPV6 router advertisement in zebra. +@item --disable-rtadv +Disable support IPV6 router advertisement in zebra. +@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 @@ -267,6 +282,8 @@ bgpd 2605/tcp # BGPd vty ospf6d 2606/tcp # OSPF6d vty ospfapi 2607/tcp # ospfapi isisd 2608/tcp # ISISd vty +pimd 2611/tcp # PIMd vty +nhrpd 2612/tcp # nhrpd 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 b6cc43763..5be2babed 100644 --- a/doc/ipv6.texi +++ b/doc/ipv6.texi @@ -2,7 +2,7 @@ @chapter IPv6 Support Quagga fully supports IPv6 routing. As described so far, Quagga supports -RIPng, OSPFv3, Babel and BGP-4+. You can give IPv6 addresses to an interface +RIPng, OSPFv3, and BGP-4+. You can give IPv6 addresses to an interface and configure static IPv6 routing information. Quagga IPv6 also provides automatic address configuration via a feature called @code{address auto configuration}. To do it, the router must send router advertisement @@ -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/isisd.texi b/doc/isisd.texi new file mode 100644 index 000000000..bbc289675 --- /dev/null +++ b/doc/isisd.texi @@ -0,0 +1,432 @@ +@cindex ISIS +@node ISIS +@chapter ISIS + +@acronym{ISIS,Intermediate System to Intermediate System} is a routing protocol +which is described in @cite{ISO10589, RFC1195, RFC5308}. ISIS is an +@acronym{IGP,Interior Gateway Protocol}. Compared with @acronym{RIP}, +@acronym{ISIS} can provide scalable network support and faster +convergence times like @acronym{OSPF}. ISIS is widely used in large networks such as +@acronym{ISP,Internet Service Provider} and carrier backbone networks. + +@menu +* Configuring isisd:: +* ISIS router:: +* ISIS Timer:: +* ISIS region:: +* ISIS interface:: +* Showing ISIS information:: +* ISIS Traffic Engineering:: +* Debugging ISIS:: +* ISIS Configuration Examples:: +@end menu + +@node Configuring isisd +@section Configuring isisd + +There are no @command{isisd} specific options. Common options can be +specified (@pxref{Common Invocation Options}) to @command{isisd}. +@command{isisd} needs to acquire interface information from +@command{zebra} in order to function. Therefore @command{zebra} must be +running before invoking @command{isisd}. Also, if @command{zebra} is +restarted then @command{isisd} must be too. + +Like other daemons, @command{isisd} configuration is done in @acronym{ISIS} +specific configuration file @file{isisd.conf}. + +@node ISIS router +@section ISIS router + +To start ISIS process you have to specify the ISIS router. As of this +writing, @command{isisd} does not support multiple ISIS processes. + +@deffn Command {router isis WORD} {} +@deffnx Command {no router isis WORD} {} +@anchor{router isis WORD}Enable or disable the ISIS process by specifying the ISIS domain with 'WORD'. +@command{isisd} does not yet support multiple ISIS processes but you must specify +the name of ISIS process. The ISIS process name 'WORD' is then used for interface +(see command @ref{ip router isis WORD}). +@end deffn + +@deffn {ISIS Command} {net XX.XXXX. ... .XXX.XX} {} +@deffnx {ISIS Command} {no net XX.XXXX. ... .XXX.XX} {} +Set/Unset network entity title (NET) provided in ISO format. +@end deffn + +@deffn {ISIS Command} {hostname dynamic} {} +@deffnx {ISIS Command} {no hostname dynamic} {} +Enable support for dynamic hostname. +@end deffn + +@deffn {ISIS Command} {area-password [clear | md5] } {} +@deffnx {ISIS Command} {domain-password [clear | md5] } {} +@deffnx {ISIS Command} {no area-password} {} +@deffnx {ISIS Command} {no domain-password} {} +Configure the authentication password for an area, respectively a domain, +as clear text or md5 one. +@end deffn + +@deffn {ISIS Command} {log-adjacency-changes} {} +@deffnx {ISIS Command} {no log-adjacency-changes} {} +Log changes in adjacency state. +@end deffn + +@deffn {ISIS Command} {metric-style [narrow | transition | wide]} {} +@deffnx {ISIS Command} {no metric-style} {} +@anchor{metric-style}Set old-style (ISO 10589) or new-style packet formats: + - narrow Use old style of TLVs with narrow metric + - transition Send and accept both styles of TLVs during transition + - wide Use new style of TLVs to carry wider metric +@end deffn + +@deffn {ISIS Command} {set-overload-bit} {} +@deffnx {ISIS Command} {no set-overload-bit} {} +Set overload bit to avoid any transit traffic. +@end deffn + +@node ISIS Timer +@section ISIS Timer + +@deffn {ISIS Command} {lsp-gen-interval <1-120>} {} +@deffnx {ISIS Command} {lsp-gen-interval [level-1 | level-2] <1-120>} {} +@deffnx {ISIS Command} {no lsp-gen-interval} {} +@deffnx {ISIS Command} {no lsp-gen-interval [level-1 | level-2]} {} +Set minimum interval in seconds between regenerating same LSP, +globally, for an area (level-1) or a domain (level-2). +@end deffn + +@deffn {ISIS Command} {lsp-refresh-interval <1-65235>} {} +@deffnx {ISIS Command} {lsp-refresh-interval [level-1 | level-2] <1-65235>} {} +@deffnx {ISIS Command} {no lsp-refresh-interval} {} +@deffnx {ISIS Command} {no lsp-refresh-interval [level-1 | level-2]} {} +Set LSP refresh interval in seconds, globally, for an area (level-1) or a domain (level-2). +@end deffn + +@deffn {ISIS Command} {lsp-refresh-interval <1-65235>} {} +@deffnx {ISIS Command} {lsp-refresh-interval [level-1 | level-2] <1-65235>} {} +@deffnx {ISIS Command} {no lsp-refresh-interval} {} +@deffnx {ISIS Command} {no lsp-refresh-interval [level-1 | level-2]} {} +Set LSP refresh interval in seconds, globally, for an area (level-1) or a domain (level-2). +@end deffn + +@deffn {ISIS Command} {max-lsp-lifetime <360-65535>} {} +@deffnx {ISIS Command} {max-lsp-lifetime [level-1 | level-2] <360-65535>} {} +@deffnx {ISIS Command} {no max-lsp-lifetime} {} +@deffnx {ISIS Command} {no max-lsp-lifetime [level-1 | level-2]} {} +Set LSP maximum LSP lifetime in seconds, globally, for an area (level-1) or a domain (level-2). +@end deffn + +@deffn {ISIS Command} {spf-interval <1-120>} {} +@deffnx {ISIS Command} {spf-interval [level-1 | level-2] <1-120>} {} +@deffnx {ISIS Command} {no spf-interval} {} +@deffnx {ISIS Command} {no spf-interval [level-1 | level-2]} {} +Set minimum interval between consecutive SPF calculations in seconds. +@end deffn + +@node ISIS region +@section ISIS region + +@deffn {ISIS Command} {is-type [level-1 | level-1-2 | level-2-only]} {} +@deffnx {ISIS Command} {no is-type} {} +Define the ISIS router behavior: + - level-1 Act as a station router only + - level-1-2 Act as both a station router and an area router + - level-2-only Act as an area router only +@end deffn + +@node ISIS interface +@section ISIS interface + +@deffn {Interface Command} {ip router isis WORD} {} +@deffnx {Interface Command} {no ip router isis WORD} {} +@anchor{ip router isis WORD}Activate ISIS adjacency on this interface. Note that the name +of ISIS instance must be the same as the one used to configure the ISIS process +(see command @ref{router isis WORD}). +@end deffn + +@deffn {Interface Command} {isis circuit-type [level-1 | level-1-2 | level-2]} {} +@deffnx {Interface Command} {no isis circuit-type} {} +Configure circuit type for interface: + - level-1 Level-1 only adjacencies are formed + - level-1-2 Level-1-2 adjacencies are formed + - level-2-only Level-2 only adjacencies are formed +@end deffn + +@deffn {Interface Command} {isis csnp-interval <1-600>} {} +@deffnx {Interface Command} {isis csnp-interval <1-600> [level-1 | level-2]} {} +@deffnx {Interface Command} {no isis csnp-interval} {} +@deffnx {Interface Command} {no isis csnp-interval [level-1 | level-2]} {} +Set CSNP interval in seconds globally, for an area (level-1) or a domain (level-2). +@end deffn + +@deffn {Interface Command} {isis hello padding} {} +Add padding to IS-IS hello packets. +@end deffn + +@deffn {Interface Command} {isis hello-interval <1-600>} {} +@deffnx {Interface Command} {isis hello-interval <1-600> [level-1 | level-2]} {} +@deffnx {Interface Command} {no isis hello-interval} {} +@deffnx {Interface Command} {no isis hello-interval [level-1 | level-2]} {} +Set Hello interval in seconds globally, for an area (level-1) or a domain (level-2). +@end deffn + +@deffn {Interface Command} {isis hello-multiplier <2-100>} {} +@deffnx {Interface Command} {isis hello-multiplier <2-100> [level-1 | level-2]} {} +@deffnx {Interface Command} {no isis hello-multiplier} {} +@deffnx {Interface Command} {no isis hello-multiplier [level-1 | level-2]} {} +Set multiplier for Hello holding time globally, for an area (level-1) or a domain (level-2). +@end deffn + +@deffn {Interface Command} {isis metric [<0-255> | <0-16777215>]} {} +@deffnx {Interface Command} {isis metric [<0-255> | <0-16777215>] [level-1 | level-2]} {} +@deffnx {Interface Command} {no isis metric} {} +@deffnx {Interface Command} {no isis metric [level-1 | level-2]} {} +Set default metric value globally, for an area (level-1) or a domain (level-2). +Max value depend if metric support narrow or wide value (see command @ref{metric-style}). +@end deffn + +@deffn {Interface Command} {isis network point-to-point} {} +@deffnx {Interface Command} {no isis network point-to-point} {} +Set network type to 'Point-to-Point' (broadcast by default). +@end deffn + +@deffn {Interface Command} {isis passive} {} +@deffnx {Interface Command} {no isis passive} {} +Configure the passive mode for this interface. +@end deffn + +@deffn {Interface Command} {isis password [clear | md5] } {} +@deffnx {Interface Command} {no isis password} {} +Configure the authentication password (clear or encoded text) for the interface. +@end deffn + +@deffn {Interface Command} {isis priority <0-127>} {} +@deffnx {Interface Command} {isis priority <0-127> [level-1 | level-2]} {} +@deffnx {Interface Command} {no isis priority} {} +@deffnx {Interface Command} {no isis priority [level-1 | level-2]} {} +Set priority for Designated Router election, globally, for the area (level-1) +or the domain (level-2). +@end deffn + +@deffn {Interface Command} {isis psnp-interval <1-120>} {} +@deffnx {Interface Command} {isis psnp-interval <1-120> [level-1 | level-2]} {} +@deffnx {Interface Command} {no isis psnp-interval} {} +@deffnx {Interface Command} {no isis psnp-interval [level-1 | level-2]} {} +Set PSNP interval in seconds globally, for an area (level-1) or a domain (level-2). +@end deffn + +@node Showing ISIS information +@section Showing ISIS information + +@deffn {Command} {show isis summary} {} +Show summary information about ISIS. +@end deffn + +@deffn {Command} {show isis hostname} {} +Show information about ISIS node. +@end deffn + +@deffn {Command} {show isis interface} {} +@deffnx {Command} {show isis interface detail} {} +@deffnx {Command} {show isis interface } {} +Show state and configuration of ISIS specified interface, or all +interfaces if no interface is given with or without details. +@end deffn + +@deffn {Command} {show isis neighbor} {} +@deffnx {Command} {show isis neighbor } {} +@deffnx {Command} {show isis neighbor detail} {} +Show state and information of ISIS specified neighbor, or all +neighbors if no system id is given with or without details. +@end deffn + +@deffn {Command} {show isis database} {} +@deffnx {Command} {show isis database [detail]} {} +@deffnx {Command} {show isis database [detail]} {} +@deffnx {Command} {show isis database detail } {} +Show the ISIS database globally, for a specific LSP id without or with details. +@end deffn + +@deffn {Command} {show isis topology} {} +@deffnx {Command} {show isis topology [level-1|level-2]} {} +Show topology IS-IS paths to Intermediate Systems, globally, +in area (level-1) or domain (level-2). +@end deffn + +@deffn {Command} {show ip route isis} {} +Show the ISIS routing table, as determined by the most recent SPF calculation. +@end deffn + +@node ISIS Traffic Engineering +@section Traffic Engineering + +@deffn {ISIS Command} {mpls-te on} {} +@deffnx {ISIS Command} {no mpls-te} {} +Enable Traffic Engineering LSP flooding. +@end deffn + +@deffn {ISIS Command} {mpls-te router-address } {} +@deffnx {ISIS Command} {no mpls-te router-address} {} +Configure stable IP address for MPLS-TE. +@end deffn + +@deffn {Command} {show isis mpls-te interface} {} +@deffnx {Command} {show isis mpls-te interface @var{interface}} {} +Show MPLS Traffic Engineering parameters for all or specified interface. +@end deffn + +@deffn {Command} {show isis mpls-te router} {} +Show Traffic Engineering router parameters. +@end deffn + +@node Debugging ISIS +@section Debugging ISIS + +@deffn {Command} {debug isis adj-packets} {} +@deffnx {Command} {no debug isis adj-packets} {} +IS-IS Adjacency related packets. +@end deffn + +@deffn {Command} {debug isis checksum-errors} {} +@deffnx {Command} {no debug isis checksum-errors} {} +IS-IS LSP checksum errors. +@end deffn + +@deffn {Command} {debug isis events} {} +@deffnx {Command} {no debug isis events} {} +IS-IS Events. +@end deffn + +@deffn {Command} {debug isis local-updates} {} +@deffnx {Command} {no debug isis local-updates} {} +IS-IS local update packets. +@end deffn + +@deffn {Command} {debug isis packet-dump} {} +@deffnx {Command} {no debug isis packet-dump} {} +IS-IS packet dump. +@end deffn + +@deffn {Command} {debug isis protocol-errors} {} +@deffnx {Command} {no debug isis protocol-errors} {} +IS-IS LSP protocol errors. +@end deffn + +@deffn {Command} {debug isis route-events} {} +@deffnx {Command} {no debug isis route-events} {} +IS-IS Route related events. +@end deffn + +@deffn {Command} {debug isis snp-packets} {} +@deffnx {Command} {no debug isis snp-packets} {} +IS-IS CSNP/PSNP packets. +@end deffn + +@deffn {Command} {debug isis spf-events} {} +@deffnx {Command} {debug isis spf-statistics} {} +@deffnx {Command} {debug isis spf-triggers} {} +@deffnx {Command} {no debug isis spf-events} {} +@deffnx {Command} {no debug isis spf-statistics} {} +@deffnx {Command} {no debug isis spf-triggers} {} +IS-IS Shortest Path First Events, Timing and Statistic Data +and triggering events. +@end deffn + +@deffn {Command} {debug isis update-packets} {} +@deffnx {Command} {no debug isis update-packets} {} +Update related packets. +@end deffn + +@deffn {Command} {show debugging isis} {} +Print which ISIS debug level is activate. +@end deffn + +@node ISIS Configuration Examples +@section ISIS Configuration Examples +A simple example, with MD5 authentication enabled: + +@example +@group +! +interface eth0 + ip router isis FOO + isis network point-to-point + isis circuit-type level-2-only +! +router isis FOO +net 47.0023.0000.0000.0000.0000.0000.0000.1900.0004.00 + metric-style wide + is-type level-2-only +@end group +@end example + + +A Traffic Engineering configuration, with Inter-ASv2 support. + + - First, the 'zebra.conf' part: + +@example +@group +hostname HOSTNAME +password PASSWORD +log file /var/log/zebra.log +! +interface eth0 + ip address 10.2.2.2/24 + mpls-te on + mpls-te link metric 10 + mpls-te link max-bw 1.25e+06 + mpls-te link max-rsv-bw 1.25e+06 + mpls-te link unrsv-bw 0 1.25e+06 + mpls-te link unrsv-bw 1 1.25e+06 + mpls-te link unrsv-bw 2 1.25e+06 + mpls-te link unrsv-bw 3 1.25e+06 + mpls-te link unrsv-bw 4 1.25e+06 + mpls-te link unrsv-bw 5 1.25e+06 + mpls-te link unrsv-bw 6 1.25e+06 + mpls-te link unrsv-bw 7 1.25e+06 + mpls-te link rsc-clsclr 0xab +! +interface eth1 + ip address 10.1.1.1/24 + mpls-te on + mpls-te link metric 10 + mpls-te link max-bw 1.25e+06 + mpls-te link max-rsv-bw 1.25e+06 + mpls-te link unrsv-bw 0 1.25e+06 + mpls-te link unrsv-bw 1 1.25e+06 + mpls-te link unrsv-bw 2 1.25e+06 + mpls-te link unrsv-bw 3 1.25e+06 + mpls-te link unrsv-bw 4 1.25e+06 + mpls-te link unrsv-bw 5 1.25e+06 + mpls-te link unrsv-bw 6 1.25e+06 + mpls-te link unrsv-bw 7 1.25e+06 + mpls-te link rsc-clsclr 0xab + mpls-te neighbor 10.1.1.2 as 65000 +@end group +@end example + + - Then the 'isisd.conf' itself: + +@example +@group +hostname HOSTNAME +password PASSWORD +log file /var/log/isisd.log +! +! +interface eth0 + ip router isis FOO +! +interface eth1 + ip router isis FOO +! +! +router isis FOO + isis net 47.0023.0000.0000.0000.0000.0000.0000.1900.0004.00 + mpls-te on + mpls-te router-address 10.1.1.1 +! +line vty +@end group +@end example diff --git a/doc/main.texi b/doc/main.texi index a57591377..6d42c04e7 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 @@ -40,6 +42,14 @@ When program terminates, retain routes added by zebra. @node Interface Commands @section Interface Commands +@menu +* Standard Commands:: +* Link Parameters Commands:: +@end menu + +@node Standard Commands +@subsection Standard Commands + @deffn Command {interface @var{ifname}} {} @end deffn @@ -72,18 +82,71 @@ Enable or disables multicast flag for the interface. @deffn {Interface Command} {bandwidth <1-10000000>} {} @deffnx {Interface Command} {no bandwidth <1-10000000>} {} -Set bandwidth value of the interface in kilobits/sec. This is for -calculating OSPF cost. This command does not affect the actual device +Set bandwidth value of the interface in kilobits/sec. This is for +calculating OSPF cost. This command does not affect the actual device configuration. @end deffn @deffn {Interface Command} {link-detect} {} @deffnx {Interface Command} {no link-detect} {} -Enable/disable link-detect on platforms which support this. Currently +Enable/disable link-detect on platforms which support this. Currently only Linux and Solaris, and only where network interface drivers support reporting link-state via the IFF_RUNNING flag. @end deffn +@node Link Parameters Commands +@subsection Link Parameters Commands + +@deffn {Interface Command} {link-params} {} +@deffnx {Interface Command} {no link-param} {} +Enter into the link parameters sub node. At least 'enable' must be set to activate the link parameters, +and consequently Traffic Engineering on this interface. MPLS-TE must be enable at the OSPF (@ref{OSPF Traffic Engineering}) +or ISIS (@ref{ISIS Traffic Engineering}) router level in complement to this. +Disable link parameters for this interface. +@end deffn + +Under link parameter statement, the following commands set the different TE values: + +@deffn link-params {enable} +Enable link parameters for this interface. +@end deffn + +@deffn link-params {metric <0-4294967295>} {} +@deffnx link-params {max-bw @var{bandwidth}} {} +@deffnx link-params {max-rsv-bw @var{bandwidth}} {} +@deffnx link-params {unrsv-bw <0-7> @var{bandwidth}} {} +@deffnx link-params {admin-grp @var{bandwidth}} {} +These commands specifies the Traffic Engineering parameters of the interface in conformity to RFC3630 (OSPF) +or RFC5305 (ISIS). +There are respectively the TE Metric (different from the OSPF or ISIS metric), Maximum Bandwidth (interface speed +by default), Maximum Reservable Bandwidth, Unreserved Bandwidth for each 0-7 priority and Admin Group (ISIS) or +Resource Class/Color (OSPF). + +Note that @var{bandwidth} are specified in IEEE floating point format and express in Bytes/second. +@end deffn + +@deffn link-param {delay <0-16777215> [min <0-16777215> | max <0-16777215>]} {} +@deffnx link-param {delay-variation <0-16777215>} {} +@deffnx link-param {packet-loss @var{percentage}} {} +@deffnx link-param {res-bw @var{bandwidth}} {} +@deffnx link-param {ava-bw @var{bandwidth}} {} +@deffnx link-param {use-bw @var{bandwidth}} {} +These command specifies additionnal Traffic Engineering parameters of the interface in conformity to +draft-ietf-ospf-te-metrics-extension-05.txt and draft-ietf-isis-te-metrics-extension-03.txt. There are +respectively the delay, jitter, loss, available bandwidth, reservable bandwidth and utilized bandwidth. + +Note that @var{bandwidth} are specified in IEEE floating point format and express in Bytes/second. +Delays and delay variation are express in micro-second (µs). Loss is specified in @var{percentage} ranging +from 0 to 50.331642% by step of 0.000003. +@end deffn + +@deffn link-param {neighbor as <0-65535>} {} +@deffnx link-param {no neighbor} {} +Specifies the remote ASBR IP address and Autonomous System (AS) number for InterASv2 link in OSPF (RFC5392). +Note that this option is not yet supported for ISIS (RFC5316). +@end deffn + + @node Static Route Commands @section Static Route Commands @@ -114,9 +177,9 @@ A.B.C.D format, user must define @var{netmask} value with A.B.C.D format. @var{gateway} is same option as above command @example -ip route 10.0.0.0 255.255.255.0 10.0.0.2 -ip route 10.0.0.0 255.255.255.0 ppp0 -ip route 10.0.0.0 255.255.255.0 null0 +ip route 10.0.0.0 255.0.0.0 10.0.0.2 +ip route 10.0.0.0 255.0.0.0 ppp0 +ip route 10.0.0.0 255.0.0.0 null0 @end example These statements are equivalent to those in the previous example. @@ -160,7 +223,7 @@ prevent traffic destined for a prefix to match less-specific routes (eg default) should the specified gateways not be reachable. Eg: @example -zebra> show ip route 10.0.0.0/8 +zebra> show ip route 10.0.0.0/8 Routing entry for 10.0.0.0/8 Known via "static", distance 1, metric 0 10.0.0.2 inactive @@ -180,10 +243,90 @@ These behave similarly to their ipv4 counterparts. @deffn Command {table @var{tableno}} {} Select the primary kernel routing table to be used. This only works for kernels supporting multiple routing tables (like GNU/Linux 2.2.x -and later). After setting @var{tableno} with this command, +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 @@ -212,11 +355,11 @@ Within a route-map, set the preferred source address for matching routes when installing in the kernel. @end deffn -@example The following creates a prefix-list that matches all addresses, a route-map that sets the preferred source address, and applies the route-map to all @command{rip} routes. +@example @group ip prefix-list ANY permit 0.0.0.0/0 le 32 route-map RM1 permit 10 @@ -227,6 +370,78 @@ 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 encapsulation header for the messages exchanged with the FPM is +defined by the file @file{fpm/fpm.h} in the quagga tree. The routes +themselves are encoded in netlink or protobuf format, with netlink +being the default. + +Protobuf is one of a number of new serialization formats wherein the +message schema is expressed in a purpose-built language. Code for +encoding/decoding to/from the wire format is generated from the +schema. Protobuf messages can be extended easily while maintaining +backward-compatibility with older code. Protobuf has the following +advantages over netlink: + +@itemize +@item +Code for serialization/deserialization is generated +automatically. This reduces the likelihood of bugs, allows third-party +programs to be integrated quickly, and makes it easy to add fields. +@item +The message format is not tied to an OS (Linux), and can be evolved +independently. +@end itemize + +As mentioned before, zebra encodes routes sent to the FPM in netlink +format by default. The format can be controlled via the +@code{--fpm_format} command-line option to zebra, which currently +takes the values @code{netlink} and @code{protobuf}. + +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 @@ -235,8 +450,8 @@ Display current routes which zebra holds in its database. @example @group -Router# show ip route -Codes: K - kernel route, C - connected, S - static, R - RIP, +Router# show ip route +Codes: K - kernel route, C - connected, S - static, R - RIP, B - BGP * - FIB route. K* 0.0.0.0/0 203.181.89.241 @@ -271,3 +486,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/ChangeLog.opaque.txt b/doc/mpls/ChangeLog.opaque.txt index 68ddf4c81..afcfaa359 100644 --- a/doc/mpls/ChangeLog.opaque.txt +++ b/doc/mpls/ChangeLog.opaque.txt @@ -1,3 +1,16 @@ +----- * ----- * ----- * ----- * ----- * ----- * ----- * ----- * ----- * ----- +Changes 2013.07.01 + +1. Feature enhancements + + 1.1 Update ospf_te.[c,h] in conformance to RFC3630 and clean the code. + Add new directive to enable MPLS-TE per interface instead of globally + + 1.2 Add support for RFC4970 "Router Information" and RFC5088 "PCE + Capabilities announcement". + + 1.3 Incorporate the mpls documentation into the main stream doc. + ----- * ----- * ----- * ----- * ----- * ----- * ----- * ----- * ----- * ----- Changes 2001.12.03 diff --git a/doc/mpls/opaque_lsa.txt b/doc/mpls/opaque_lsa.txt index 7d5c7fed4..ae89ee34b 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/mpls/ospfd.conf b/doc/mpls/ospfd.conf index 6be11f912..2b15fa4d0 100644 --- a/doc/mpls/ospfd.conf +++ b/doc/mpls/ospfd.conf @@ -17,6 +17,7 @@ debug ospf packet all detail interface fxp0 ip ospf hello-interval 60 ip ospf dead-interval 240 + mpls-te on mpls-te link metric 999 mpls-te link max-bw 1.25e+06 mpls-te link max-rsv-bw 1.25e+06 diff --git a/doc/next-hop-tracking.txt b/doc/next-hop-tracking.txt new file mode 100644 index 000000000..d157866e8 --- /dev/null +++ b/doc/next-hop-tracking.txt @@ -0,0 +1,326 @@ +0. Introduction + +This is the design specification for next hop tracking feature in +Quagga. + +1. Background + +Recursive routes are of the form: + + p/m --> n + [Ex: 1.1.0.0/16 --> 2.2.2.2] + +where 'n' itself is resolved through another route as follows: + + p2/m --> h, interface + [Ex: 2.2.2.0/24 --> 3.3.3.3, eth0] + +Usually, BGP routes are recursive in nature and BGP nexthops get +resolved through an IGP route. IGP usually adds its routes pointing to +an interface (these are called non-recursive routes). + +When BGP receives a recursive route from a peer, it needs to validate +the nexthop. The path is marked valid or invalid based on the +reachability status of the nexthop. Nexthop validation is also +important for BGP decision process as the metric to reach the nexthop +is a parameter to best path selection process. + +As it goes with routing, this is a dynamic process. Route to the +nexthop can change. The nexthop can become unreachable or +reachable. In the current BGP implementation, the nexthop validation +is done periodically in the scanner run. The default scanner run +interval is one minute. Every minute, the scanner task walks the +entire BGP table. It checks the validity of each nexthop with Zebra +(the routing table manager) through a request and response message +exchange between BGP and Zebra process. BGP process is blocked for +that duration. The mechanism has two major drawbacks: + +(1) The scanner task runs to completion. That can potentially starve + the other tasks for long periods of time, based on the BGP table + size and number of nexthops. + +(2) Convergence around routing changes that affect the nexthops can be + long (around a minute with the default intervals). The interval + can be shortened to achieve faster reaction time, but it makes the + first problem worse, with the scanner task consuming most of the + CPU resources. + +"Next hop tracking" feature makes this process event-driven. It +eliminates periodic nexthop validation and introduces an asynchronous +communication path between BGP and Zebra for route change notifications +that can then be acted upon. + +2. Goal + +Stating the obvious, the main goal is to remove the two limitations we +discussed in the previous section. The goals, in a constructive tone, +are the following: + +- fairness: the scanner run should not consume an unjustly high amount + of CPU time. This should give an overall good performance and + response time to other events (route changes, session events, + IO/user interface). + +- convergence: BGP must react to nexthop changes instantly and provide + sub-second convergence. This may involve diverting the routes from + one nexthop to another. + +3. Overview of the changes + +The changes are in both BGP and Zebra modules. The short summary is +the following: + +- Zebra implements a registration mechanism by which clients can + register for next hop notification. Consequently, it maintains a + separate table, per (VRF, AF) pair, of next hops and interested + client-list per next hop. + +- When the main routing table changes in Zebra, it evaluates the next + hop table: for each next hop, it checks if the route table + modifications have changed its state. If so, it notifies the + interested clients. + +- BGP is one such client. It registers the next hops corresponding to + all of its received routes/paths. It also threads the paths against + each nexthop structure. + +- When BGP receives a next hop notification from Zebra, it walks the + corresponding path list. It makes them valid or invalid depending + on the next hop notification. It then re-computes best path for the + corresponding destination. This may result in re-announcing those + destinations to peers. + +4. Design + +4.1. Modules + +The core design introduces an "nht" (next hop tracking) module in BGP +and "rnh" (recursive nexthop) module in Zebra. The "nht" module +provides the following APIs: + +bgp_find_or_add_nexthop() : find or add a nexthop in BGP nexthop table +bgp_find_nexthop() : find a nexthop in BGP nexthop table +bgp_parse_nexthop_update() : parse a nexthop update message coming + from zebra + +The "rnh" module provides the following APIs: + +zebra_add_rnh() : add a recursive nexthop +zebra_delete_rnh() : delete a recursive nexthop +zebra_lookup_rnh() : lookup a recursive nexthop + +zebra_add_rnh_client() : register a client for nexthop notifications + against a recursive nexthop + +zebra_remove_rnh_client(): remove the client registration for a + recursive nexthop + +zebra_evaluate_rnh_table(): (re)evaluate the recursive nexthop table + (most probably because the main routing + table has changed). + +zebra_cleanup_rnh_client(): Cleanup a client from the "rnh" module + data structures (most probably because the + client is going away). + +4.2. Control flow + +The next hop registration control flow is the following: + +<==== BGP Process ====>|<==== Zebra Process ====> + | +receive module nht module | zserv module rnh module +---------------------------------------------------------------------- + | | | +bgp_update_ | | | + main() | bgp_find_or_add_ | | + | nexthop() | | + | | | + | | zserv_nexthop_ | + | | register() | + | | | zebra_add_rnh() + | | | + + +The next hop notification control flow is the following: + +<==== Zebra Process ====>|<==== BGP Process ====> + | +rib module rnh module | zebra module nht module +---------------------------------------------------------------------- + | | | +meta_queue_ | | | + process() | zebra_evaluate_ | | + | rnh_table() | | + | | | + | | bgp_read_nexthop_ | + | | update() | + | | | bgp_parse_ + | | | nexthop_update() + | | | + + +4.3. zclient message format + +ZEBRA_NEXTHOP_REGISTER and ZEBRA_NEXTHOP_UNREGISTER messages are +encoded in the following way: + +/* + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | AF | prefix len | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * . Nexthop prefix . + * . . + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * . . + * . . + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | AF | prefix len | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * . Nexthop prefix . + * . . + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + +ZEBRA_NEXTHOP_UPDATE message is encoded as follows: + +/* + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | AF | prefix len | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * . Nexthop prefix getting resolved . + * . . + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | metric | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | #nexthops | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | nexthop type | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * . resolving Nexthop details . + * . . + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * . . + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | nexthop type | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * . resolving Nexthop details . + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + +4.4. BGP data structure + +Legend: + +/\ struct bgp_node: a BGP destination/route/prefix +\/ + +[ ] struct bgp_info: a BGP path (e.g. route received from a peer) + + _ +(_) struct bgp_nexthop_cache: a BGP nexthop + + + + /\ NULL + \/--+ ^ + | : + +--[ ]--[ ]--[ ]--> NULL + /\ : + \/--+ : + | : + +--[ ]--[ ]--> NULL + : + _ : + (_)............. + + +4.5. Zebra data structure + +rnh table: + + O + / \ + O O + / \ + O O + + struct rnh + { + u_char flags; + struct rib *state; + struct list *client_list; + struct route_node *node; + }; + +5. User interface changes + +quagga# show ip nht +3.3.3.3 + resolved via kernel + via 11.0.0.6, swp1 + Client list: bgp(fd 12) +11.0.0.10 + resolved via connected + is directly connected, swp2 + Client list: bgp(fd 12) +11.0.0.18 + resolved via connected + is directly connected, swp4 + Client list: bgp(fd 12) +11.11.11.11 + resolved via kernel + via 10.0.1.2, eth0 + Client list: bgp(fd 12) + +quagga# show ip bgp nexthop +Current BGP nexthop cache: + 3.3.3.3 valid [IGP metric 0], #paths 3 + Last update: Wed Oct 16 04:43:49 2013 + + 11.0.0.10 valid [IGP metric 1], #paths 1 + Last update: Wed Oct 16 04:43:51 2013 + + 11.0.0.18 valid [IGP metric 1], #paths 2 + Last update: Wed Oct 16 04:43:47 2013 + + 11.11.11.11 valid [IGP metric 0], #paths 1 + Last update: Wed Oct 16 04:43:47 2013 + +quagga# show ipv6 nht +quagga# show ip bgp nexthop detail + +quagga# debug bgp nht +quagga# debug zebra nht + +6. Sample test cases + + r2----r3 + / \ / + r1----r4 + +- Verify that a change in IGP cost triggers NHT + + shutdown the r1-r4 and r2-r4 links + + no shut the r1-r4 and r2-r4 links and wait for OSPF to come back + up + + We should be back to the original nexthop via r4 now +- Verify that a NH becoming unreachable triggers NHT + + Shutdown all links to r4 +- Verify that a NH becoming reachable triggers NHT + + no shut all links to r4 + +7. Future work + +- route-policy for next hop validation (e.g. ignore default route) +- damping for rapid next hop changes +- prioritized handling of nexthop changes ((un)reachability vs. metric + changes) +- handling recursion loop, e.g. + 11.11.11.11/32 -> 12.12.12.12 + 12.12.12.12/32 -> 11.11.11.11 + 11.0.0.0/8 -> +- better statistics diff --git a/doc/nhrpd.8 b/doc/nhrpd.8 new file mode 100644 index 000000000..e227c207f --- /dev/null +++ b/doc/nhrpd.8 @@ -0,0 +1,105 @@ +.TH NHRP 8 "24 January 2017" "Quagga NHRP daemon" "Version 1.1" +.SH NAME +nhrpd \- a Next Hop Routing Protocol routing engine for use with Quagga routing software. +.SH SYNOPSIS +.B nhrpd +[ +.B \-dhv +] [ +.B \-f +.I config-file +] [ +.B \-i +.I pid-file +] [ +.B \-P +.I port-number +] [ +.B \-A +.I vty-address +] [ +.B \-u +.I user +] [ +.B \-g +.I group +] +.SH DESCRIPTION +.B nhrpd +is a routing component that works with the +.B Quagga +routing engine. +.SH OPTIONS +Options available for the +.B nhrpd +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/nhrpd.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 nhrpd starts its process identifier is written to +\fB\fIpid-file\fR. The init system uses the recorded PID to stop or +restart nhrpd. The likely default is \fB\fI/var/run/nhrpd.pid\fR. +.TP +\fB\-P\fR, \fB\-\-vty_port \fR\fIport-number\fR +Specify the port that the nhrpd VTY will listen on. This defaults to +2608, as specified in \fB\fI/etc/services\fR. +.TP +\fB\-A\fR, \fB\-\-vty_addr \fR\fIvty-address\fR +Specify the address that the nhrpd 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. +.SH FILES +.TP +.BI /usr/local/sbin/nhrpd +The default location of the +.B nhrpd +binary. +.TP +.BI /usr/local/etc/nhrpd.conf +The default location of the +.B nhrpd +config file. +.TP +.BI $(PWD)/nhrpd.log +If the +.B nhrpd +process is config'd to output logs to a file, then you will find this +file in the directory where you started \fBnhrpd\fR. +.SH WARNING +This man page is intended to be a quick reference for command line +options. The definitive document is the Info file \fBQuagga\fR. +.SH DIAGNOSTICS +The nhrpd process may log to standard output, to a VTY, to a log +file, or through syslog to the system logs. \fBnhrpd\fR supports many +debugging options, see the Info file, or the source for details. +.SH "SEE ALSO" +.BR bgpd (8), +.BR ripd (8), +.BR ripngd (8), +.BR ospfd (8), +.BR ospf6d (8), +.BR zebra (8), +.BR vtysh (1) + +.B nhrpd +eats bugs for breakfast. If you have food for the maintainers try +.BI http://bugzilla.quagga.net +.SH AUTHORS +Timo Teräs diff --git a/doc/nhrpd.texi b/doc/nhrpd.texi new file mode 100644 index 000000000..71d1ce996 --- /dev/null +++ b/doc/nhrpd.texi @@ -0,0 +1,144 @@ +@cindex NHRP +@node NHRP +@chapter NHRP + +@command{nhrpd} is a daemon to support Next Hop Routing Protocol (NHRP). +NHRP is described in RFC2332. + +NHRP is used to improve the efficiency of routing computer network +traffic over Non-Broadcast, Multiple Access (NBMA) Networks. NHRP provides +an ARP-like solution that allows a system to dynamically learn the NBMA +address of the other systems that are part of that network, allowing +these systems to directly communicate without requiring traffic to use +an intermediate hop. + +Cisco Dynamic Multipoint VPN (DMVPN) is based on NHRP, and Quagga nrhpd +implements this scenario. + +@menu +* Routing Design:: +* Configuring NHRP:: +* Hub Functionality:: +* Integration with IKE:: +* NHRP Events:: +* Configuration Example:: +@end menu + +@node Routing Design +@section Routing Design + +nhrpd never handles routing of prefixes itself. You need to run some +real routing protocol (e.g. BGP) to advertise routes over the tunnels. +What nhrpd does it establishes 'shortcut routes' that optimizes the +routing protocol to avoid going through extra nodes in NBMA GRE mesh. + +nhrpd does route NHRP domain addresses individually using per-host prefixes. +This is similar to Cisco FlexVPN; but in contrast to opennhrp which uses +a generic subnet route. + +To create NBMA GRE tunnel you might use the following (linux terminal +commands): +@example +@group + ip tunnel add gre1 mode gre key 42 ttl 64 + ip addr add 10.255.255.2/32 dev gre1 + ip link set gre1 up +@end group +@end example + +Note that the IP-address is assigned as host prefix to gre1. nhrpd will +automatically create additional host routes pointing to gre1 when +a connection with these hosts is established. + +The gre1 subnet prefix should be announced by routing protocol from the +hub nodes (e.g. BGP 'network' announce). This allows the routing protocol +to decide which is the closest hub and determine the relay hub on prefix +basis when direct tunnel is not established. + +nhrpd will redistribute directly connected neighbors to zebra. Within +hub nodes, these routes should be internally redistributed using some +routing protocol (e.g. iBGP) to allow hubs to be able to relay all traffic. + +This can be achieved in hubs with the following bgp configuration (network +command defines the GRE subnet): +@example +@group +router bgp 65555 + network 172.16.0.0/16 + redistribute nhrp +@end group +@end example + + +@node Configuring NHRP +@section Configuring NHRP + +FIXME + +@node Hub Functionality +@section Hub Functionality + +In addition to routing nhrp redistributed host prefixes, the hub nodes +are also responsible to send NHRP Traffic Indication messages that +trigger creation of the shortcut tunnels. + +nhrpd sends Traffic Indication messages based on network traffic captured +using NFLOG. Typically you want to send Traffic Indications for network +traffic that is routed from gre1 back to gre1 in rate limited manner. +This can be achieved with the following iptables rule. + +@example +@group +iptables -A FORWARD -i gre1 -o gre1 \ + -m hashlimit --hashlimit-upto 4/minute --hashlimit-burst 1 \ + --hashlimit-mode srcip,dstip --hashlimit-srcmask 24 \ + --hashlimit-dstmask 24 --hashlimit-name loglimit-0 \ + -j NFLOG --nflog-group 1 --nflog-range 128 +@end group +@end example + +You can fine tune the src/dstmask according to the prefix lengths you +announce internal, add additional IP range matches, or rate limitation +if needed. However, the above should be good in most cases. + +This kernel NFLOG target's nflog-group is configured in global nhrp config +with: +@example +@group +nhrp nflog-group 1 +@end group +@end example + +To start sending these traffic notices out from hubs, use the nhrp +per-interface directive: +@example +@group +interface gre1 + ip nhrp redirect +@end group +@end example + +@node Integration with IKE +@section Integration with IKE + +nhrpd needs tight integration with IKE daemon for various reasons. +Currently only strongSwan is supported as IKE daemon. + +nhrpd connects to strongSwan using VICI protocol based on UNIX socket +(hardcoded now as /var/run/charon.vici). + +strongSwan currently needs few patches applied. Please check out the +@uref{http://git.alpinelinux.org/cgit/user/tteras/strongswan/log/?h=tteras-release,release} +and +@uref{http://git.alpinelinux.org/cgit/user/tteras/strongswan/log/?h=tteras,working tree} +git repositories for the patches. + +@node NHRP Events +@section NHRP Events + +FIXME + +@node Configuration Example +@section Configuration Example + +FIXME diff --git a/doc/ospf6d.texi b/doc/ospf6d.texi index 6667221cf..31f4db0cc 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/ospf_fundamentals.texi b/doc/ospf_fundamentals.texi new file mode 100644 index 000000000..82218e601 --- /dev/null +++ b/doc/ospf_fundamentals.texi @@ -0,0 +1,582 @@ +@c Copyright 2006 Sun Microsystems, Inc. All Rights Reserved. +@cindex OSPF Fundamentals +@node OSPF Fundamentals +@section OSPF Fundamentals + +@cindex Link-state routing protocol +@cindex Distance-vector routing protocol +@acronym{OSPF} is, mostly, a link-state routing protocol. In contrast +to @dfn{distance-vector} protocols, such as @acronym{RIP} or +@acronym{BGP}, where routers describe available @dfn{paths} (i.e@. routes) +to each other, in @dfn{link-state} protocols routers instead +describe the state of their links to their immediate neighbouring +routers. + +@cindex Link State Announcement +@cindex Link State Advertisement +@cindex LSA flooding +@cindex Link State DataBase +Each router describes their link-state information in a message known +as an @acronym{LSA,Link State Advertisement}, which is then propogated +through to all other routers in a link-state routing domain, by a +process called @dfn{flooding}. Each router thus builds up an +@acronym{LSDB,Link State Database} of all the link-state messages. From +this collection of LSAs in the LSDB, each router can then calculate the +shortest path to any other router, based on some common metric, by +using an algorithm such as @url{http://www.cs.utexas.edu/users/EWD/, +Edgser Dijkstra}'s @acronym{SPF,Shortest Path First}. + +@cindex Link-state routing protocol advantages +By describing connectivity of a network in this way, in terms of +routers and links rather than in terms of the paths through a network, +a link-state protocol can use less bandwidth and converge more quickly +than other protocols. A link-state protocol need distribute only one +link-state message throughout the link-state domain when a link on any +single given router changes state, in order for all routers to +reconverge on the best paths through the network. In contrast, distance +vector protocols can require a progression of different path update +messages from a series of different routers in order to converge. + +@cindex Link-state routing protocol disadvantages +The disadvantage to a link-state protocol is that the process of +computing the best paths can be relatively intensive when compared to +distance-vector protocols, in which near to no computation need be done +other than (potentially) select between multiple routes. This overhead +is mostly negligible for modern embedded CPUs, even for networks with +thousands of nodes. The primary scaling overhead lies more in coping +with the ever greater frequency of LSA updates as the size of a +link-state area increases, in managing the @acronym{LSDB} and required +flooding. + +This section aims to give a distilled, but accurate, description of the +more important workings of @acronym{OSPF}@ which an administrator may need +to know to be able best configure and trouble-shoot @acronym{OSPF}@. + +@subsection OSPF Mechanisms + +@acronym{OSPF} defines a range of mechanisms, concerned with detecting, +describing and propogating state through a network. These mechanisms +will nearly all be covered in greater detail further on. They may be +broadly classed as: + +@table @dfn +@cindex OSPF Hello Protocol overview +@item The Hello Protocol + +@cindex OSPF Hello Protocol +The OSPF Hello protocol allows OSPF to quickly detect changes in +two-way reachability between routers on a link. OSPF can additionally +avail of other sources of reachability information, such as link-state +information provided by hardware, or through dedicated reachability +protocols such as @acronym{BFD,Bi-directional Forwarding Detection}. + +OSPF also uses the Hello protocol to propagate certain state between +routers sharing a link, for example: + +@itemize @bullet +@item Hello protocol configured state, such as the dead-interval. +@item Router priority, for DR/BDR election. +@item DR/BDR election results. +@item Any optional capabilities supported by each router. +@end itemize + +The Hello protocol is comparatively trivial and will not be explored in +greater detail than here. + +@cindex OSPF LSA overview +@item LSAs + +At the heart of @acronym{OSPF} are @acronym{LSA,Link State +Advertisement} messages. Despite the name, some @acronym{LSA}s do not, +strictly speaking, describe link-state information. Common +@acronym{LSA}s describe information such as: + +@itemize @bullet +@item +Routers, in terms of their links. +@item +Networks, in terms of attached routers. +@item +Routes, external to a link-state domain: + +@itemize @bullet +@item External Routes + +Routes entirely external to @acronym{OSPF}@. Routers originating such +routes are known as @acronym{ASBR,Autonomous-System Border Router} +routers. + +@item Summary Routes + +Routes which summarise routing information relating to OSPF areas +external to the OSPF link-state area at hand, originated by +@acronym{ABR,Area Boundary Router} routers. +@end itemize +@end itemize + +@item LSA Flooding +OSPF defines several related mechanisms, used to manage synchronisation of +@acronym{LSDB}s between neighbours as neighbours form adjacencies and +the propogation, or @dfn{flooding} of new or updated @acronym{LSA}s. + +@xref{OSPF Flooding}. + +@cindex OSPF Areas overview +@item Areas +OSPF provides for the protocol to be broken up into multiple smaller +and independent link-state areas. Each area must be connected to a +common backbone area by an @acronym{ABR,Area Boundary Router}. These +@acronym{ABR} routers are responsible for summarising the link-state +routing information of an area into @dfn{Summary LSAs}, possibly in a +condensed (i.e. aggregated) form, and then originating these summaries +into all other areas the @acronym{ABR} is connected to. + +Note that only summaries and external routes are passed between areas. +As these describe @emph{paths}, rather than any router link-states, +routing between areas hence is by @dfn{distance-vector}, @strong{not} +link-state. + +@xref{OSPF Areas}. +@end table + +@subsection OSPF LSAs + +@acronym{LSA}s are the core object in OSPF@. Everything else in OSPF +revolves around detecting what to describe in LSAs, when to update +them, how to flood them throughout a network and how to calculate +routes from them. + +There are a variety of different @acronym{LSA}s, for purposes such +as describing actual link-state information, describing paths (i.e. +routes), describing bandwidth usage of links for +@acronym{TE,Traffic Engineering} purposes, and even arbitrary data +by way of @emph{Opaque} @acronym{LSA}s. + +@subsubsection LSA Header +All LSAs share a common header with the following information: + +@itemize @bullet +@item Type + +Different types of @acronym{LSA}s describe different things in +@acronym{OSPF}@. Types include: + +@itemize @bullet +@item Router LSA +@item Network LSA +@item Network Summary LSA +@item Router Summary LSA +@item AS-External LSA +@end itemize + +The specifics of the different types of LSA are examined below. + +@item Advertising Router + +The Router ID of the router originating the LSA, see @ref{ospf router-id}. + +@item LSA ID + +The ID of the LSA, which is typically derived in some way from the +information the LSA describes, e.g. a Router LSA uses the Router ID as +the LSA ID, a Network LSA will have the IP address of the @acronym{DR} +as its LSA ID@. + +The combination of the Type, ID and Advertising Router ID must uniquely +identify the @acronym{LSA}@. There can however be multiple instances of +an LSA with the same Type, LSA ID and Advertising Router ID, see +@ref{OSPF LSA sequence number,,LSA Sequence Number}. + +@item Age + +A number to allow stale @acronym{LSA}s to, eventually, be purged by routers +from their @acronym{LSDB}s. + +The value nominally is one of seconds. An age of 3600, i.e. 1 hour, is +called the @dfn{MaxAge}. MaxAge LSAs are ignored in routing +calculations. LSAs must be periodically refreshed by their Advertising +Router before reaching MaxAge if they are to remain valid. + +Routers may deliberately flood LSAs with the age artificially set to +3600 to indicate an LSA is no longer valid. This is called +@dfn{flushing} of an LSA@. + +It is not abnormal to see stale LSAs in the LSDB, this can occur where +a router has shutdown without flushing its LSA(s), e.g. where it has +become disconnected from the network. Such LSAs do little harm. + +@anchor{OSPF LSA sequence number} +@item Sequence Number + +A number used to distinguish newer instances of an LSA from older instances. +@end itemize + +@subsubsection Link-State LSAs +Of all the various kinds of @acronym{LSA}s, just two types comprise the +actual link-state part of @acronym{OSPF}, Router @acronym{LSA}s and +Network @acronym{LSA}s. These LSA types are absolutely core to the +protocol. + +Instances of these LSAs are specific to the link-state area in which +they are originated. Routes calculated from these two LSA types are +called @dfn{intra-area routes}. + +@itemize @bullet +@item Router LSA + +Each OSPF Router must originate a router @acronym{LSA} to describe +itself. In it, the router lists each of its @acronym{OSPF} enabled +interfaces, for the given link-state area, in terms of: + +@itemize @bullet +@item Cost + +The output cost of that interface, scaled inversely to some commonly known +reference value, @xref{OSPF auto-cost reference-bandwidth,,auto-cost +reference-bandwidth}. + +@item Link Type +@itemize @bullet +@item Transit Network + +A link to a multi-access network, on which the router has at least one +Full adjacency with another router. + +@item @acronym{PtP,Point-to-Point} + +A link to a single remote router, with a Full adjacency. No +@acronym{DR, Designated Router} is elected on such links; no network +LSA is originated for such a link. + +@item Stub + +A link with no adjacent neighbours, or a host route. +@end itemize + +@item Link ID and Data + +These values depend on the Link Type: + +@multitable @columnfractions .18 .32 .32 +@headitem Link Type @tab Link ID @tab Link Data + +@item Transit +@tab Link IP address of the @acronym{DR} +@tab Interface IP address + +@item Point-to-Point +@tab Router ID of the remote router +@tab Local interface IP address, +or the @acronym{ifindex,MIB-II interface index} +for unnumbered links + +@item Stub +@tab IP address +@tab Subnet Mask + +@end multitable +@end itemize + +Links on a router may be listed multiple times in the Router LSA, e.g. +a @acronym{PtP} interface on which OSPF is enabled must @emph{always} +be described by a Stub link in the Router @acronym{LSA}, in addition to +being listed as PtP link in the Router @acronym{LSA} if the adjacency +with the remote router is Full. + +Stub links may also be used as a way to describe links on which OSPF is +@emph{not} spoken, known as @dfn{passive interfaces}, see @ref{OSPF +passive-interface,,passive-interface}. + +@item Network LSA + +On multi-access links (e.g. ethernets, certain kinds of ATM and X@.25 +configurations), routers elect a @acronym{DR}@. The @acronym{DR} is +responsible for originating a Network @acronym{LSA}, which helps reduce +the information needed to describe multi-access networks with multiple +routers attached. The @acronym{DR} also acts as a hub for the flooding of +@acronym{LSA}s on that link, thus reducing flooding overheads. + +The contents of the Network LSA describes the: + +@itemize @bullet +@item Subnet Mask + +As the @acronym{LSA} ID of a Network LSA must be the IP address of the +@acronym{DR}, the Subnet Mask together with the @acronym{LSA} ID gives +you the network address. + +@item Attached Routers + +Each router fully-adjacent with the @acronym{DR} is listed in the LSA, +by their Router-ID. This allows the corresponding Router @acronym{LSA}s to be +easily retrieved from the @acronym{LSDB}@. +@end itemize +@end itemize + +Summary of Link State LSAs: + +@multitable @columnfractions .18 .32 .40 +@headitem LSA Type @tab LSA ID Describes @tab LSA Data Describes + +@item Router LSA +@tab The Router ID +@tab The @acronym{OSPF} enabled links of the router, within + a specific link-state area. + +@item Network LSA +@tab The IP address of the @acronym{DR} for the network +@tab The Subnet Mask of the network, and the Router IDs of all routers + on the network. +@end multitable + +With an LSDB composed of just these two types of @acronym{LSA}, it is +possible to construct a directed graph of the connectivity between all +routers and networks in a given OSPF link-state area. So, not +surprisingly, when OSPF routers build updated routing tables, the first +stage of @acronym{SPF} calculation concerns itself only with these two +LSA types. + +@subsubsection Link-State LSA Examples + +The example below (@pxref{OSPF Link-State LSA Example}) shows two +@acronym{LSA}s, both originated by the same router (Router ID +192.168.0.49) and with the same @acronym{LSA} ID (192.168.0.49), but of +different LSA types. + +The first LSA being the router LSA describing 192.168.0.49's links: 2 links +to multi-access networks with fully-adjacent neighbours (i.e. Transit +links) and 1 being a Stub link (no adjacent neighbours). + +The second LSA being a Network LSA, for which 192.168.0.49 is the +@acronym{DR}, listing the Router IDs of 4 routers on that network which +are fully adjacent with 192.168.0.49. + +@anchor{OSPF Link-State LSA Example} +@example +# show ip ospf database router 192.168.0.49 + + OSPF Router with ID (192.168.0.53) + + + Router Link States (Area 0.0.0.0) + + LS age: 38 + Options: 0x2 : *|-|-|-|-|-|E|* + LS Flags: 0x6 + Flags: 0x2 : ASBR + LS Type: router-LSA + Link State ID: 192.168.0.49 + Advertising Router: 192.168.0.49 + LS Seq Number: 80000f90 + Checksum: 0x518b + Length: 60 + Number of Links: 3 + + Link connected to: a Transit Network + (Link ID) Designated Router address: 192.168.1.3 + (Link Data) Router Interface address: 192.168.1.3 + Number of TOS metrics: 0 + TOS 0 Metric: 10 + + Link connected to: a Transit Network + (Link ID) Designated Router address: 192.168.0.49 + (Link Data) Router Interface address: 192.168.0.49 + Number of TOS metrics: 0 + TOS 0 Metric: 10 + + Link connected to: Stub Network + (Link ID) Net: 192.168.3.190 + (Link Data) Network Mask: 255.255.255.255 + Number of TOS metrics: 0 + TOS 0 Metric: 39063 +# show ip ospf database network 192.168.0.49 + + OSPF Router with ID (192.168.0.53) + + + Net Link States (Area 0.0.0.0) + + LS age: 285 + Options: 0x2 : *|-|-|-|-|-|E|* + LS Flags: 0x6 + LS Type: network-LSA + Link State ID: 192.168.0.49 (address of Designated Router) + Advertising Router: 192.168.0.49 + LS Seq Number: 80000074 + Checksum: 0x0103 + Length: 40 + Network Mask: /29 + Attached Router: 192.168.0.49 + Attached Router: 192.168.0.52 + Attached Router: 192.168.0.53 + Attached Router: 192.168.0.54 +@end example + +Note that from one LSA, you can find the other. E.g. Given the +Network-LSA you have a list of Router IDs on that network, from which +you can then look up, in the local @acronym{LSDB}, the matching Router +LSA@. From that Router-LSA you may (potentially) find links to other +Transit networks and Routers IDs which can be used to lookup the +corresponding Router or Network LSA@. And in that fashion, one can find +all the Routers and Networks reachable from that starting @acronym{LSA}@. + +Given the Router LSA instead, you have the IP address of the +@acronym{DR} of any attached transit links. Network LSAs will have that IP +as their LSA ID, so you can then look up that Network LSA and from that +find all the attached routers on that link, leading potentially to more +links and Network and Router LSAs, etc. etc. + +From just the above two @acronym{LSA}s, one can already see the +following partial topology: +@example +@group + + + --------------------- Network: ...... + | Designated Router IP: 192.168.1.3 + | + IP: 192.168.1.3 + (transit link) + (cost: 10) + Router ID: 192.168.0.49(stub)---------- IP: 192.168.3.190/32 + (cost: 10) (cost: 39063) + (transit link) + IP: 192.168.0.49 + | + | +------------------------------ Network: 192.168.0.48/29 + | | | Designated Router IP: 192.168.0.49 + | | | + | | Router ID: 192.168.0.54 + | | + | Router ID: 192.168.0.53 + | +Router ID: 192.168.0.52 +@end group +@end example + +Note the Router IDs, though they look like IP addresses and often are +IP addresses, are not strictly speaking IP addresses, nor need they be +reachable addresses (though, OSPF will calculate routes to Router IDs). + +@subsubsection External LSAs + +External, or "Type 5", @acronym{LSA}s describe routing information which is +entirely external to @acronym{OSPF}, and is "injected" into +@acronym{OSPF}@. Such routing information may have come from another +routing protocol, such as RIP or BGP, they may represent static routes +or they may represent a default route. + +An @acronym{OSPF} router which originates External @acronym{LSA}s is known as an +@acronym{ASBR,AS Boundary Router}. Unlike the link-state @acronym{LSA}s, and +most other @acronym{LSA}s, which are flooded only within the area in +which they originate, External @acronym{LSA}s are flooded through-out +the @acronym{OSPF} network to all areas capable of carrying External +@acronym{LSA}s (@pxref{OSPF Areas}). + +Routes internal to OSPF (intra-area or inter-area) are always preferred +over external routes. + +The External @acronym{LSA} describes the following: + +@itemize @bullet +@item IP Network number + +The IP Network number of the route is described by the @acronym{LSA} ID +field. + +@item IP Network Mask + +The body of the External LSA describes the IP Network Mask of the +route. This, together with the @acronym{LSA} ID, describes the prefix +of the IP route concerned. + +@item Metric + +The cost of the External Route. This cost may be an OSPF cost (also +known as a "Type 1" metric), i.e. equivalent to the normal OSPF costs, +or an externally derived cost ("Type 2" metric) which is not comparable +to OSPF costs and always considered larger than any OSPF cost. Where +there are both Type 1 and 2 External routes for a route, the Type 1 is +always preferred. + +@item Forwarding Address + +The address of the router to forward packets to for the route. This may +be, and usually is, left as 0 to specify that the ASBR originating the +External @acronym{LSA} should be used. There must be an internal OSPF +route to the forwarding address, for the forwarding address to be +useable. + +@item Tag + +An arbitrary 4-bytes of data, not interpreted by OSPF, which may +carry whatever information about the route which OSPF speakers desire. +@end itemize + +@subsubsection AS External LSA Example + +To illustrate, below is an example of an External @acronym{LSA} in the +@acronym{LSDB} of an OSPF router. It describes a route to the IP prefix +of 192.168.165.0/24, originated by the ASBR with Router-ID +192.168.0.49. The metric of 20 is external to OSPF. The forwarding +address is 0, so the route should forward to the originating ASBR if +selected. + +@example +@group +# show ip ospf database external 192.168.165.0 + LS age: 995 + Options: 0x2 : *|-|-|-|-|-|E|* + LS Flags: 0x9 + LS Type: AS-external-LSA + Link State ID: 192.168.165.0 (External Network Number) + Advertising Router: 192.168.0.49 + LS Seq Number: 800001d8 + Checksum: 0xea27 + Length: 36 + Network Mask: /24 + Metric Type: 2 (Larger than any link state path) + TOS: 0 + Metric: 20 + Forward Address: 0.0.0.0 + External Route Tag: 0 +@end group +@end example + +We can add this to our partial topology from above, which now looks +like: +@example +@group + --------------------- Network: ...... + | Designated Router IP: 192.168.1.3 + | + IP: 192.168.1.3 /---- External route: 192.168.165.0/24 + (transit link) / Cost: 20 (External metric) + (cost: 10) / + Router ID: 192.168.0.49(stub)---------- IP: 192.168.3.190/32 + (cost: 10) (cost: 39063) + (transit link) + IP: 192.168.0.49 + | + | +------------------------------ Network: 192.168.0.48/29 + | | | Designated Router IP: 192.168.0.49 + | | | + | | Router ID: 192.168.0.54 + | | + | Router ID: 192.168.0.53 + | +Router ID: 192.168.0.52 +@end group +@end example + +@subsubsection Summary LSAs + +Summary LSAs are created by @acronym{ABR}s to summarise the destinations available within one area to other areas. These LSAs may describe IP networks, potentially in aggregated form, or @acronym{ASBR} routers. + +@anchor{OSPF Flooding} +@subsection OSPF Flooding + +@anchor{OSPF Areas} +@subsection OSPF Areas diff --git a/doc/ospfclient.8 b/doc/ospfclient.8 new file mode 100644 index 000000000..ccfad1aad --- /dev/null +++ b/doc/ospfclient.8 @@ -0,0 +1,42 @@ +.\" This file was originally generated by help2man 1.36. +.TH OSPFCLIENT "1" "July 2010" +.SH NAME +ospfclient \- an example ospf-api client +.SH SYNOPSIS +.B ospfclient +.I ospfd +.I lsatype +.I opaquetype +.I opaqueid +.I ifaddr +.I areaid +.SH DESCRIPTION +.B ospfclient +is a an example ospf-api client to test the ospfd daemon. +.SH OPTIONS +.TP +.I ospfd +A router where the API\-enabled OSPF daemon is running. +.TP +.I lsatype +The value has to be either "9", "10", or "11", depending on the flooding +scope. +.TP +.I opaquetype +The value has to be in the range of 0\-255 (for example, experimental +applications use +.I opaquetype +larger than 128). +.TP +.I opaqueid +Arbitrary application instance (24 bits). +.TP +.I ifaddr +Interface IP address for type 9, otherwise it will be ignored. +.TP +.I areaid +Area in the IP address format for type 10, otherwise it will be ignored. +.SH "SEE ALSO" +.BR ospfd (8). +.SH AUTHORS +See the project homepage at . diff --git a/doc/ospfd.texi b/doc/ospfd.texi index 856a2ba05..d60ecf29a 100644 --- a/doc/ospfd.texi +++ b/doc/ospfd.texi @@ -1,3 +1,4 @@ + @cindex OSPFv2 @node OSPFv2 @chapter OSPFv2 @@ -11,16 +12,22 @@ convergence times. OSPF is widely used in large networks such as networks. @menu +* OSPF Fundamentals:: * Configuring ospfd:: * OSPF router:: * OSPF area:: * OSPF interface:: * Redistribute routes to OSPF:: * Showing OSPF information:: +* Opaque LSA:: +* OSPF Traffic Engineering:: +* Router Information:: * Debugging OSPF:: * OSPF Configuration Examples:: @end menu +@include ospf_fundamentals.texi + @node Configuring ospfd @section Configuring ospfd @@ -216,6 +223,7 @@ OSPF domain. @deffnx {OSPF Command} {network @var{a.b.c.d/m} area @var{<0-4294967295>}} {} @deffnx {OSPF Command} {no network @var{a.b.c.d/m} area @var{a.b.c.d}} {} @deffnx {OSPF Command} {no network @var{a.b.c.d/m} area @var{<0-4294967295>}} {} +@anchor{OSPF network command} This command specifies the OSPF enabled interface(s). If the interface has an address from range 192.168.1.0/24 then the command below enables ospf on this interface so router can provide network information to the other @@ -239,6 +247,10 @@ Currently, if a peer prefix has been configured, then we test whether the prefix in the network command contains the destination prefix. Otherwise, we test whether the network command prefix contains the local address prefix of the interface. + +In some cases it may be more convenient to enable OSPF on a per +interface/subnet basis (@pxref{OSPF ip ospf area command}). + @end deffn @node OSPF area @@ -406,6 +418,19 @@ settings will override any per-area authentication setting. @node OSPF interface @section OSPF interface +@deffn {Interface Command} {ip ospf area @var{AREA} [@var{ADDR}]} {} +@deffnx {Interface Command} {no ip ospf area [@var{ADDR}]} {} +@anchor{OSPF ip ospf area command} + +Enable OSPF on the interface, optionally restricted to just the IP address +given by @var{ADDR}, putting it in the @var{AREA} area. Per interface area +settings take precedence to network commands (@pxref{OSPF network command}). + +If you have a lot of interfaces, and/or a lot of subnets, then enabling OSPF +via this command may result in a slight performance improvement. + +@end deffn + @deffn {Interface Command} {ip ospf authentication-key @var{AUTH_KEY}} {} @deffnx {Interface Command} {no ip ospf authentication-key} {} Set OSPF authentication key to a simple password. After setting @var{AUTH_KEY}, @@ -588,14 +613,16 @@ interfaces if no interface is given. @end deffn @deffn {Command} {show ip ospf database} {} -@end deffn - -@deffn {Command} {show ip ospf database (asbr-summary|external|network|router|summary)} {} -@deffnx {Command} {show ip ospf database (asbr-summary|external|network|router|summary) @var{link-state-id}} {} -@deffnx {Command} {show ip ospf database (asbr-summary|external|network|router|summary) @var{link-state-id} adv-router @var{adv-router}} {} -@deffnx {Command} {show ip ospf database (asbr-summary|external|network|router|summary) adv-router @var{adv-router}} {} -@deffnx {Command} {show ip ospf database (asbr-summary|external|network|router|summary) @var{link-state-id} self-originate} {} -@deffnx {Command} {show ip ospf database (asbr-summary|external|network|router|summary) self-originate} {} +@deffnx {Command} {show ip ospf database asbr-summary} {} +@deffnx {Command} {show ip ospf database external} {} +@deffnx {Command} {show ip ospf database network} {} +@deffnx {Command} {show ip ospf database asbr-router} {} +@deffnx {Command} {show ip ospf database summary} {} +@deffnx {Command} {show ip ospf database @dots{} @var{link-state-id}} {} +@deffnx {Command} {show ip ospf database @dots{} @var{link-state-id} adv-router @var{adv-router}} {} +@deffnx {Command} {show ip ospf database @dots{} adv-router @var{adv-router}} {} +@deffnx {Command} {show ip ospf database @dots{} @var{link-state-id} self-originate} {} +@deffnx {Command} {show ip ospf database @dots{} self-originate} {} @end deffn @deffn {Command} {show ip ospf database max-age} {} @@ -608,35 +635,137 @@ interfaces if no interface is given. Show the OSPF routing table, as determined by the most recent SPF calculation. @end deffn +@node Opaque LSA +@section Opaque LSA + +@deffn {OSPF Command} {ospf opaque-lsa} {} +@deffnx {OSPF Command} {capability opaque} {} +@deffnx {OSPF Command} {no ospf opaque-lsa} {} +@deffnx {OSPF Command} {no capability opaque} {} +@command{ospfd} support Opaque LSA (RFC2370) as fondment for MPLS Traffic Engineering LSA. Prior to used MPLS TE, opaque-lsa must be enable in the configuration file. Alternate command could be "mpls-te on" (@ref{OSPF Traffic Engineering}). +@end deffn + +@deffn {Command} {show ip ospf database (opaque-link|opaque-area|opaque-external)} {} +@deffnx {Command} {show ip ospf database (opaque-link|opaque-area|opaque-external) @var{link-state-id}} {} +@deffnx {Command} {show ip ospf database (opaque-link|opaque-area|opaque-external) @var{link-state-id} adv-router @var{adv-router}} {} +@deffnx {Command} {show ip ospf database (opaque-link|opaque-area|opaque-external) adv-router @var{adv-router}} {} +@deffnx {Command} {show ip ospf database (opaque-link|opaque-area|opaque-external) @var{link-state-id} self-originate} {} +@deffnx {Command} {show ip ospf database (opaque-link|opaque-area|opaque-external) self-originate} {} +Show Opaque LSA from the database. +@end deffn + +@node OSPF Traffic Engineering +@section Traffic Engineering + +@deffn {OSPF Command} {mpls-te on} {} +@deffnx {OSPF Command} {no mpls-te} {} +Enable Traffic Engineering LSA flooding. +@end deffn + +@deffn {OSPF Command} {mpls-te router-address } {} +@deffnx {OSPF Command} {no mpls-te} {} +Configure stable IP address for MPLS-TE. This IP address is then advertise in Opaque LSA Type-10 TLV=1 (TE) +option 1 (Router-Address). +@end deffn + +@deffn {OSPF Command} {mpls-te inter-as area |as} {} +@deffnx {OSPF Command} {no mpls-te inter-as} {} +Enable RFC5392 suuport - Inter-AS TE v2 - to flood Traffic Engineering parameters of Inter-AS link. +2 modes are supported: AREA and AS; LSA are flood in AREA with Opaque Type-10, +respectively in AS with Opaque Type-11. In all case, Opaque-LSA TLV=6. +@end deffn + +@deffn {Command} {show ip ospf mpls-te interface} {} +@deffnx {Command} {show ip ospf mpls-te interface @var{interface}} {} +Show MPLS Traffic Engineering parameters for all or specified interface. +@end deffn + +@deffn {Command} {show ip ospf mpls-te router} {} +Show Traffic Engineering router parameters. +@end deffn + +@node Router Information +@section Router Information + +@deffn {OSPF Command} {router-info [as | area ]} {} +@deffnx {OSPF Command} {no router-info} {} +Enable Router Information (RFC4970) LSA advertisement with AS scope (default) or Area scope flooding +when area is specified. +@end deffn + +@deffn {OSPF Command} {pce address } {} +@deffnx {OSPF Command} {no pce address} {} +@deffnx {OSPF Command} {pce domain as <0-65535>} {} +@deffnx {OSPF Command} {no pce domain as <0-65535>} {} +@deffnx {OSPF Command} {pce neighbor as <0-65535>} {} +@deffnx {OSPF Command} {no pce neighbor as <0-65535>} {} +@deffnx {OSPF Command} {pce flag BITPATTERN} {} +@deffnx {OSPF Command} {no pce flag} {} +@deffnx {OSPF Command} {pce scope BITPATTERN} {} +@deffnx {OSPF Command} {no pce scope} {} +The commands are conform to RFC 5088 and allow OSPF router announce Path Compuatation Elemenent (PCE) capabilities +through the Router Information (RI) LSA. Router Information must be enable prior to this. The command set/unset +respectively the PCE IP adress, Autonomous System (AS) numbers of controlled domains, neighbor ASs, flag and scope. +For flag and scope, please refer to RFC5088 for the BITPATTERN recognition. Multiple 'pce neighbor' command could +be specified in order to specify all PCE neighbours. +@end deffn + +@deffn {Command} {show ip ospf router-info} {} +Show Router Capabilities flag. +@end deffn +@deffn {Command} {show ip ospf router-info pce} {} +Show Router Capabilities PCE parameters. +@end deffn + @node Debugging OSPF @section Debugging OSPF @deffn {Command} {debug ospf packet (hello|dd|ls-request|ls-update|ls-ack|all) (send|recv) [detail]} {} @deffnx {Command} {no debug ospf packet (hello|dd|ls-request|ls-update|ls-ack|all) (send|recv) [detail]} {} +Dump Packet for debugging @end deffn @deffn {Command} {debug ospf ism} {} @deffnx {Command} {debug ospf ism (status|events|timers)} {} @deffnx {Command} {no debug ospf ism} {} @deffnx {Command} {no debug ospf ism (status|events|timers)} {} +Show debug information of Interface State Machine @end deffn @deffn {Command} {debug ospf nsm} {} @deffnx {Command} {debug ospf nsm (status|events|timers)} {} @deffnx {Command} {no debug ospf nsm} {} @deffnx {Command} {no debug ospf nsm (status|events|timers)} {} +Show debug information of Network State Machine +@end deffn + +@deffn {Command} {debug ospf event} {} +@deffnx {Command} {no debug ospf event} {} +Show debug information of OSPF event +@end deffn + +@deffn {Command} {debug ospf nssa} {} +@deffnx {Command} {no debug ospf nssa} {} +Show debug information about Not So Stub Area @end deffn @deffn {Command} {debug ospf lsa} {} @deffnx {Command} {debug ospf lsa (generate|flooding|refresh)} {} @deffnx {Command} {no debug ospf lsa} {} @deffnx {Command} {no debug ospf lsa (generate|flooding|refresh)} {} +Show debug detail of Link State messages +@end deffn + +@deffn {Command} {debug ospf te} {} +@deffnx {Command} {no debug ospf te} {} +Show debug information about Traffic Engineering LSA @end deffn @deffn {Command} {debug ospf zebra} {} @deffnx {Command} {debug ospf zebra (interface|redistribute)} {} @deffnx {Command} {no debug ospf zebra} {} @deffnx {Command} {no debug ospf zebra (interface|redistribute)} {} +Show debug information of ZEBRA API @end deffn @deffn {Command} {show debugging ospf} {} @@ -694,3 +823,100 @@ router ospf ! @end group @end example + +A Traffic Engineering configuration, with Inter-ASv2 support. + + - First, the 'zebra.conf' part: + +@example +@group +hostname HOSTNAME +password PASSWORD +log file /var/log/zebra.log +! +interface eth0 + ip address 198.168.1.1/24 + mpls-te on + mpls-te link metric 10 + mpls-te link max-bw 1.25e+06 + mpls-te link max-rsv-bw 1.25e+06 + mpls-te link unrsv-bw 0 1.25e+06 + mpls-te link unrsv-bw 1 1.25e+06 + mpls-te link unrsv-bw 2 1.25e+06 + mpls-te link unrsv-bw 3 1.25e+06 + mpls-te link unrsv-bw 4 1.25e+06 + mpls-te link unrsv-bw 5 1.25e+06 + mpls-te link unrsv-bw 6 1.25e+06 + mpls-te link unrsv-bw 7 1.25e+06 + mpls-te link rsc-clsclr 0xab +! +interface eth1 + ip address 192.168.2.1/24 + mpls-te on + mpls-te link metric 10 + mpls-te link max-bw 1.25e+06 + mpls-te link max-rsv-bw 1.25e+06 + mpls-te link unrsv-bw 0 1.25e+06 + mpls-te link unrsv-bw 1 1.25e+06 + mpls-te link unrsv-bw 2 1.25e+06 + mpls-te link unrsv-bw 3 1.25e+06 + mpls-te link unrsv-bw 4 1.25e+06 + mpls-te link unrsv-bw 5 1.25e+06 + mpls-te link unrsv-bw 6 1.25e+06 + mpls-te link unrsv-bw 7 1.25e+06 + mpls-te link rsc-clsclr 0xab + mpls-te neighbor 192.168.2.2 as 65000 +@end group +@end example + + - Then the 'ospfd.conf' itself: + +@example +@group +hostname HOSTNAME +password PASSWORD +log file /var/log/ospfd.log +! +! +interface eth0 + ip ospf hello-interval 60 + ip ospf dead-interval 240 +! +interface eth1 + ip ospf hello-interval 60 + ip ospf dead-interval 240 +! +! +router ospf + ospf router-id 192.168.1.1 + network 192.168.0.0/16 area 1 + ospf opaque-lsa + mpls-te + mpls-te router-address 192.168.1.1 + mpls-te inter-as area 1 +! +line vty +@end group +@end example + +A router information example with PCE advsertisement: + +@example +@group +! +router ospf + ospf router-id 192.168.1.1 + network 192.168.0.0/16 area 1 + capability opaque + mpls-te + mpls-te router-address 192.168.1.1 + router-info area 0.0.0.1 + pce address 192.168.1.1 + pce flag 0x80 + pce domain as 65400 + pce neighbor as 65500 + pce neighbor as 65200 + pce scope 0x80 +! +@end group +@end example diff --git a/doc/overview.texi b/doc/overview.texi index 435834b71..2301c4b7f 100644 --- a/doc/overview.texi +++ b/doc/overview.texi @@ -4,10 +4,10 @@ @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 +supports IPv6 routing protocols. With SNMP daemon which supports SMUX and AgentX protocol, Quagga provides routing protocol MIBs (@pxref{SNMP Support}). Quagga uses an advanced software architecture to provide you with a high @@ -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 +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 -FreeBSD 4.x and higher +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 @@ -238,6 +264,9 @@ J. Chu, Editor. July 1994.} @cite{OSPF Version 2 Management Information Base. F. Baker, R. Coltun. November 1995.} +@item @asis{RFC2741} +@cite{Agent Extensibility (AgentX) Protocol. M. Daniele, B. Wijnen. January 2000.} + @end table @node How to get Quagga diff --git a/doc/pimd.8 b/doc/pimd.8 new file mode 100644 index 000000000..0dd170a2c --- /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 ff913aa5f..13e988e61 100644 --- a/doc/quagga.texi +++ b/doc/quagga.texi @@ -1,12 +1,14 @@ \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}} +@iftex +@afourpaper +@end iftex +@c %**end of header @c automake will automatically generate version.texi @c and set EDITION, VERSION, UPDATED and UPDATED-MONTH @@ -71,7 +73,7 @@ Version @value{VERSION}. @uref{http://www.quagga.net,,Quagga} is an advanced routing software package that provides a suite of TCP/IP based routing protocols. This is the Manual -for @value{PACKAGE_STRING}. @uref{http://www.quagga.net,,Quagga} is a fork of +for Quagga @value{VERSION}. @uref{http://www.quagga.net,,Quagga} is a fork of @uref{http://www.zebra.org,,GNU Zebra}. @insertcopying @@ -86,7 +88,8 @@ for @value{PACKAGE_STRING}. @uref{http://www.quagga.net,,Quagga} is a fork of * RIPng:: * OSPFv2:: * OSPFv3:: -* Babel:: +* ISIS:: +* NHRP:: * BGP:: * Configuring Quagga as a Route Server:: * VTY shell:: @@ -111,7 +114,8 @@ for @value{PACKAGE_STRING}. @uref{http://www.quagga.net,,Quagga} is a fork of @include ripngd.texi @include ospfd.texi @include ospf6d.texi -@include babeld.texi +@include isisd.texi +@include nhrpd.texi @include bgpd.texi @include routeserver.texi @include vtysh.texi diff --git a/doc/ripd.texi b/doc/ripd.texi index c6f804af2..78d63eed4 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/routemap.texi b/doc/routemap.texi index db3e72d23..b3ef7ca76 100644 --- a/doc/routemap.texi +++ b/doc/routemap.texi @@ -151,6 +151,10 @@ Matches the specified @var{as_path}. Matches the specified @var{metric}. @end deffn +@deffn {Route-map Command} {match local-preference @var{metric}} {} +Matches the specified @var{local-preference}. +@end deffn + @deffn {Route-map Command} {match community @var{community_list}} {} Matches the specified @var{community_list} @end deffn @@ -171,6 +175,7 @@ Set the route's weight. @end deffn @deffn {Route-map Command} {set metric @var{metric}} {} +@anchor{routemap set metric} Set the BGP attribute MED. @end deffn diff --git a/doc/routeserver.texi b/doc/routeserver.texi index f4a454621..cfe904127 100644 --- a/doc/routeserver.texi +++ b/doc/routeserver.texi @@ -11,9 +11,8 @@ The purpose of a Route Server is to centralize the peerings between BGP speakers. For example if we have an exchange point scenario with four BGP speakers, each of which maintaining a BGP peering with the other three -(@pxref{fig:full-mesh}), we can convert it into a centralized scenario where -each of the four establishes a single BGP peering against the Route Server -(@pxref{fig:route-server}). +we can convert it into a centralized scenario where +each of the four establishes a single BGP peering against the Route Server. We will first describe briefly the Route Server model implemented by Quagga. We will explain the commands that have been added for configuring that @@ -60,20 +59,10 @@ of a peer are announced to that peer. @end itemize @float Figure,fig:normal-processing -@image{fig-normal-processing,400pt,,Normal announcement processing} +@center @image{fig-normal-processing,400pt,,Normal announcement processing} @caption{Announcement processing inside a ``normal'' BGP speaker} @end float -@float Figure,fig:full-mesh -@image{fig_topologies_full,120pt,,Full Mesh BGP Topology} -@caption{Full Mesh} -@end float - -@float Figure,fig:route-server -@image{fig_topologies_rs,120pt,,Route Server BGP Topology} -@caption{Route Server and clients} -@end float - Of course we want that the routing tables obtained in each of the routers are the same when using the route server than when not. But as a consequence of having a single BGP peering (against the route server), the BGP speakers @@ -173,7 +162,7 @@ they do not hurt anybody (they can always be left empty). @end itemize @float Figure,fig:rs-processing -@image{fig-rs-processing,450pt,,Route Server Processing Model} +@center @image{fig-rs-processing,430pt,,Route Server Processing Model} @caption{Announcement processing model implemented by the Route Server} @end float diff --git a/doc/snmp.texi b/doc/snmp.texi index 3f80cc58f..0918a462f 100644 --- a/doc/snmp.texi +++ b/doc/snmp.texi @@ -5,11 +5,12 @@ feature for collecting network information from router and/or host. Quagga itself does not support SNMP agent (server daemon) functionality but is able to connect to a SNMP agent using the SMUX protocol -(@cite{RFC1227}) and make the routing protocol MIBs available through -it. +(@cite{RFC1227}) or the AgentX protocol (@cite{RFC2741}) and make the +routing protocol MIBs available through it. @menu * Getting and installing an SNMP agent:: +* AgentX configuration:: * SMUX configuration:: * MIB and command reference:: * Handling SNMP Traps:: @@ -18,20 +19,82 @@ it. @node Getting and installing an SNMP agent @section Getting and installing an SNMP agent -There are several SNMP agent which support SMUX. We recommend to use the latest +There are several SNMP agent which support SMUX or AgentX. We recommend to use the latest version of @code{net-snmp} which was formerly known as @code{ucd-snmp}. It is free and open software and available at @uref{http://www.net-snmp.org/} and as binary package for most Linux distributions. -@code{net-snmp} has to be compiled with @code{--with-mib-modules=smux} to -be able to accept connections from Quagga. +@code{net-snmp} has to be compiled with @code{--with-mib-modules=agentx} to +be able to accept connections from Quagga using AgentX protocol or with +@code{--with-mib-modules=smux} to use SMUX protocol. + +Nowadays, SMUX is a legacy protocol. The AgentX protocol should be +preferred for any new deployment. Both protocols have the same coverage. + +@node AgentX configuration +@section AgentX configuration + +To enable AgentX protocol support, Quagga must have been build with the +@code{--enable-snmp} or @code{--enable-snmp=agentx} option. Both the +master SNMP agent (snmpd) and each of the Quagga daemons must be +configured. In @code{/etc/snmp/snmpd.conf}, @code{master agentx} +directive should be added. In each of the Quagga daemons, @code{agentx} +command will enable AgentX support. + +@example +/etc/snmp/snmpd.conf: + # + # example access restrictions setup + # + com2sec readonly default public + group MyROGroup v1 readonly + view all included .1 80 + access MyROGroup "" any noauth exact all none none + # + # enable master agent for AgentX subagents + # + master agentx + +/etc/quagga/ospfd.conf: + ! ... the rest of ospfd.conf has been omitted for clarity ... + ! + agentx + ! +@end example + +Upon successful connection, you should get something like this in the +log of each Quagga daemons: + +@example +2012/05/25 11:39:08 ZEBRA: snmp[info]: NET-SNMP version 5.4.3 AgentX subagent connected +@end example + +Then, you can use the following command to check everything works as expected: + +@example +# snmpwalk -c public -v1 localhost .1.3.6.1.2.1.14.1.1 +OSPF-MIB::ospfRouterId.0 = IpAddress: 192.168.42.109 +[...] +@end example + +The AgentX protocol can be transported over a Unix socket or using TCP +or UDP. It usually defaults to a Unix socket and depends on how NetSNMP +was built. If need to configure Quagga to use another transport, you can +configure it through @code{/etc/snmp/quagga.conf}: + +@example +/etc/snmp/quagga.conf: + [snmpd] + # Use a remote master agent + agentXSocket tcp:192.168.15.12:705 +@end example @node SMUX configuration @section SMUX configuration To enable SMUX protocol support, Quagga must have been build with the -@code{--enable-snmp} option. +@code{--enable-snmp=smux} option. -A separate connection has then to be established between between the +A separate connection has then to be established between the SNMP agent (snmpd) and each of the Quagga daemons. This connections each use different OID numbers and passwords. Be aware that this OID number is not the one that is used in queries by clients, it is solely @@ -85,7 +148,7 @@ troublesome @code{snmp_log()} line in the function @section MIB and command reference The following OID numbers are used for the interprocess communication of snmpd and -the Quagga daemons. Sadly, SNMP has not been implemented in all daemons yet. +the Quagga daemons with SMUX only. @example (OIDs below .iso.org.dod.internet.private.enterprises) zebra .1.3.6.1.4.1.3317.1.2.1 .gnome.gnomeProducts.zebra.zserv @@ -95,7 +158,8 @@ ospfd .1.3.6.1.4.1.3317.1.2.5 .gnome.gnomeProducts.zebra.ospfd ospf6d .1.3.6.1.4.1.3317.1.2.6 .gnome.gnomeProducts.zebra.ospf6d @end example -The following OID numbers are used for querying the SNMP daemon by a client: +Sadly, SNMP has not been implemented in all daemons yet. The following +OID numbers are used for querying the SNMP daemon by a client: @example zebra .1.3.6.1.2.1.4.24 .iso.org.dot.internet.mgmt.mib-2.ip.ipForward ospfd .1.3.6.1.2.1.14 .iso.org.dot.internet.mgmt.mib-2.ospf @@ -104,7 +168,7 @@ ripd .1.3.6.1.2.1.23 .iso.org.dot.internet.mgmt.mib-2.rip2 ospf6d .1.3.6.1.3.102 .iso.org.dod.internet.experimental.ospfv3 @end example -The following syntax is understood by the Quagga daemons for configuring SNMP: +The following syntax is understood by the Quagga daemons for configuring SNMP using SMUX: @deffn {Command} {smux peer @var{oid}} {} @deffnx {Command} {no smux peer @var{oid}} {} @end deffn @@ -113,4 +177,9 @@ The following syntax is understood by the Quagga daemons for configuring SNMP: @deffnx {Command} {no smux peer @var{oid} @var{password}} {} @end deffn +Here is the syntax for using AgentX: +@deffn {Command} {agentx} {} +@deffnx {Command} {no agentx} {} +@end deffn + @include snmptrap.texi diff --git a/doc/snmptrap.texi b/doc/snmptrap.texi index 31145639c..6c67288d7 100644 --- a/doc/snmptrap.texi +++ b/doc/snmptrap.texi @@ -69,15 +69,21 @@ like sound a siren, have your display flash, etc., be creative ;). # get some vars from stdin uptime=`echo $INPUT | cut -d' ' -f5` - peer=`echo $INPUT | cut -d' ' -f8 | sed -e 's/SNMPv2-SMI::mib-2.15.3.1.14.//g'` + peer=`echo $INPUT | cut -d' ' -f8 | \ + sed -e 's/SNMPv2-SMI::mib-2.15.3.1.14.//g'` peerstate=`echo $INPUT | cut -d' ' -f13` errorcode=`echo $INPUT | cut -d' ' -f9 | sed -e 's/\"//g'` suberrorcode=`echo $INPUT | cut -d' ' -f10 | sed -e 's/\"//g'` - remoteas=`snmpget -v2c -c $COMMUNITY localhost SNMPv2-SMI::mib-2.15.3.1.9.$peer | cut -d' ' -f4` - - WHOISINFO=`whois -h whois.ripe.net " -r AS$remoteas" | egrep '(as-name|descr)'` - asname=`echo "$WHOISINFO" | grep "^as-name:" | sed -e 's/^as-name://g' -e 's/ //g' -e 's/^ //g' | uniq` - asdescr=`echo "$WHOISINFO" | grep "^descr:" | sed -e 's/^descr://g' -e 's/ //g' -e 's/^ //g' | uniq` + remoteas=`snmpget -v2c -c $COMMUNITY \ + localhost SNMPv2-SMI::mib-2.15.3.1.9.$peer \ + | cut -d' ' -f4` + + WHOISINFO=`whois -h whois.ripe.net " -r AS$remoteas" | \ + egrep '(as-name|descr)'` + asname=`echo "$WHOISINFO" | grep "^as-name:" | \ + sed -e 's/^as-name://g' -e 's/ //g' -e 's/^ //g' | uniq` + asdescr=`echo "$WHOISINFO" | grep "^descr:" | \ + sed -e 's/^descr://g' -e 's/ //g' -e 's/^ //g' | uniq` # if peer address is in $WARN_PEER, the email should also # be sent to $EMAILADDR_WARN diff --git a/doc/texinfo.css b/doc/texinfo.css new file mode 100644 index 000000000..f5fa4f407 --- /dev/null +++ b/doc/texinfo.css @@ -0,0 +1,227 @@ +/* + CSS style for Texinfo documents + + Public domain 2016 sirgazil. All rights waived. + + Obtained from: + + https://sirgazil.bitbucket.io/en/artifact + https://sirgazil.bitbucket.io/en/doc/texinfo-css/tip/manual/static/css/document.css +*/ + + + +/* NATIVE ELEMENTS */ +a:link, +a:visited { + color: #1E90FF; + text-decoration: none; +} + +a:active, +a:focus, +a:hover { + text-decoration: underline; +} + +abbr, +acronym { + cursor: help; +} + +blockquote { + color: #555753; + font-style: oblique; + margin: 30px 0px; + padding-left: 3em; +} + +body { + background-color: white; + box-shadow: 0 0 2px gray; + box-sizing: border-box; + color: #333; + font-family: sans-serif; + font-size: 16px; + margin: 50px auto; + max-width: 960px; + padding: 50px; +} + +code, +samp, +tt, +var { + color: purple; + font-size: 0.8em; +} + +div.example, +div.lisp { + margin: 0px; +} + +dl { + margin: 3em 0em; +} + +dl dl { + margin: 0em; +} + +dt { + background-color: #F5F5F5; + padding: 0.5em; +} + +h1, +h2, +h2.contents-heading, +h3, +h4 { + padding: 20px 0px 0px 0px; + font-weight: normal; +} + +h1 { + font-size: 2.4em; +} + +h2 { + font-size: 2.2em; + font-weight: bold; +} + +h3 { + font-size: 1.8em; +} + +h4 { + font-size: 1.4em; +} + +hr { + background-color: silver; + border-style: none; + height: 1px; + margin: 0px; +} + +html { + background-color: #F5F5F5; +} + +img { + max-width: 100%; +} + +li { + padding: 5px; +} + +pre.display, +pre.example, +pre.format, +pre.lisp, +pre.verbatim{ + overflow: auto; +} + +pre.example, +pre.lisp, +pre.verbatim { + background-color: #2D3743; + border-color: #000; + border-style: solid; + border-width: thin; + color: #E1E1E1; + font-size: smaller; + padding: 1em; +} + +table { + border-collapse: collapse; + margin: 40px 0px; +} + +table.index-cp *, +table.index-fn *, +table.index-ky *, +table.index-pg *, +table.index-tp *, +table.index-vr * { + background-color: inherit; + border-style: none; +} + +td, +th { + border-color: silver; + border-style: solid; + border-width: thin; + padding: 10px; +} + +th { + background-color: #F5F5F5; +} +/* END NATIVE ELEMENTS */ + + + +/* CLASSES */ +.contents { + margin-bottom: 4em; +} + +.float { + margin: 3em 0em; +} + +.float-caption { + font-size: smaller; + text-align: center; +} + +.float > img { + display: block; + margin: auto; +} + +.footnote { + font-size: smaller; + margin: 5em 0em; +} + +.footnote h3 { + display: inline; + font-size: small; +} + +.header { + background-color: #F2F2F2; + font-size: small; + padding: 0.2em 1em; +} + +.key { + color: purple; + font-size: 0.8em; +} + +.menu * { + border-style: none; +} + +.menu td { + padding: 0.5em 0em; +} + +.menu td:last-child { + width: 60%; +} + +.menu th { + background-color: inherit; +} +/* END CLASSES */ diff --git a/doc/texinfo.tex b/doc/texinfo.tex new file mode 100644 index 000000000..ddda670ef --- /dev/null +++ b/doc/texinfo.tex @@ -0,0 +1,11198 @@ +% texinfo.tex -- TeX macros to handle Texinfo files. +% +% Load plain if necessary, i.e., if running under initex. +\expandafter\ifx\csname fmtname\endcsname\relax\input plain\fi +% +\def\texinfoversion{2016-02-05.07} +% +% Copyright 1985, 1986, 1988, 1990, 1991, 1992, 1993, 1994, 1995, +% 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, +% 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 +% Free Software Foundation, Inc. +% +% This texinfo.tex file 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 3 of the +% License, or (at your option) any later version. +% +% This texinfo.tex file 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, see . +% +% As a special exception, when this file is read by TeX when processing +% a Texinfo source document, you may use the result without +% restriction. This Exception is an additional permission under section 7 +% of the GNU General Public License, version 3 ("GPLv3"). +% +% Please try the latest version of texinfo.tex before submitting bug +% reports; you can get the latest version from: +% http://ftp.gnu.org/gnu/texinfo/ (the Texinfo release area), or +% http://ftpmirror.gnu.org/texinfo/ (same, via a mirror), or +% http://www.gnu.org/software/texinfo/ (the Texinfo home page) +% The texinfo.tex in any given distribution could well be out +% of date, so if that's what you're using, please check. +% +% Send bug reports to bug-texinfo@gnu.org. Please include including a +% complete document in each bug report with which we can reproduce the +% problem. Patches are, of course, greatly appreciated. +% +% To process a Texinfo manual with TeX, it's most reliable to use the +% texi2dvi shell script that comes with the distribution. For a simple +% manual foo.texi, however, you can get away with this: +% tex foo.texi +% texindex foo.?? +% tex foo.texi +% tex foo.texi +% dvips foo.dvi -o # or whatever; this makes foo.ps. +% The extra TeX runs get the cross-reference information correct. +% Sometimes one run after texindex suffices, and sometimes you need more +% than two; texi2dvi does it as many times as necessary. +% +% It is possible to adapt texinfo.tex for other languages, to some +% extent. You can get the existing language-specific files from the +% full Texinfo distribution. +% +% The GNU Texinfo home page is http://www.gnu.org/software/texinfo. + + +\message{Loading texinfo [version \texinfoversion]:} + +% If in a .fmt file, print the version number +% and turn on active characters that we couldn't do earlier because +% they might have appeared in the input file name. +\everyjob{\message{[Texinfo version \texinfoversion]}% + \catcode`+=\active \catcode`\_=\active} + +\chardef\other=12 + +% We never want plain's \outer definition of \+ in Texinfo. +% For @tex, we can use \tabalign. +\let\+ = \relax + +% Save some plain tex macros whose names we will redefine. +\let\ptexb=\b +\let\ptexbullet=\bullet +\let\ptexc=\c +\let\ptexcomma=\, +\let\ptexdot=\. +\let\ptexdots=\dots +\let\ptexend=\end +\let\ptexequiv=\equiv +\let\ptexexclam=\! +\let\ptexfootnote=\footnote +\let\ptexgtr=> +\let\ptexhat=^ +\let\ptexi=\i +\let\ptexindent=\indent +\let\ptexinsert=\insert +\let\ptexlbrace=\{ +\let\ptexless=< +\let\ptexnewwrite\newwrite +\let\ptexnoindent=\noindent +\let\ptexplus=+ +\let\ptexraggedright=\raggedright +\let\ptexrbrace=\} +\let\ptexslash=\/ +\let\ptexsp=\sp +\let\ptexstar=\* +\let\ptexsup=\sup +\let\ptext=\t +\let\ptextop=\top +{\catcode`\'=\active \global\let\ptexquoteright'}% active in plain's math mode + +% If this character appears in an error message or help string, it +% starts a new line in the output. +\newlinechar = `^^J + +% Use TeX 3.0's \inputlineno to get the line number, for better error +% messages, but if we're using an old version of TeX, don't do anything. +% +\ifx\inputlineno\thisisundefined + \let\linenumber = \empty % Pre-3.0. +\else + \def\linenumber{l.\the\inputlineno:\space} +\fi + +% Set up fixed words for English if not already set. +\ifx\putwordAppendix\undefined \gdef\putwordAppendix{Appendix}\fi +\ifx\putwordChapter\undefined \gdef\putwordChapter{Chapter}\fi +\ifx\putworderror\undefined \gdef\putworderror{error}\fi +\ifx\putwordfile\undefined \gdef\putwordfile{file}\fi +\ifx\putwordin\undefined \gdef\putwordin{in}\fi +\ifx\putwordIndexIsEmpty\undefined \gdef\putwordIndexIsEmpty{(Index is empty)}\fi +\ifx\putwordIndexNonexistent\undefined \gdef\putwordIndexNonexistent{(Index is nonexistent)}\fi +\ifx\putwordInfo\undefined \gdef\putwordInfo{Info}\fi +\ifx\putwordInstanceVariableof\undefined \gdef\putwordInstanceVariableof{Instance Variable of}\fi +\ifx\putwordMethodon\undefined \gdef\putwordMethodon{Method on}\fi +\ifx\putwordNoTitle\undefined \gdef\putwordNoTitle{No Title}\fi +\ifx\putwordof\undefined \gdef\putwordof{of}\fi +\ifx\putwordon\undefined \gdef\putwordon{on}\fi +\ifx\putwordpage\undefined \gdef\putwordpage{page}\fi +\ifx\putwordsection\undefined \gdef\putwordsection{section}\fi +\ifx\putwordSection\undefined \gdef\putwordSection{Section}\fi +\ifx\putwordsee\undefined \gdef\putwordsee{see}\fi +\ifx\putwordSee\undefined \gdef\putwordSee{See}\fi +\ifx\putwordShortTOC\undefined \gdef\putwordShortTOC{Short Contents}\fi +\ifx\putwordTOC\undefined \gdef\putwordTOC{Table of Contents}\fi +% +\ifx\putwordMJan\undefined \gdef\putwordMJan{January}\fi +\ifx\putwordMFeb\undefined \gdef\putwordMFeb{February}\fi +\ifx\putwordMMar\undefined \gdef\putwordMMar{March}\fi +\ifx\putwordMApr\undefined \gdef\putwordMApr{April}\fi +\ifx\putwordMMay\undefined \gdef\putwordMMay{May}\fi +\ifx\putwordMJun\undefined \gdef\putwordMJun{June}\fi +\ifx\putwordMJul\undefined \gdef\putwordMJul{July}\fi +\ifx\putwordMAug\undefined \gdef\putwordMAug{August}\fi +\ifx\putwordMSep\undefined \gdef\putwordMSep{September}\fi +\ifx\putwordMOct\undefined \gdef\putwordMOct{October}\fi +\ifx\putwordMNov\undefined \gdef\putwordMNov{November}\fi +\ifx\putwordMDec\undefined \gdef\putwordMDec{December}\fi +% +\ifx\putwordDefmac\undefined \gdef\putwordDefmac{Macro}\fi +\ifx\putwordDefspec\undefined \gdef\putwordDefspec{Special Form}\fi +\ifx\putwordDefvar\undefined \gdef\putwordDefvar{Variable}\fi +\ifx\putwordDefopt\undefined \gdef\putwordDefopt{User Option}\fi +\ifx\putwordDeffunc\undefined \gdef\putwordDeffunc{Function}\fi + +% Give the space character the catcode for a space. +\def\spaceisspace{\catcode`\ =10\relax} + +\chardef\dashChar = `\- +\chardef\slashChar = `\/ +\chardef\underChar = `\_ + +% Ignore a token. +% +\def\gobble#1{} + +% The following is used inside several \edef's. +\def\makecsname#1{\expandafter\noexpand\csname#1\endcsname} + +% Hyphenation fixes. +\hyphenation{ + Flor-i-da Ghost-script Ghost-view Mac-OS Post-Script + ap-pen-dix bit-map bit-maps + data-base data-bases eshell fall-ing half-way long-est man-u-script + man-u-scripts mini-buf-fer mini-buf-fers over-view par-a-digm + par-a-digms rath-er rec-tan-gu-lar ro-bot-ics se-vere-ly set-up spa-ces + spell-ing spell-ings + stand-alone strong-est time-stamp time-stamps which-ever white-space + wide-spread wrap-around +} + +% Sometimes it is convenient to have everything in the transcript file +% and nothing on the terminal. We don't just call \tracingall here, +% since that produces some useless output on the terminal. We also make +% some effort to order the tracing commands to reduce output in the log +% file; cf. trace.sty in LaTeX. +% +\def\gloggingall{\begingroup \globaldefs = 1 \loggingall \endgroup}% +\def\loggingall{% + \tracingstats2 + \tracingpages1 + \tracinglostchars2 % 2 gives us more in etex + \tracingparagraphs1 + \tracingoutput1 + \tracingmacros2 + \tracingrestores1 + \showboxbreadth\maxdimen \showboxdepth\maxdimen + \ifx\eTeXversion\thisisundefined\else % etex gives us more logging + \tracingscantokens1 + \tracingifs1 + \tracinggroups1 + \tracingnesting2 + \tracingassigns1 + \fi + \tracingcommands3 % 3 gives us more in etex + \errorcontextlines16 +}% + +% @errormsg{MSG}. Do the index-like expansions on MSG, but if things +% aren't perfect, it's not the end of the world, being an error message, +% after all. +% +\def\errormsg{\begingroup \indexnofonts \doerrormsg} +\def\doerrormsg#1{\errmessage{#1}} + +% add check for \lastpenalty to plain's definitions. If the last thing +% we did was a \nobreak, we don't want to insert more space. +% +\def\smallbreak{\ifnum\lastpenalty<10000\par\ifdim\lastskip<\smallskipamount + \removelastskip\penalty-50\smallskip\fi\fi} +\def\medbreak{\ifnum\lastpenalty<10000\par\ifdim\lastskip<\medskipamount + \removelastskip\penalty-100\medskip\fi\fi} +\def\bigbreak{\ifnum\lastpenalty<10000\par\ifdim\lastskip<\bigskipamount + \removelastskip\penalty-200\bigskip\fi\fi} + +% Output routine +% + +% For a final copy, take out the rectangles +% that mark overfull boxes (in case you have decided +% that the text looks ok even though it passes the margin). +% +\def\finalout{\overfullrule=0pt } + +% Do @cropmarks to get crop marks. +% +\newif\ifcropmarks +\let\cropmarks = \cropmarkstrue +% +% Dimensions to add cropmarks at corners. +% Added by P. A. MacKay, 12 Nov. 1986 +% +\newdimen\outerhsize \newdimen\outervsize % set by the paper size routines +\newdimen\cornerlong \cornerlong=1pc +\newdimen\cornerthick \cornerthick=.3pt +\newdimen\topandbottommargin \topandbottommargin=.75in + +% Output a mark which sets \thischapter, \thissection and \thiscolor. +% We dump everything together because we only have one kind of mark. +% This works because we only use \botmark / \topmark, not \firstmark. +% +% A mark contains a subexpression of the \ifcase ... \fi construct. +% \get*marks macros below extract the needed part using \ifcase. +% +% Another complication is to let the user choose whether \thischapter +% (\thissection) refers to the chapter (section) in effect at the top +% of a page, or that at the bottom of a page. + +% \domark is called twice inside \chapmacro, to add one +% mark before the section break, and one after. +% In the second call \prevchapterdefs is the same as \lastchapterdefs, +% and \prevsectiondefs is the same as \lastsectiondefs. +% Then if the page is not broken at the mark, some of the previous +% section appears on the page, and we can get the name of this section +% from \firstmark for @everyheadingmarks top. +% @everyheadingmarks bottom uses \botmark. +% +% See page 260 of The TeXbook. +\def\domark{% + \toks0=\expandafter{\lastchapterdefs}% + \toks2=\expandafter{\lastsectiondefs}% + \toks4=\expandafter{\prevchapterdefs}% + \toks6=\expandafter{\prevsectiondefs}% + \toks8=\expandafter{\lastcolordefs}% + \mark{% + \the\toks0 \the\toks2 % 0: marks for @everyheadingmarks top + \noexpand\or \the\toks4 \the\toks6 % 1: for @everyheadingmarks bottom + \noexpand\else \the\toks8 % 2: color marks + }% +} + +% \gettopheadingmarks, \getbottomheadingmarks, +% \getcolormarks - extract needed part of mark. +% +% \topmark doesn't work for the very first chapter (after the title +% page or the contents), so we use \firstmark there -- this gets us +% the mark with the chapter defs, unless the user sneaks in, e.g., +% @setcolor (or @url, or @link, etc.) between @contents and the very +% first @chapter. +\def\gettopheadingmarks{% + \ifcase0\topmark\fi + \ifx\thischapter\empty \ifcase0\firstmark\fi \fi +} +\def\getbottomheadingmarks{\ifcase1\botmark\fi} +\def\getcolormarks{\ifcase2\topmark\fi} + +% Avoid "undefined control sequence" errors. +\def\lastchapterdefs{} +\def\lastsectiondefs{} +\def\lastsection{} +\def\prevchapterdefs{} +\def\prevsectiondefs{} +\def\lastcolordefs{} + +% Margin to add to right of even pages, to left of odd pages. +\newdimen\bindingoffset +\newdimen\normaloffset +\newdimen\pagewidth \newdimen\pageheight + +% Main output routine. +% +\chardef\PAGE = 255 +\output = {\onepageout{\pagecontents\PAGE}} + +\newbox\headlinebox +\newbox\footlinebox + +% \onepageout takes a vbox as an argument. +% \shipout a vbox for a single page, adding an optional header, footer, +% cropmarks, and footnote. This also causes index entries for this page +% to be written to the auxiliary files. +% +\def\onepageout#1{% + \ifcropmarks \hoffset=0pt \else \hoffset=\normaloffset \fi + % + \ifodd\pageno \advance\hoffset by \bindingoffset + \else \advance\hoffset by -\bindingoffset\fi + % + % Common context changes for both heading and footing. + % Do this outside of the \shipout so @code etc. will be expanded in + % the headline as they should be, not taken literally (outputting ''code). + \def\commmonheadfootline{\let\hsize=\pagewidth \texinfochars} + % + % Retrieve the information for the headings from the marks in the page, + % and call Plain TeX's \makeheadline and \makefootline, which use the + % values in \headline and \footline. + % + % This is used to check if we are on the first page of a chapter. + \ifcase1\topmark\fi + \let\prevchaptername\thischaptername + \ifcase0\firstmark\fi + \let\curchaptername\thischaptername + % + \ifodd\pageno \getoddheadingmarks \else \getevenheadingmarks \fi + \ifodd\pageno \getoddfootingmarks \else \getevenfootingmarks \fi + % + \ifx\curchaptername\prevchaptername + \let\thischapterheading\thischapter + \else + % \thischapterheading is the same as \thischapter except it is blank + % for the first page of a chapter. This is to prevent the chapter name + % being shown twice. + \def\thischapterheading{}% + \fi + % + \global\setbox\headlinebox = \vbox{\commmonheadfootline \makeheadline}% + \global\setbox\footlinebox = \vbox{\commmonheadfootline \makefootline}% + % + {% + % Set context for writing to auxiliary files like index files. + % Have to do this stuff outside the \shipout because we want it to + % take effect in \write's, yet the group defined by the \vbox ends + % before the \shipout runs. + % + \indexdummies % don't expand commands in the output. + \normalturnoffactive % \ in index entries must not stay \, e.g., if + % the page break happens to be in the middle of an example. + % We don't want .vr (or whatever) entries like this: + % \entry{{\indexbackslash }acronym}{32}{\code {\acronym}} + % "\acronym" won't work when it's read back in; + % it needs to be + % {\code {{\backslashcurfont }acronym} + \shipout\vbox{% + % Do this early so pdf references go to the beginning of the page. + \ifpdfmakepagedest \pdfdest name{\the\pageno} xyz\fi + % + \ifcropmarks \vbox to \outervsize\bgroup + \hsize = \outerhsize + \vskip-\topandbottommargin + \vtop to0pt{% + \line{\ewtop\hfil\ewtop}% + \nointerlineskip + \line{% + \vbox{\moveleft\cornerthick\nstop}% + \hfill + \vbox{\moveright\cornerthick\nstop}% + }% + \vss}% + \vskip\topandbottommargin + \line\bgroup + \hfil % center the page within the outer (page) hsize. + \ifodd\pageno\hskip\bindingoffset\fi + \vbox\bgroup + \fi + % + \unvbox\headlinebox + \pagebody{#1}% + \ifdim\ht\footlinebox > 0pt + % Only leave this space if the footline is nonempty. + % (We lessened \vsize for it in \oddfootingyyy.) + % The \baselineskip=24pt in plain's \makefootline has no effect. + \vskip 24pt + \unvbox\footlinebox + \fi + % + \ifcropmarks + \egroup % end of \vbox\bgroup + \hfil\egroup % end of (centering) \line\bgroup + \vskip\topandbottommargin plus1fill minus1fill + \boxmaxdepth = \cornerthick + \vbox to0pt{\vss + \line{% + \vbox{\moveleft\cornerthick\nsbot}% + \hfill + \vbox{\moveright\cornerthick\nsbot}% + }% + \nointerlineskip + \line{\ewbot\hfil\ewbot}% + }% + \egroup % \vbox from first cropmarks clause + \fi + }% end of \shipout\vbox + }% end of group with \indexdummies + \advancepageno + \ifnum\outputpenalty>-20000 \else\dosupereject\fi +} + +\newinsert\margin \dimen\margin=\maxdimen + +% Main part of page, including any footnotes +\def\pagebody#1{\vbox to\pageheight{\boxmaxdepth=\maxdepth #1}} +{\catcode`\@ =11 +\gdef\pagecontents#1{\ifvoid\topins\else\unvbox\topins\fi +% marginal hacks, juha@viisa.uucp (Juha Takala) +\ifvoid\margin\else % marginal info is present + \rlap{\kern\hsize\vbox to\z@{\kern1pt\box\margin \vss}}\fi +\dimen@=\dp#1\relax \unvbox#1\relax +\ifvoid\footins\else\vskip\skip\footins\footnoterule \unvbox\footins\fi +\ifr@ggedbottom \kern-\dimen@ \vfil \fi} +} + +% Here are the rules for the cropmarks. Note that they are +% offset so that the space between them is truly \outerhsize or \outervsize +% (P. A. MacKay, 12 November, 1986) +% +\def\ewtop{\vrule height\cornerthick depth0pt width\cornerlong} +\def\nstop{\vbox + {\hrule height\cornerthick depth\cornerlong width\cornerthick}} +\def\ewbot{\vrule height0pt depth\cornerthick width\cornerlong} +\def\nsbot{\vbox + {\hrule height\cornerlong depth\cornerthick width\cornerthick}} + + +% Argument parsing + +% Parse an argument, then pass it to #1. The argument is the rest of +% the input line (except we remove a trailing comment). #1 should be a +% macro which expects an ordinary undelimited TeX argument. +% For example, \def\foo{\parsearg\fooxxx}. +% +\def\parsearg{\parseargusing{}} +\def\parseargusing#1#2{% + \def\argtorun{#2}% + \begingroup + \obeylines + \spaceisspace + #1% + \parseargline\empty% Insert the \empty token, see \finishparsearg below. +} + +{\obeylines % + \gdef\parseargline#1^^M{% + \endgroup % End of the group started in \parsearg. + \argremovecomment #1\comment\ArgTerm% + }% +} + +% First remove any @comment, then any @c comment. Also remove a @texinfoc +% comment (see \scanmacro for details). Pass the result on to \argcheckspaces. +\def\argremovecomment#1\comment#2\ArgTerm{\argremovec #1\c\ArgTerm} +\def\argremovec#1\c#2\ArgTerm{\argremovetexinfoc #1\texinfoc\ArgTerm} +\def\argremovetexinfoc#1\texinfoc#2\ArgTerm{\argcheckspaces#1\^^M\ArgTerm} + +% Each occurrence of `\^^M' or `\^^M' is replaced by a single space. +% +% \argremovec might leave us with trailing space, e.g., +% @end itemize @c foo +% This space token undergoes the same procedure and is eventually removed +% by \finishparsearg. +% +\def\argcheckspaces#1\^^M{\argcheckspacesX#1\^^M \^^M} +\def\argcheckspacesX#1 \^^M{\argcheckspacesY#1\^^M} +\def\argcheckspacesY#1\^^M#2\^^M#3\ArgTerm{% + \def\temp{#3}% + \ifx\temp\empty + % Do not use \next, perhaps the caller of \parsearg uses it; reuse \temp: + \let\temp\finishparsearg + \else + \let\temp\argcheckspaces + \fi + % Put the space token in: + \temp#1 #3\ArgTerm +} + +% If a _delimited_ argument is enclosed in braces, they get stripped; so +% to get _exactly_ the rest of the line, we had to prevent such situation. +% We prepended an \empty token at the very beginning and we expand it now, +% just before passing the control to \argtorun. +% (Similarly, we have to think about #3 of \argcheckspacesY above: it is +% either the null string, or it ends with \^^M---thus there is no danger +% that a pair of braces would be stripped. +% +% But first, we have to remove the trailing space token. +% +\def\finishparsearg#1 \ArgTerm{\expandafter\argtorun\expandafter{#1}} + + +% \parseargdef - define a command taking an argument on the line +% +% \parseargdef\foo{...} +% is roughly equivalent to +% \def\foo{\parsearg\Xfoo} +% \def\Xfoo#1{...} +\def\parseargdef#1{% + \expandafter \doparseargdef \csname\string#1\endcsname #1% +} +\def\doparseargdef#1#2{% + \def#2{\parsearg#1}% + \def#1##1% +} + +% Several utility definitions with active space: +{ + \obeyspaces + \gdef\obeyedspace{ } + + % Make each space character in the input produce a normal interword + % space in the output. Don't allow a line break at this space, as this + % is used only in environments like @example, where each line of input + % should produce a line of output anyway. + % + \gdef\sepspaces{\obeyspaces\let =\tie} + + % If an index command is used in an @example environment, any spaces + % therein should become regular spaces in the raw index file, not the + % expansion of \tie (\leavevmode \penalty \@M \ ). + \gdef\unsepspaces{\let =\space} +} + + +\def\flushcr{\ifx\par\lisppar \def\next##1{}\else \let\next=\relax \fi \next} + +% Define the framework for environments in texinfo.tex. It's used like this: +% +% \envdef\foo{...} +% \def\Efoo{...} +% +% It's the responsibility of \envdef to insert \begingroup before the +% actual body; @end closes the group after calling \Efoo. \envdef also +% defines \thisenv, so the current environment is known; @end checks +% whether the environment name matches. The \checkenv macro can also be +% used to check whether the current environment is the one expected. +% +% Non-false conditionals (@iftex, @ifset) don't fit into this, so they +% are not treated as environments; they don't open a group. (The +% implementation of @end takes care not to call \endgroup in this +% special case.) + + +% At run-time, environments start with this: +\def\startenvironment#1{\begingroup\def\thisenv{#1}} +% initialize +\let\thisenv\empty + +% ... but they get defined via ``\envdef\foo{...}'': +\long\def\envdef#1#2{\def#1{\startenvironment#1#2}} +\def\envparseargdef#1#2{\parseargdef#1{\startenvironment#1#2}} + +% Check whether we're in the right environment: +\def\checkenv#1{% + \def\temp{#1}% + \ifx\thisenv\temp + \else + \badenverr + \fi +} + +% Environment mismatch, #1 expected: +\def\badenverr{% + \errhelp = \EMsimple + \errmessage{This command can appear only \inenvironment\temp, + not \inenvironment\thisenv}% +} +\def\inenvironment#1{% + \ifx#1\empty + outside of any environment% + \else + in environment \expandafter\string#1% + \fi +} + +% @end foo executes the definition of \Efoo. +% But first, it executes a specialized version of \checkenv +% +\parseargdef\end{% + \if 1\csname iscond.#1\endcsname + \else + % The general wording of \badenverr may not be ideal. + \expandafter\checkenv\csname#1\endcsname + \csname E#1\endcsname + \endgroup + \fi +} + +\newhelp\EMsimple{Press RETURN to continue.} + + +% Be sure we're in horizontal mode when doing a tie, since we make space +% equivalent to this in @example-like environments. Otherwise, a space +% at the beginning of a line will start with \penalty -- and +% since \penalty is valid in vertical mode, we'd end up putting the +% penalty on the vertical list instead of in the new paragraph. +{\catcode`@ = 11 + % Avoid using \@M directly, because that causes trouble + % if the definition is written into an index file. + \global\let\tiepenalty = \@M + \gdef\tie{\leavevmode\penalty\tiepenalty\ } +} + +% @: forces normal size whitespace following. +\def\:{\spacefactor=1000 } + +% @* forces a line break. +\def\*{\unskip\hfil\break\hbox{}\ignorespaces} + +% @/ allows a line break. +\let\/=\allowbreak + +% @. is an end-of-sentence period. +\def\.{.\spacefactor=\endofsentencespacefactor\space} + +% @! is an end-of-sentence bang. +\def\!{!\spacefactor=\endofsentencespacefactor\space} + +% @? is an end-of-sentence query. +\def\?{?\spacefactor=\endofsentencespacefactor\space} + +% @frenchspacing on|off says whether to put extra space after punctuation. +% +\def\onword{on} +\def\offword{off} +% +\parseargdef\frenchspacing{% + \def\temp{#1}% + \ifx\temp\onword \plainfrenchspacing + \else\ifx\temp\offword \plainnonfrenchspacing + \else + \errhelp = \EMsimple + \errmessage{Unknown @frenchspacing option `\temp', must be on|off}% + \fi\fi +} + +% @w prevents a word break. Without the \leavevmode, @w at the +% beginning of a paragraph, when TeX is still in vertical mode, would +% produce a whole line of output instead of starting the paragraph. +\def\w#1{\leavevmode\hbox{#1}} + +% @group ... @end group forces ... to be all on one page, by enclosing +% it in a TeX vbox. We use \vtop instead of \vbox to construct the box +% to keep its height that of a normal line. According to the rules for +% \topskip (p.114 of the TeXbook), the glue inserted is +% max (\topskip - \ht (first item), 0). If that height is large, +% therefore, no glue is inserted, and the space between the headline and +% the text is small, which looks bad. +% +% Another complication is that the group might be very large. This can +% cause the glue on the previous page to be unduly stretched, because it +% does not have much material. In this case, it's better to add an +% explicit \vfill so that the extra space is at the bottom. The +% threshold for doing this is if the group is more than \vfilllimit +% percent of a page (\vfilllimit can be changed inside of @tex). +% +\newbox\groupbox +\def\vfilllimit{0.7} +% +\envdef\group{% + \ifnum\catcode`\^^M=\active \else + \errhelp = \groupinvalidhelp + \errmessage{@group invalid in context where filling is enabled}% + \fi + \startsavinginserts + % + \setbox\groupbox = \vtop\bgroup + % Do @comment since we are called inside an environment such as + % @example, where each end-of-line in the input causes an + % end-of-line in the output. We don't want the end-of-line after + % the `@group' to put extra space in the output. Since @group + % should appear on a line by itself (according to the Texinfo + % manual), we don't worry about eating any user text. + \comment +} +% +% The \vtop produces a box with normal height and large depth; thus, TeX puts +% \baselineskip glue before it, and (when the next line of text is done) +% \lineskip glue after it. Thus, space below is not quite equal to space +% above. But it's pretty close. +\def\Egroup{% + % To get correct interline space between the last line of the group + % and the first line afterwards, we have to propagate \prevdepth. + \endgraf % Not \par, as it may have been set to \lisppar. + \global\dimen1 = \prevdepth + \egroup % End the \vtop. + \addgroupbox + \prevdepth = \dimen1 + \checkinserts +} + +\def\addgroupbox{ + % \dimen0 is the vertical size of the group's box. + \dimen0 = \ht\groupbox \advance\dimen0 by \dp\groupbox + % \dimen2 is how much space is left on the page (more or less). + \dimen2 = \pageheight \advance\dimen2 by -\pagetotal + % if the group doesn't fit on the current page, and it's a big big + % group, force a page break. + \ifdim \dimen0 > \dimen2 + \ifdim \pagetotal < \vfilllimit\pageheight + \page + \fi + \fi + \box\groupbox +} + +% +% TeX puts in an \escapechar (i.e., `@') at the beginning of the help +% message, so this ends up printing `@group can only ...'. +% +\newhelp\groupinvalidhelp{% +group can only be used in environments such as @example,^^J% +where each line of input produces a line of output.} + +% @need space-in-mils +% forces a page break if there is not space-in-mils remaining. + +\newdimen\mil \mil=0.001in + +\parseargdef\need{% + % Ensure vertical mode, so we don't make a big box in the middle of a + % paragraph. + \par + % + % If the @need value is less than one line space, it's useless. + \dimen0 = #1\mil + \dimen2 = \ht\strutbox + \advance\dimen2 by \dp\strutbox + \ifdim\dimen0 > \dimen2 + % + % Do a \strut just to make the height of this box be normal, so the + % normal leading is inserted relative to the preceding line. + % And a page break here is fine. + \vtop to #1\mil{\strut\vfil}% + % + % TeX does not even consider page breaks if a penalty added to the + % main vertical list is 10000 or more. But in order to see if the + % empty box we just added fits on the page, we must make it consider + % page breaks. On the other hand, we don't want to actually break the + % page after the empty box. So we use a penalty of 9999. + % + % There is an extremely small chance that TeX will actually break the + % page at this \penalty, if there are no other feasible breakpoints in + % sight. (If the user is using lots of big @group commands, which + % almost-but-not-quite fill up a page, TeX will have a hard time doing + % good page breaking, for example.) However, I could not construct an + % example where a page broke at this \penalty; if it happens in a real + % document, then we can reconsider our strategy. + \penalty9999 + % + % Back up by the size of the box, whether we did a page break or not. + \kern -#1\mil + % + % Do not allow a page break right after this kern. + \nobreak + \fi +} + +% @br forces paragraph break (and is undocumented). + +\let\br = \par + +% @page forces the start of a new page. +% +\def\page{\par\vfill\supereject} + +% @exdent text.... +% outputs text on separate line in roman font, starting at standard page margin + +% This records the amount of indent in the innermost environment. +% That's how much \exdent should take out. +\newskip\exdentamount + +% This defn is used inside fill environments such as @defun. +\parseargdef\exdent{\hfil\break\hbox{\kern -\exdentamount{\rm#1}}\hfil\break} + +% This defn is used inside nofill environments such as @example. +\parseargdef\nofillexdent{{\advance \leftskip by -\exdentamount + \leftline{\hskip\leftskip{\rm#1}}}} + +% @inmargin{WHICH}{TEXT} puts TEXT in the WHICH margin next to the current +% paragraph. For more general purposes, use the \margin insertion +% class. WHICH is `l' or `r'. Not documented, written for gawk manual. +% +\newskip\inmarginspacing \inmarginspacing=1cm +\def\strutdepth{\dp\strutbox} +% +\def\doinmargin#1#2{\strut\vadjust{% + \nobreak + \kern-\strutdepth + \vtop to \strutdepth{% + \baselineskip=\strutdepth + \vss + % if you have multiple lines of stuff to put here, you'll need to + % make the vbox yourself of the appropriate size. + \ifx#1l% + \llap{\ignorespaces #2\hskip\inmarginspacing}% + \else + \rlap{\hskip\hsize \hskip\inmarginspacing \ignorespaces #2}% + \fi + \null + }% +}} +\def\inleftmargin{\doinmargin l} +\def\inrightmargin{\doinmargin r} +% +% @inmargin{TEXT [, RIGHT-TEXT]} +% (if RIGHT-TEXT is given, use TEXT for left page, RIGHT-TEXT for right; +% else use TEXT for both). +% +\def\inmargin#1{\parseinmargin #1,,\finish} +\def\parseinmargin#1,#2,#3\finish{% not perfect, but better than nothing. + \setbox0 = \hbox{\ignorespaces #2}% + \ifdim\wd0 > 0pt + \def\lefttext{#1}% have both texts + \def\righttext{#2}% + \else + \def\lefttext{#1}% have only one text + \def\righttext{#1}% + \fi + % + \ifodd\pageno + \def\temp{\inrightmargin\righttext}% odd page -> outside is right margin + \else + \def\temp{\inleftmargin\lefttext}% + \fi + \temp +} + +% @| inserts a changebar to the left of the current line. It should +% surround any changed text. This approach does *not* work if the +% change spans more than two lines of output. To handle that, we would +% have adopt a much more difficult approach (putting marks into the main +% vertical list for the beginning and end of each change). This command +% is not documented, not supported, and doesn't work. +% +\def\|{% + % \vadjust can only be used in horizontal mode. + \leavevmode + % + % Append this vertical mode material after the current line in the output. + \vadjust{% + % We want to insert a rule with the height and depth of the current + % leading; that is exactly what \strutbox is supposed to record. + \vskip-\baselineskip + % + % \vadjust-items are inserted at the left edge of the type. So + % the \llap here moves out into the left-hand margin. + \llap{% + % + % For a thicker or thinner bar, change the `1pt'. + \vrule height\baselineskip width1pt + % + % This is the space between the bar and the text. + \hskip 12pt + }% + }% +} + +% @include FILE -- \input text of FILE. +% +\def\include{\parseargusing\filenamecatcodes\includezzz} +\def\includezzz#1{% + \pushthisfilestack + \def\thisfile{#1}% + {% + \makevalueexpandable % we want to expand any @value in FILE. + \turnoffactive % and allow special characters in the expansion + \indexnofonts % Allow `@@' and other weird things in file names. + \wlog{texinfo.tex: doing @include of #1^^J}% + \edef\temp{\noexpand\input #1 }% + % + % This trickery is to read FILE outside of a group, in case it makes + % definitions, etc. + \expandafter + }\temp + \popthisfilestack +} +\def\filenamecatcodes{% + \catcode`\\=\other + \catcode`~=\other + \catcode`^=\other + \catcode`_=\other + \catcode`|=\other + \catcode`<=\other + \catcode`>=\other + \catcode`+=\other + \catcode`-=\other + \catcode`\`=\other + \catcode`\'=\other +} + +\def\pushthisfilestack{% + \expandafter\pushthisfilestackX\popthisfilestack\StackTerm +} +\def\pushthisfilestackX{% + \expandafter\pushthisfilestackY\thisfile\StackTerm +} +\def\pushthisfilestackY #1\StackTerm #2\StackTerm {% + \gdef\popthisfilestack{\gdef\thisfile{#1}\gdef\popthisfilestack{#2}}% +} + +\def\popthisfilestack{\errthisfilestackempty} +\def\errthisfilestackempty{\errmessage{Internal error: + the stack of filenames is empty.}} +% +\def\thisfile{} + +% @center line +% outputs that line, centered. +% +\parseargdef\center{% + \ifhmode + \let\centersub\centerH + \else + \let\centersub\centerV + \fi + \centersub{\hfil \ignorespaces#1\unskip \hfil}% + \let\centersub\relax % don't let the definition persist, just in case +} +\def\centerH#1{{% + \hfil\break + \advance\hsize by -\leftskip + \advance\hsize by -\rightskip + \line{#1}% + \break +}} +% +\newcount\centerpenalty +\def\centerV#1{% + % The idea here is the same as in \startdefun, \cartouche, etc.: if + % @center is the first thing after a section heading, we need to wipe + % out the negative parskip inserted by \sectionheading, but still + % prevent a page break here. + \centerpenalty = \lastpenalty + \ifnum\centerpenalty>10000 \vskip\parskip \fi + \ifnum\centerpenalty>9999 \penalty\centerpenalty \fi + \line{\kern\leftskip #1\kern\rightskip}% +} + +% @sp n outputs n lines of vertical space +% +\parseargdef\sp{\vskip #1\baselineskip} + +% @comment ...line which is ignored... +% @c is the same as @comment +% @ignore ... @end ignore is another way to write a comment +% +\def\comment{\begingroup \catcode`\^^M=\active% +\catcode`\@=\other \catcode`\{=\other \catcode`\}=\other\commentxxx}% + +{\catcode`\^^M=\active% +\gdef\commentxxx#1^^M{\endgroup% +\futurelet\nexttoken\commentxxxx}% +\gdef\commentxxxx{\ifx\nexttoken\aftermacro\expandafter\comment\fi}% +} + +\def\c{\begingroup \catcode`\^^M=\active% +\catcode`\@=\other \catcode`\{=\other \catcode`\}=\other% +\cxxx} +{\catcode`\^^M=\active \gdef\cxxx#1^^M{\endgroup}} +% See comment in \scanmacro about why the definitions of @c and @comment differ + +% @paragraphindent NCHARS +% We'll use ems for NCHARS, close enough. +% NCHARS can also be the word `asis' or `none'. +% We cannot feasibly implement @paragraphindent asis, though. +% +\def\asisword{asis} % no translation, these are keywords +\def\noneword{none} +% +\parseargdef\paragraphindent{% + \def\temp{#1}% + \ifx\temp\asisword + \else + \ifx\temp\noneword + \defaultparindent = 0pt + \else + \defaultparindent = #1em + \fi + \fi + \parindent = \defaultparindent +} + +% @exampleindent NCHARS +% We'll use ems for NCHARS like @paragraphindent. +% It seems @exampleindent asis isn't necessary, but +% I preserve it to make it similar to @paragraphindent. +\parseargdef\exampleindent{% + \def\temp{#1}% + \ifx\temp\asisword + \else + \ifx\temp\noneword + \lispnarrowing = 0pt + \else + \lispnarrowing = #1em + \fi + \fi +} + +% @firstparagraphindent WORD +% If WORD is `none', then suppress indentation of the first paragraph +% after a section heading. If WORD is `insert', then do indent at such +% paragraphs. +% +% The paragraph indentation is suppressed or not by calling +% \suppressfirstparagraphindent, which the sectioning commands do. +% We switch the definition of this back and forth according to WORD. +% By default, we suppress indentation. +% +\def\suppressfirstparagraphindent{\dosuppressfirstparagraphindent} +\def\insertword{insert} +% +\parseargdef\firstparagraphindent{% + \def\temp{#1}% + \ifx\temp\noneword + \let\suppressfirstparagraphindent = \dosuppressfirstparagraphindent + \else\ifx\temp\insertword + \let\suppressfirstparagraphindent = \relax + \else + \errhelp = \EMsimple + \errmessage{Unknown @firstparagraphindent option `\temp'}% + \fi\fi +} + +% Here is how we actually suppress indentation. Redefine \everypar to +% \kern backwards by \parindent, and then reset itself to empty. +% +% We also make \indent itself not actually do anything until the next +% paragraph. +% +\gdef\dosuppressfirstparagraphindent{% + \gdef\indent {\restorefirstparagraphindent \indent}% + \gdef\noindent{\restorefirstparagraphindent \noindent}% + \global\everypar = {\kern -\parindent \restorefirstparagraphindent}% +} +% +\gdef\restorefirstparagraphindent{% + \global\let\indent = \ptexindent + \global\let\noindent = \ptexnoindent + \global\everypar = {}% +} + + +% @refill is a no-op. +\let\refill=\relax + +% @setfilename INFO-FILENAME - ignored +\let\setfilename=\comment + +% @bye. +\outer\def\bye{\pagealignmacro\tracingstats=1\ptexend} + + +\message{pdf,} +% adobe `portable' document format +\newcount\tempnum +\newcount\lnkcount +\newtoks\filename +\newcount\filenamelength +\newcount\pgn +\newtoks\toksA +\newtoks\toksB +\newtoks\toksC +\newtoks\toksD +\newbox\boxA +\newbox\boxB +\newcount\countA +\newif\ifpdf +\newif\ifpdfmakepagedest + +% when pdftex is run in dvi mode, \pdfoutput is defined (so \pdfoutput=1 +% can be set). So we test for \relax and 0 as well as being undefined. +\ifx\pdfoutput\thisisundefined +\else + \ifx\pdfoutput\relax + \else + \ifcase\pdfoutput + \else + \pdftrue + \fi + \fi +\fi + +% PDF uses PostScript string constants for the names of xref targets, +% for display in the outlines, and in other places. Thus, we have to +% double any backslashes. Otherwise, a name like "\node" will be +% interpreted as a newline (\n), followed by o, d, e. Not good. +% +% See http://www.ntg.nl/pipermail/ntg-pdftex/2004-July/000654.html and +% related messages. The final outcome is that it is up to the TeX user +% to double the backslashes and otherwise make the string valid, so +% that's what we do. pdftex 1.30.0 (ca.2005) introduced a primitive to +% do this reliably, so we use it. + +% #1 is a control sequence in which to do the replacements, +% which we \xdef. +\def\txiescapepdf#1{% + \ifx\pdfescapestring\thisisundefined + % No primitive available; should we give a warning or log? + % Many times it won't matter. + \else + % The expandable \pdfescapestring primitive escapes parentheses, + % backslashes, and other special chars. + \xdef#1{\pdfescapestring{#1}}% + \fi +} + +\newhelp\nopdfimagehelp{Texinfo supports .png, .jpg, .jpeg, and .pdf images +with PDF output, and none of those formats could be found. (.eps cannot +be supported due to the design of the PDF format; use regular TeX (DVI +output) for that.)} + +\ifpdf + % + % Color manipulation macros using ideas from pdfcolor.tex, + % except using rgb instead of cmyk; the latter is said to render as a + % very dark gray on-screen and a very dark halftone in print, instead + % of actual black. The dark red here is dark enough to print on paper as + % nearly black, but still distinguishable for online viewing. We use + % black by default, though. + \def\rgbDarkRed{0.50 0.09 0.12} + \def\rgbBlack{0 0 0} + % + % rg sets the color for filling (usual text, etc.); + % RG sets the color for stroking (thin rules, e.g., normal _'s). + \def\pdfsetcolor#1{\pdfliteral{#1 rg #1 RG}} + % + % Set color, and create a mark which defines \thiscolor accordingly, + % so that \makeheadline knows which color to restore. + \def\setcolor#1{% + \xdef\lastcolordefs{\gdef\noexpand\thiscolor{#1}}% + \domark + \pdfsetcolor{#1}% + } + % + \def\maincolor{\rgbBlack} + \pdfsetcolor{\maincolor} + \edef\thiscolor{\maincolor} + \def\lastcolordefs{} + % + \def\makefootline{% + \baselineskip24pt + \line{\pdfsetcolor{\maincolor}\the\footline}% + } + % + \def\makeheadline{% + \vbox to 0pt{% + \vskip-22.5pt + \line{% + \vbox to8.5pt{}% + % Extract \thiscolor definition from the marks. + \getcolormarks + % Typeset the headline with \maincolor, then restore the color. + \pdfsetcolor{\maincolor}\the\headline\pdfsetcolor{\thiscolor}% + }% + \vss + }% + \nointerlineskip + } + % + % + \pdfcatalog{/PageMode /UseOutlines} + % + % #1 is image name, #2 width (might be empty/whitespace), #3 height (ditto). + \def\dopdfimage#1#2#3{% + \def\pdfimagewidth{#2}\setbox0 = \hbox{\ignorespaces #2}% + \def\pdfimageheight{#3}\setbox2 = \hbox{\ignorespaces #3}% + % + % pdftex (and the PDF format) support .pdf, .png, .jpg (among + % others). Let's try in that order, PDF first since if + % someone has a scalable image, presumably better to use that than a + % bitmap. + \let\pdfimgext=\empty + \begingroup + \openin 1 #1.pdf \ifeof 1 + \openin 1 #1.PDF \ifeof 1 + \openin 1 #1.png \ifeof 1 + \openin 1 #1.jpg \ifeof 1 + \openin 1 #1.jpeg \ifeof 1 + \openin 1 #1.JPG \ifeof 1 + \errhelp = \nopdfimagehelp + \errmessage{Could not find image file #1 for pdf}% + \else \gdef\pdfimgext{JPG}% + \fi + \else \gdef\pdfimgext{jpeg}% + \fi + \else \gdef\pdfimgext{jpg}% + \fi + \else \gdef\pdfimgext{png}% + \fi + \else \gdef\pdfimgext{PDF}% + \fi + \else \gdef\pdfimgext{pdf}% + \fi + \closein 1 + \endgroup + % + % without \immediate, ancient pdftex seg faults when the same image is + % included twice. (Version 3.14159-pre-1.0-unofficial-20010704.) + \ifnum\pdftexversion < 14 + \immediate\pdfimage + \else + \immediate\pdfximage + \fi + \ifdim \wd0 >0pt width \pdfimagewidth \fi + \ifdim \wd2 >0pt height \pdfimageheight \fi + \ifnum\pdftexversion<13 + #1.\pdfimgext + \else + {#1.\pdfimgext}% + \fi + \ifnum\pdftexversion < 14 \else + \pdfrefximage \pdflastximage + \fi} + % + \def\pdfmkdest#1{{% + % We have to set dummies so commands such as @code, and characters + % such as \, aren't expanded when present in a section title. + \indexnofonts + \turnoffactive + \makevalueexpandable + \def\pdfdestname{#1}% + \txiescapepdf\pdfdestname + \safewhatsit{\pdfdest name{\pdfdestname} xyz}% + }} + % + % used to mark target names; must be expandable. + \def\pdfmkpgn#1{#1} + % + % by default, use black for everything. + \def\urlcolor{\rgbBlack} + \def\linkcolor{\rgbBlack} + \def\endlink{\setcolor{\maincolor}\pdfendlink} + % + % Adding outlines to PDF; macros for calculating structure of outlines + % come from Petr Olsak + \def\expnumber#1{\expandafter\ifx\csname#1\endcsname\relax 0% + \else \csname#1\endcsname \fi} + \def\advancenumber#1{\tempnum=\expnumber{#1}\relax + \advance\tempnum by 1 + \expandafter\xdef\csname#1\endcsname{\the\tempnum}} + % + % #1 is the section text, which is what will be displayed in the + % outline by the pdf viewer. #2 is the pdf expression for the number + % of subentries (or empty, for subsubsections). #3 is the node text, + % which might be empty if this toc entry had no corresponding node. + % #4 is the page number + % + \def\dopdfoutline#1#2#3#4{% + % Generate a link to the node text if that exists; else, use the + % page number. We could generate a destination for the section + % text in the case where a section has no node, but it doesn't + % seem worth the trouble, since most documents are normally structured. + \edef\pdfoutlinedest{#3}% + \ifx\pdfoutlinedest\empty + \def\pdfoutlinedest{#4}% + \else + \txiescapepdf\pdfoutlinedest + \fi + % + % Also escape PDF chars in the display string. + \edef\pdfoutlinetext{#1}% + \txiescapepdf\pdfoutlinetext + % + \pdfoutline goto name{\pdfmkpgn{\pdfoutlinedest}}#2{\pdfoutlinetext}% + } + % + \def\pdfmakeoutlines{% + \begingroup + % Read toc silently, to get counts of subentries for \pdfoutline. + \def\partentry##1##2##3##4{}% ignore parts in the outlines + \def\numchapentry##1##2##3##4{% + \def\thischapnum{##2}% + \def\thissecnum{0}% + \def\thissubsecnum{0}% + }% + \def\numsecentry##1##2##3##4{% + \advancenumber{chap\thischapnum}% + \def\thissecnum{##2}% + \def\thissubsecnum{0}% + }% + \def\numsubsecentry##1##2##3##4{% + \advancenumber{sec\thissecnum}% + \def\thissubsecnum{##2}% + }% + \def\numsubsubsecentry##1##2##3##4{% + \advancenumber{subsec\thissubsecnum}% + }% + \def\thischapnum{0}% + \def\thissecnum{0}% + \def\thissubsecnum{0}% + % + % use \def rather than \let here because we redefine \chapentry et + % al. a second time, below. + \def\appentry{\numchapentry}% + \def\appsecentry{\numsecentry}% + \def\appsubsecentry{\numsubsecentry}% + \def\appsubsubsecentry{\numsubsubsecentry}% + \def\unnchapentry{\numchapentry}% + \def\unnsecentry{\numsecentry}% + \def\unnsubsecentry{\numsubsecentry}% + \def\unnsubsubsecentry{\numsubsubsecentry}% + \readdatafile{toc}% + % + % Read toc second time, this time actually producing the outlines. + % The `-' means take the \expnumber as the absolute number of + % subentries, which we calculated on our first read of the .toc above. + % + % We use the node names as the destinations. + \def\numchapentry##1##2##3##4{% + \dopdfoutline{##1}{count-\expnumber{chap##2}}{##3}{##4}}% + \def\numsecentry##1##2##3##4{% + \dopdfoutline{##1}{count-\expnumber{sec##2}}{##3}{##4}}% + \def\numsubsecentry##1##2##3##4{% + \dopdfoutline{##1}{count-\expnumber{subsec##2}}{##3}{##4}}% + \def\numsubsubsecentry##1##2##3##4{% count is always zero + \dopdfoutline{##1}{}{##3}{##4}}% + % + % PDF outlines are displayed using system fonts, instead of + % document fonts. Therefore we cannot use special characters, + % since the encoding is unknown. For example, the eogonek from + % Latin 2 (0xea) gets translated to a | character. Info from + % Staszek Wawrykiewicz, 19 Jan 2004 04:09:24 +0100. + % + % TODO this right, we have to translate 8-bit characters to + % their "best" equivalent, based on the @documentencoding. Too + % much work for too little return. Just use the ASCII equivalents + % we use for the index sort strings. + % + \indexnofonts + \setupdatafile + % We can have normal brace characters in the PDF outlines, unlike + % Texinfo index files. So set that up. + \def\{{\lbracecharliteral}% + \def\}{\rbracecharliteral}% + \catcode`\\=\active \otherbackslash + \input \tocreadfilename + \endgroup + } + {\catcode`[=1 \catcode`]=2 + \catcode`{=\other \catcode`}=\other + \gdef\lbracecharliteral[{]% + \gdef\rbracecharliteral[}]% + ] + % + \def\skipspaces#1{\def\PP{#1}\def\D{|}% + \ifx\PP\D\let\nextsp\relax + \else\let\nextsp\skipspaces + \addtokens{\filename}{\PP}% + \advance\filenamelength by 1 + \fi + \nextsp} + \def\getfilename#1{% + \filenamelength=0 + % If we don't expand the argument now, \skipspaces will get + % snagged on things like "@value{foo}". + \edef\temp{#1}% + \expandafter\skipspaces\temp|\relax + } + \ifnum\pdftexversion < 14 + \let \startlink \pdfannotlink + \else + \let \startlink \pdfstartlink + \fi + % make a live url in pdf output. + \def\pdfurl#1{% + \begingroup + % it seems we really need yet another set of dummies; have not + % tried to figure out what each command should do in the context + % of @url. for now, just make @/ a no-op, that's the only one + % people have actually reported a problem with. + % + \normalturnoffactive + \def\@{@}% + \let\/=\empty + \makevalueexpandable + % do we want to go so far as to use \indexnofonts instead of just + % special-casing \var here? + \def\var##1{##1}% + % + \leavevmode\setcolor{\urlcolor}% + \startlink attr{/Border [0 0 0]}% + user{/Subtype /Link /A << /S /URI /URI (#1) >>}% + \endgroup} + \def\pdfgettoks#1.{\setbox\boxA=\hbox{\toksA={#1.}\toksB={}\maketoks}} + \def\addtokens#1#2{\edef\addtoks{\noexpand#1={\the#1#2}}\addtoks} + \def\adn#1{\addtokens{\toksC}{#1}\global\countA=1\let\next=\maketoks} + \def\poptoks#1#2|ENDTOKS|{\let\first=#1\toksD={#1}\toksA={#2}} + \def\maketoks{% + \expandafter\poptoks\the\toksA|ENDTOKS|\relax + \ifx\first0\adn0 + \else\ifx\first1\adn1 \else\ifx\first2\adn2 \else\ifx\first3\adn3 + \else\ifx\first4\adn4 \else\ifx\first5\adn5 \else\ifx\first6\adn6 + \else\ifx\first7\adn7 \else\ifx\first8\adn8 \else\ifx\first9\adn9 + \else + \ifnum0=\countA\else\makelink\fi + \ifx\first.\let\next=\done\else + \let\next=\maketoks + \addtokens{\toksB}{\the\toksD} + \ifx\first,\addtokens{\toksB}{\space}\fi + \fi + \fi\fi\fi\fi\fi\fi\fi\fi\fi\fi + \next} + \def\makelink{\addtokens{\toksB}% + {\noexpand\pdflink{\the\toksC}}\toksC={}\global\countA=0} + \def\pdflink#1{% + \startlink attr{/Border [0 0 0]} goto name{\pdfmkpgn{#1}} + \setcolor{\linkcolor}#1\endlink} + \def\done{\edef\st{\global\noexpand\toksA={\the\toksB}}\st} +\else + % non-pdf mode + \let\pdfmkdest = \gobble + \let\pdfurl = \gobble + \let\endlink = \relax + \let\setcolor = \gobble + \let\pdfsetcolor = \gobble + \let\pdfmakeoutlines = \relax +\fi % \ifx\pdfoutput + +% +% @image support for XeTeX +% +\newif\ifxeteximgpdf +\ifx\XeTeXrevision\thisisundefined +\else + % + % #1 is image name, #2 width (might be empty/whitespace), #3 height (ditto). + \def\doxeteximage#1#2#3{% + \def\xeteximagewidth{#2}\setbox0 = \hbox{\ignorespaces #2}% + \def\xeteximageheight{#3}\setbox2 = \hbox{\ignorespaces #3}% + % + % XeTeX (and the PDF format) support .pdf, .png, .jpg (among + % others). Let's try in that order, PDF first since if + % someone has a scalable image, presumably better to use that than a + % bitmap. + \let\xeteximgext=\empty + \xeteximgpdffalse + \begingroup + \openin 1 #1.pdf \ifeof 1 + \openin 1 #1.PDF \ifeof 1 + \openin 1 #1.png \ifeof 1 + \openin 1 #1.jpg \ifeof 1 + \openin 1 #1.jpeg \ifeof 1 + \openin 1 #1.JPG \ifeof 1 + \errmessage{Could not find image file #1 for XeTeX}% + \else \gdef\xeteximgext{JPG}% + \fi + \else \gdef\xeteximgext{jpeg}% + \fi + \else \gdef\xeteximgext{jpg}% + \fi + \else \gdef\xeteximgext{png}% + \fi + \else \gdef\xeteximgext{PDF} \global\xeteximgpdftrue% + \fi + \else \gdef\xeteximgext{pdf} \global\xeteximgpdftrue% + \fi + \closein 1 + \endgroup + % + \ifxeteximgpdf + \XeTeXpdffile "#1".\xeteximgext "" + \else + \XeTeXpicfile "#1".\xeteximgext "" + \fi + \ifdim \wd0 >0pt width \xeteximagewidth \fi + \ifdim \wd2 >0pt height \xeteximageheight \fi \relax + } +\fi + +\message{fonts,} + +% Change the current font style to #1, remembering it in \curfontstyle. +% For now, we do not accumulate font styles: @b{@i{foo}} prints foo in +% italics, not bold italics. +% +\def\setfontstyle#1{% + \def\curfontstyle{#1}% not as a control sequence, because we are \edef'd. + \csname ten#1\endcsname % change the current font +} + +% Select #1 fonts with the current style. +% +\def\selectfonts#1{\csname #1fonts\endcsname \csname\curfontstyle\endcsname} + +\def\rm{\fam=0 \setfontstyle{rm}} +\def\it{\fam=\itfam \setfontstyle{it}} +\def\sl{\fam=\slfam \setfontstyle{sl}} +\def\bf{\fam=\bffam \setfontstyle{bf}}\def\bfstylename{bf} +\def\tt{\fam=\ttfam \setfontstyle{tt}} + +% Unfortunately, we have to override this for titles and the like, since +% in those cases "rm" is bold. Sigh. +\def\rmisbold{\rm\def\curfontstyle{bf}} + +% Texinfo sort of supports the sans serif font style, which plain TeX does not. +% So we set up a \sf. +\newfam\sffam +\def\sf{\fam=\sffam \setfontstyle{sf}} +\let\li = \sf % Sometimes we call it \li, not \sf. + +% We don't need math for this font style. +\def\ttsl{\setfontstyle{ttsl}} + + +% Set the baselineskip to #1, and the lineskip and strut size +% correspondingly. There is no deep meaning behind these magic numbers +% used as factors; they just match (closely enough) what Knuth defined. +% +\def\lineskipfactor{.08333} +\def\strutheightpercent{.70833} +\def\strutdepthpercent {.29167} +% +% can get a sort of poor man's double spacing by redefining this. +\def\baselinefactor{1} +% +\newdimen\textleading +\def\setleading#1{% + \dimen0 = #1\relax + \normalbaselineskip = \baselinefactor\dimen0 + \normallineskip = \lineskipfactor\normalbaselineskip + \normalbaselines + \setbox\strutbox =\hbox{% + \vrule width0pt height\strutheightpercent\baselineskip + depth \strutdepthpercent \baselineskip + }% +} + +% PDF CMaps. See also LaTeX's t1.cmap. +% +% do nothing with this by default. +\expandafter\let\csname cmapOT1\endcsname\gobble +\expandafter\let\csname cmapOT1IT\endcsname\gobble +\expandafter\let\csname cmapOT1TT\endcsname\gobble + +% if we are producing pdf, and we have \pdffontattr, then define cmaps. +% (\pdffontattr was introduced many years ago, but people still run +% older pdftex's; it's easy to conditionalize, so we do.) +\ifpdf \ifx\pdffontattr\thisisundefined \else + \begingroup + \catcode`\^^M=\active \def^^M{^^J}% Output line endings as the ^^J char. + \catcode`\%=12 \immediate\pdfobj stream {%!PS-Adobe-3.0 Resource-CMap +%%DocumentNeededResources: ProcSet (CIDInit) +%%IncludeResource: ProcSet (CIDInit) +%%BeginResource: CMap (TeX-OT1-0) +%%Title: (TeX-OT1-0 TeX OT1 0) +%%Version: 1.000 +%%EndComments +/CIDInit /ProcSet findresource begin +12 dict begin +begincmap +/CIDSystemInfo +<< /Registry (TeX) +/Ordering (OT1) +/Supplement 0 +>> def +/CMapName /TeX-OT1-0 def +/CMapType 2 def +1 begincodespacerange +<00> <7F> +endcodespacerange +8 beginbfrange +<00> <01> <0393> +<09> <0A> <03A8> +<23> <26> <0023> +<28> <3B> <0028> +<3F> <5B> <003F> +<5D> <5E> <005D> +<61> <7A> <0061> +<7B> <7C> <2013> +endbfrange +40 beginbfchar +<02> <0398> +<03> <039B> +<04> <039E> +<05> <03A0> +<06> <03A3> +<07> <03D2> +<08> <03A6> +<0B> <00660066> +<0C> <00660069> +<0D> <0066006C> +<0E> <006600660069> +<0F> <00660066006C> +<10> <0131> +<11> <0237> +<12> <0060> +<13> <00B4> +<14> <02C7> +<15> <02D8> +<16> <00AF> +<17> <02DA> +<18> <00B8> +<19> <00DF> +<1A> <00E6> +<1B> <0153> +<1C> <00F8> +<1D> <00C6> +<1E> <0152> +<1F> <00D8> +<21> <0021> +<22> <201D> +<27> <2019> +<3C> <00A1> +<3D> <003D> +<3E> <00BF> +<5C> <201C> +<5F> <02D9> +<60> <2018> +<7D> <02DD> +<7E> <007E> +<7F> <00A8> +endbfchar +endcmap +CMapName currentdict /CMap defineresource pop +end +end +%%EndResource +%%EOF + }\endgroup + \expandafter\edef\csname cmapOT1\endcsname#1{% + \pdffontattr#1{/ToUnicode \the\pdflastobj\space 0 R}% + }% +% +% \cmapOT1IT + \begingroup + \catcode`\^^M=\active \def^^M{^^J}% Output line endings as the ^^J char. + \catcode`\%=12 \immediate\pdfobj stream {%!PS-Adobe-3.0 Resource-CMap +%%DocumentNeededResources: ProcSet (CIDInit) +%%IncludeResource: ProcSet (CIDInit) +%%BeginResource: CMap (TeX-OT1IT-0) +%%Title: (TeX-OT1IT-0 TeX OT1IT 0) +%%Version: 1.000 +%%EndComments +/CIDInit /ProcSet findresource begin +12 dict begin +begincmap +/CIDSystemInfo +<< /Registry (TeX) +/Ordering (OT1IT) +/Supplement 0 +>> def +/CMapName /TeX-OT1IT-0 def +/CMapType 2 def +1 begincodespacerange +<00> <7F> +endcodespacerange +8 beginbfrange +<00> <01> <0393> +<09> <0A> <03A8> +<25> <26> <0025> +<28> <3B> <0028> +<3F> <5B> <003F> +<5D> <5E> <005D> +<61> <7A> <0061> +<7B> <7C> <2013> +endbfrange +42 beginbfchar +<02> <0398> +<03> <039B> +<04> <039E> +<05> <03A0> +<06> <03A3> +<07> <03D2> +<08> <03A6> +<0B> <00660066> +<0C> <00660069> +<0D> <0066006C> +<0E> <006600660069> +<0F> <00660066006C> +<10> <0131> +<11> <0237> +<12> <0060> +<13> <00B4> +<14> <02C7> +<15> <02D8> +<16> <00AF> +<17> <02DA> +<18> <00B8> +<19> <00DF> +<1A> <00E6> +<1B> <0153> +<1C> <00F8> +<1D> <00C6> +<1E> <0152> +<1F> <00D8> +<21> <0021> +<22> <201D> +<23> <0023> +<24> <00A3> +<27> <2019> +<3C> <00A1> +<3D> <003D> +<3E> <00BF> +<5C> <201C> +<5F> <02D9> +<60> <2018> +<7D> <02DD> +<7E> <007E> +<7F> <00A8> +endbfchar +endcmap +CMapName currentdict /CMap defineresource pop +end +end +%%EndResource +%%EOF + }\endgroup + \expandafter\edef\csname cmapOT1IT\endcsname#1{% + \pdffontattr#1{/ToUnicode \the\pdflastobj\space 0 R}% + }% +% +% \cmapOT1TT + \begingroup + \catcode`\^^M=\active \def^^M{^^J}% Output line endings as the ^^J char. + \catcode`\%=12 \immediate\pdfobj stream {%!PS-Adobe-3.0 Resource-CMap +%%DocumentNeededResources: ProcSet (CIDInit) +%%IncludeResource: ProcSet (CIDInit) +%%BeginResource: CMap (TeX-OT1TT-0) +%%Title: (TeX-OT1TT-0 TeX OT1TT 0) +%%Version: 1.000 +%%EndComments +/CIDInit /ProcSet findresource begin +12 dict begin +begincmap +/CIDSystemInfo +<< /Registry (TeX) +/Ordering (OT1TT) +/Supplement 0 +>> def +/CMapName /TeX-OT1TT-0 def +/CMapType 2 def +1 begincodespacerange +<00> <7F> +endcodespacerange +5 beginbfrange +<00> <01> <0393> +<09> <0A> <03A8> +<21> <26> <0021> +<28> <5F> <0028> +<61> <7E> <0061> +endbfrange +32 beginbfchar +<02> <0398> +<03> <039B> +<04> <039E> +<05> <03A0> +<06> <03A3> +<07> <03D2> +<08> <03A6> +<0B> <2191> +<0C> <2193> +<0D> <0027> +<0E> <00A1> +<0F> <00BF> +<10> <0131> +<11> <0237> +<12> <0060> +<13> <00B4> +<14> <02C7> +<15> <02D8> +<16> <00AF> +<17> <02DA> +<18> <00B8> +<19> <00DF> +<1A> <00E6> +<1B> <0153> +<1C> <00F8> +<1D> <00C6> +<1E> <0152> +<1F> <00D8> +<20> <2423> +<27> <2019> +<60> <2018> +<7F> <00A8> +endbfchar +endcmap +CMapName currentdict /CMap defineresource pop +end +end +%%EndResource +%%EOF + }\endgroup + \expandafter\edef\csname cmapOT1TT\endcsname#1{% + \pdffontattr#1{/ToUnicode \the\pdflastobj\space 0 R}% + }% +\fi\fi + + +% Set the font macro #1 to the font named \fontprefix#2. +% #3 is the font's design size, #4 is a scale factor, #5 is the CMap +% encoding (only OT1, OT1IT and OT1TT are allowed, or empty to omit). +% Example: +% #1 = \textrm +% #2 = \rmshape +% #3 = 10 +% #4 = \mainmagstep +% #5 = OT1 +% +\def\setfont#1#2#3#4#5{% + \font#1=\fontprefix#2#3 scaled #4 + \csname cmap#5\endcsname#1% +} +% This is what gets called when #5 of \setfont is empty. +\let\cmap\gobble +% +% (end of cmaps) + +% Use cm as the default font prefix. +% To specify the font prefix, you must define \fontprefix +% before you read in texinfo.tex. +\ifx\fontprefix\thisisundefined +\def\fontprefix{cm} +\fi +% Support font families that don't use the same naming scheme as CM. +\def\rmshape{r} +\def\rmbshape{bx} % where the normal face is bold +\def\bfshape{b} +\def\bxshape{bx} +\def\ttshape{tt} +\def\ttbshape{tt} +\def\ttslshape{sltt} +\def\itshape{ti} +\def\itbshape{bxti} +\def\slshape{sl} +\def\slbshape{bxsl} +\def\sfshape{ss} +\def\sfbshape{ss} +\def\scshape{csc} +\def\scbshape{csc} + +% Definitions for a main text size of 11pt. (The default in Texinfo.) +% +\def\definetextfontsizexi{% +% Text fonts (11.2pt, magstep1). +\def\textnominalsize{11pt} +\edef\mainmagstep{\magstephalf} +\setfont\textrm\rmshape{10}{\mainmagstep}{OT1} +\setfont\texttt\ttshape{10}{\mainmagstep}{OT1TT} +\setfont\textbf\bfshape{10}{\mainmagstep}{OT1} +\setfont\textit\itshape{10}{\mainmagstep}{OT1IT} +\setfont\textsl\slshape{10}{\mainmagstep}{OT1} +\setfont\textsf\sfshape{10}{\mainmagstep}{OT1} +\setfont\textsc\scshape{10}{\mainmagstep}{OT1} +\setfont\textttsl\ttslshape{10}{\mainmagstep}{OT1TT} +\font\texti=cmmi10 scaled \mainmagstep +\font\textsy=cmsy10 scaled \mainmagstep +\def\textecsize{1095} + +% A few fonts for @defun names and args. +\setfont\defbf\bfshape{10}{\magstep1}{OT1} +\setfont\deftt\ttshape{10}{\magstep1}{OT1TT} +\setfont\defsl\slshape{10}{\magstep1}{OT1TT} +\setfont\defttsl\ttslshape{10}{\magstep1}{OT1TT} +\def\df{\let\tentt=\deftt \let\tenbf = \defbf +\let\tenttsl=\defttsl \let\tensl=\defsl \bf} + +% Fonts for indices, footnotes, small examples (9pt). +\def\smallnominalsize{9pt} +\setfont\smallrm\rmshape{9}{1000}{OT1} +\setfont\smalltt\ttshape{9}{1000}{OT1TT} +\setfont\smallbf\bfshape{10}{900}{OT1} +\setfont\smallit\itshape{9}{1000}{OT1IT} +\setfont\smallsl\slshape{9}{1000}{OT1} +\setfont\smallsf\sfshape{9}{1000}{OT1} +\setfont\smallsc\scshape{10}{900}{OT1} +\setfont\smallttsl\ttslshape{10}{900}{OT1TT} +\font\smalli=cmmi9 +\font\smallsy=cmsy9 +\def\smallecsize{0900} + +% Fonts for small examples (8pt). +\def\smallernominalsize{8pt} +\setfont\smallerrm\rmshape{8}{1000}{OT1} +\setfont\smallertt\ttshape{8}{1000}{OT1TT} +\setfont\smallerbf\bfshape{10}{800}{OT1} +\setfont\smallerit\itshape{8}{1000}{OT1IT} +\setfont\smallersl\slshape{8}{1000}{OT1} +\setfont\smallersf\sfshape{8}{1000}{OT1} +\setfont\smallersc\scshape{10}{800}{OT1} +\setfont\smallerttsl\ttslshape{10}{800}{OT1TT} +\font\smalleri=cmmi8 +\font\smallersy=cmsy8 +\def\smallerecsize{0800} + +% Fonts for title page (20.4pt): +\def\titlenominalsize{20pt} +\setfont\titlerm\rmbshape{12}{\magstep3}{OT1} +\setfont\titleit\itbshape{10}{\magstep4}{OT1IT} +\setfont\titlesl\slbshape{10}{\magstep4}{OT1} +\setfont\titlett\ttbshape{12}{\magstep3}{OT1TT} +\setfont\titlettsl\ttslshape{10}{\magstep4}{OT1TT} +\setfont\titlesf\sfbshape{17}{\magstep1}{OT1} +\let\titlebf=\titlerm +\setfont\titlesc\scbshape{10}{\magstep4}{OT1} +\font\titlei=cmmi12 scaled \magstep3 +\font\titlesy=cmsy10 scaled \magstep4 +\def\titleecsize{2074} + +% Chapter (and unnumbered) fonts (17.28pt). +\def\chapnominalsize{17pt} +\setfont\chaprm\rmbshape{12}{\magstep2}{OT1} +\setfont\chapit\itbshape{10}{\magstep3}{OT1IT} +\setfont\chapsl\slbshape{10}{\magstep3}{OT1} +\setfont\chaptt\ttbshape{12}{\magstep2}{OT1TT} +\setfont\chapttsl\ttslshape{10}{\magstep3}{OT1TT} +\setfont\chapsf\sfbshape{17}{1000}{OT1} +\let\chapbf=\chaprm +\setfont\chapsc\scbshape{10}{\magstep3}{OT1} +\font\chapi=cmmi12 scaled \magstep2 +\font\chapsy=cmsy10 scaled \magstep3 +\def\chapecsize{1728} + +% Section fonts (14.4pt). +\def\secnominalsize{14pt} +\setfont\secrm\rmbshape{12}{\magstep1}{OT1} +\setfont\secrmnotbold\rmshape{12}{\magstep1}{OT1} +\setfont\secit\itbshape{10}{\magstep2}{OT1IT} +\setfont\secsl\slbshape{10}{\magstep2}{OT1} +\setfont\sectt\ttbshape{12}{\magstep1}{OT1TT} +\setfont\secttsl\ttslshape{10}{\magstep2}{OT1TT} +\setfont\secsf\sfbshape{12}{\magstep1}{OT1} +\let\secbf\secrm +\setfont\secsc\scbshape{10}{\magstep2}{OT1} +\font\seci=cmmi12 scaled \magstep1 +\font\secsy=cmsy10 scaled \magstep2 +\def\sececsize{1440} + +% Subsection fonts (13.15pt). +\def\ssecnominalsize{13pt} +\setfont\ssecrm\rmbshape{12}{\magstephalf}{OT1} +\setfont\ssecit\itbshape{10}{1315}{OT1IT} +\setfont\ssecsl\slbshape{10}{1315}{OT1} +\setfont\ssectt\ttbshape{12}{\magstephalf}{OT1TT} +\setfont\ssecttsl\ttslshape{10}{1315}{OT1TT} +\setfont\ssecsf\sfbshape{12}{\magstephalf}{OT1} +\let\ssecbf\ssecrm +\setfont\ssecsc\scbshape{10}{1315}{OT1} +\font\sseci=cmmi12 scaled \magstephalf +\font\ssecsy=cmsy10 scaled 1315 +\def\ssececsize{1200} + +% Reduced fonts for @acro in text (10pt). +\def\reducednominalsize{10pt} +\setfont\reducedrm\rmshape{10}{1000}{OT1} +\setfont\reducedtt\ttshape{10}{1000}{OT1TT} +\setfont\reducedbf\bfshape{10}{1000}{OT1} +\setfont\reducedit\itshape{10}{1000}{OT1IT} +\setfont\reducedsl\slshape{10}{1000}{OT1} +\setfont\reducedsf\sfshape{10}{1000}{OT1} +\setfont\reducedsc\scshape{10}{1000}{OT1} +\setfont\reducedttsl\ttslshape{10}{1000}{OT1TT} +\font\reducedi=cmmi10 +\font\reducedsy=cmsy10 +\def\reducedecsize{1000} + +\textleading = 13.2pt % line spacing for 11pt CM +\textfonts % reset the current fonts +\rm +} % end of 11pt text font size definitions, \definetextfontsizexi + + +% Definitions to make the main text be 10pt Computer Modern, with +% section, chapter, etc., sizes following suit. This is for the GNU +% Press printing of the Emacs 22 manual. Maybe other manuals in the +% future. Used with @smallbook, which sets the leading to 12pt. +% +\def\definetextfontsizex{% +% Text fonts (10pt). +\def\textnominalsize{10pt} +\edef\mainmagstep{1000} +\setfont\textrm\rmshape{10}{\mainmagstep}{OT1} +\setfont\texttt\ttshape{10}{\mainmagstep}{OT1TT} +\setfont\textbf\bfshape{10}{\mainmagstep}{OT1} +\setfont\textit\itshape{10}{\mainmagstep}{OT1IT} +\setfont\textsl\slshape{10}{\mainmagstep}{OT1} +\setfont\textsf\sfshape{10}{\mainmagstep}{OT1} +\setfont\textsc\scshape{10}{\mainmagstep}{OT1} +\setfont\textttsl\ttslshape{10}{\mainmagstep}{OT1TT} +\font\texti=cmmi10 scaled \mainmagstep +\font\textsy=cmsy10 scaled \mainmagstep +\def\textecsize{1000} + +% A few fonts for @defun names and args. +\setfont\defbf\bfshape{10}{\magstephalf}{OT1} +\setfont\deftt\ttshape{10}{\magstephalf}{OT1TT} +\setfont\defsl\slshape{10}{\magstephalf}{OT1TT} +\setfont\defttsl\ttslshape{10}{\magstephalf}{OT1TT} +\def\df{\let\tentt=\deftt \let\tenbf = \defbf +\let\tensl=\defsl \let\tenttsl=\defttsl \bf} + +% Fonts for indices, footnotes, small examples (9pt). +\def\smallnominalsize{9pt} +\setfont\smallrm\rmshape{9}{1000}{OT1} +\setfont\smalltt\ttshape{9}{1000}{OT1TT} +\setfont\smallbf\bfshape{10}{900}{OT1} +\setfont\smallit\itshape{9}{1000}{OT1IT} +\setfont\smallsl\slshape{9}{1000}{OT1} +\setfont\smallsf\sfshape{9}{1000}{OT1} +\setfont\smallsc\scshape{10}{900}{OT1} +\setfont\smallttsl\ttslshape{10}{900}{OT1TT} +\font\smalli=cmmi9 +\font\smallsy=cmsy9 +\def\smallecsize{0900} + +% Fonts for small examples (8pt). +\def\smallernominalsize{8pt} +\setfont\smallerrm\rmshape{8}{1000}{OT1} +\setfont\smallertt\ttshape{8}{1000}{OT1TT} +\setfont\smallerbf\bfshape{10}{800}{OT1} +\setfont\smallerit\itshape{8}{1000}{OT1IT} +\setfont\smallersl\slshape{8}{1000}{OT1} +\setfont\smallersf\sfshape{8}{1000}{OT1} +\setfont\smallersc\scshape{10}{800}{OT1} +\setfont\smallerttsl\ttslshape{10}{800}{OT1TT} +\font\smalleri=cmmi8 +\font\smallersy=cmsy8 +\def\smallerecsize{0800} + +% Fonts for title page (20.4pt): +\def\titlenominalsize{20pt} +\setfont\titlerm\rmbshape{12}{\magstep3}{OT1} +\setfont\titleit\itbshape{10}{\magstep4}{OT1IT} +\setfont\titlesl\slbshape{10}{\magstep4}{OT1} +\setfont\titlett\ttbshape{12}{\magstep3}{OT1TT} +\setfont\titlettsl\ttslshape{10}{\magstep4}{OT1TT} +\setfont\titlesf\sfbshape{17}{\magstep1}{OT1} +\let\titlebf=\titlerm +\setfont\titlesc\scbshape{10}{\magstep4}{OT1} +\font\titlei=cmmi12 scaled \magstep3 +\font\titlesy=cmsy10 scaled \magstep4 +\def\titleecsize{2074} + +% Chapter fonts (14.4pt). +\def\chapnominalsize{14pt} +\setfont\chaprm\rmbshape{12}{\magstep1}{OT1} +\setfont\chapit\itbshape{10}{\magstep2}{OT1IT} +\setfont\chapsl\slbshape{10}{\magstep2}{OT1} +\setfont\chaptt\ttbshape{12}{\magstep1}{OT1TT} +\setfont\chapttsl\ttslshape{10}{\magstep2}{OT1TT} +\setfont\chapsf\sfbshape{12}{\magstep1}{OT1} +\let\chapbf\chaprm +\setfont\chapsc\scbshape{10}{\magstep2}{OT1} +\font\chapi=cmmi12 scaled \magstep1 +\font\chapsy=cmsy10 scaled \magstep2 +\def\chapecsize{1440} + +% Section fonts (12pt). +\def\secnominalsize{12pt} +\setfont\secrm\rmbshape{12}{1000}{OT1} +\setfont\secit\itbshape{10}{\magstep1}{OT1IT} +\setfont\secsl\slbshape{10}{\magstep1}{OT1} +\setfont\sectt\ttbshape{12}{1000}{OT1TT} +\setfont\secttsl\ttslshape{10}{\magstep1}{OT1TT} +\setfont\secsf\sfbshape{12}{1000}{OT1} +\let\secbf\secrm +\setfont\secsc\scbshape{10}{\magstep1}{OT1} +\font\seci=cmmi12 +\font\secsy=cmsy10 scaled \magstep1 +\def\sececsize{1200} + +% Subsection fonts (10pt). +\def\ssecnominalsize{10pt} +\setfont\ssecrm\rmbshape{10}{1000}{OT1} +\setfont\ssecit\itbshape{10}{1000}{OT1IT} +\setfont\ssecsl\slbshape{10}{1000}{OT1} +\setfont\ssectt\ttbshape{10}{1000}{OT1TT} +\setfont\ssecttsl\ttslshape{10}{1000}{OT1TT} +\setfont\ssecsf\sfbshape{10}{1000}{OT1} +\let\ssecbf\ssecrm +\setfont\ssecsc\scbshape{10}{1000}{OT1} +\font\sseci=cmmi10 +\font\ssecsy=cmsy10 +\def\ssececsize{1000} + +% Reduced fonts for @acro in text (9pt). +\def\reducednominalsize{9pt} +\setfont\reducedrm\rmshape{9}{1000}{OT1} +\setfont\reducedtt\ttshape{9}{1000}{OT1TT} +\setfont\reducedbf\bfshape{10}{900}{OT1} +\setfont\reducedit\itshape{9}{1000}{OT1IT} +\setfont\reducedsl\slshape{9}{1000}{OT1} +\setfont\reducedsf\sfshape{9}{1000}{OT1} +\setfont\reducedsc\scshape{10}{900}{OT1} +\setfont\reducedttsl\ttslshape{10}{900}{OT1TT} +\font\reducedi=cmmi9 +\font\reducedsy=cmsy9 +\def\reducedecsize{0900} + +\divide\parskip by 2 % reduce space between paragraphs +\textleading = 12pt % line spacing for 10pt CM +\textfonts % reset the current fonts +\rm +} % end of 10pt text font size definitions, \definetextfontsizex + + +% We provide the user-level command +% @fonttextsize 10 +% (or 11) to redefine the text font size. pt is assumed. +% +\def\xiword{11} +\def\xword{10} +\def\xwordpt{10pt} +% +\parseargdef\fonttextsize{% + \def\textsizearg{#1}% + %\wlog{doing @fonttextsize \textsizearg}% + % + % Set \globaldefs so that documents can use this inside @tex, since + % makeinfo 4.8 does not support it, but we need it nonetheless. + % + \begingroup \globaldefs=1 + \ifx\textsizearg\xword \definetextfontsizex + \else \ifx\textsizearg\xiword \definetextfontsizexi + \else + \errhelp=\EMsimple + \errmessage{@fonttextsize only supports `10' or `11', not `\textsizearg'} + \fi\fi + \endgroup +} + +% In order for the font changes to affect most math symbols and letters, +% we have to define the \textfont of the standard families. We don't +% bother to reset \scriptfont and \scriptscriptfont; awaiting user need. +% +\def\resetmathfonts{% + \textfont0=\tenrm \textfont1=\teni \textfont2=\tensy + \textfont\itfam=\tenit \textfont\slfam=\tensl \textfont\bffam=\tenbf + \textfont\ttfam=\tentt \textfont\sffam=\tensf +} + +% The font-changing commands redefine the meanings of \tenSTYLE, instead +% of just \STYLE. We do this because \STYLE needs to also set the +% current \fam for math mode. Our \STYLE (e.g., \rm) commands hardwire +% \tenSTYLE to set the current font. +% +% Each font-changing command also sets the names \lsize (one size lower) +% and \lllsize (three sizes lower). These relative commands are used +% in, e.g., the LaTeX logo and acronyms. +% +% This all needs generalizing, badly. +% +\def\textfonts{% + \let\tenrm=\textrm \let\tenit=\textit \let\tensl=\textsl + \let\tenbf=\textbf \let\tentt=\texttt \let\smallcaps=\textsc + \let\tensf=\textsf \let\teni=\texti \let\tensy=\textsy + \let\tenttsl=\textttsl + \def\curfontsize{text}% + \def\lsize{reduced}\def\lllsize{smaller}% + \resetmathfonts \setleading{\textleading}} +\def\titlefonts{% + \let\tenrm=\titlerm \let\tenit=\titleit \let\tensl=\titlesl + \let\tenbf=\titlebf \let\tentt=\titlett \let\smallcaps=\titlesc + \let\tensf=\titlesf \let\teni=\titlei \let\tensy=\titlesy + \let\tenttsl=\titlettsl + \def\curfontsize{title}% + \def\lsize{chap}\def\lllsize{subsec}% + \resetmathfonts \setleading{27pt}} +\def\titlefont#1{{\titlefonts\rmisbold #1}} +\def\chapfonts{% + \let\tenrm=\chaprm \let\tenit=\chapit \let\tensl=\chapsl + \let\tenbf=\chapbf \let\tentt=\chaptt \let\smallcaps=\chapsc + \let\tensf=\chapsf \let\teni=\chapi \let\tensy=\chapsy + \let\tenttsl=\chapttsl + \def\curfontsize{chap}% + \def\lsize{sec}\def\lllsize{text}% + \resetmathfonts \setleading{19pt}} +\def\secfonts{% + \let\tenrm=\secrm \let\tenit=\secit \let\tensl=\secsl + \let\tenbf=\secbf \let\tentt=\sectt \let\smallcaps=\secsc + \let\tensf=\secsf \let\teni=\seci \let\tensy=\secsy + \let\tenttsl=\secttsl + \def\curfontsize{sec}% + \def\lsize{subsec}\def\lllsize{reduced}% + \resetmathfonts \setleading{17pt}} +\def\subsecfonts{% + \let\tenrm=\ssecrm \let\tenit=\ssecit \let\tensl=\ssecsl + \let\tenbf=\ssecbf \let\tentt=\ssectt \let\smallcaps=\ssecsc + \let\tensf=\ssecsf \let\teni=\sseci \let\tensy=\ssecsy + \let\tenttsl=\ssecttsl + \def\curfontsize{ssec}% + \def\lsize{text}\def\lllsize{small}% + \resetmathfonts \setleading{15pt}} +\let\subsubsecfonts = \subsecfonts +\def\reducedfonts{% + \let\tenrm=\reducedrm \let\tenit=\reducedit \let\tensl=\reducedsl + \let\tenbf=\reducedbf \let\tentt=\reducedtt \let\reducedcaps=\reducedsc + \let\tensf=\reducedsf \let\teni=\reducedi \let\tensy=\reducedsy + \let\tenttsl=\reducedttsl + \def\curfontsize{reduced}% + \def\lsize{small}\def\lllsize{smaller}% + \resetmathfonts \setleading{10.5pt}} +\def\smallfonts{% + \let\tenrm=\smallrm \let\tenit=\smallit \let\tensl=\smallsl + \let\tenbf=\smallbf \let\tentt=\smalltt \let\smallcaps=\smallsc + \let\tensf=\smallsf \let\teni=\smalli \let\tensy=\smallsy + \let\tenttsl=\smallttsl + \def\curfontsize{small}% + \def\lsize{smaller}\def\lllsize{smaller}% + \resetmathfonts \setleading{10.5pt}} +\def\smallerfonts{% + \let\tenrm=\smallerrm \let\tenit=\smallerit \let\tensl=\smallersl + \let\tenbf=\smallerbf \let\tentt=\smallertt \let\smallcaps=\smallersc + \let\tensf=\smallersf \let\teni=\smalleri \let\tensy=\smallersy + \let\tenttsl=\smallerttsl + \def\curfontsize{smaller}% + \def\lsize{smaller}\def\lllsize{smaller}% + \resetmathfonts \setleading{9.5pt}} + +% Fonts for short table of contents. +\setfont\shortcontrm\rmshape{12}{1000}{OT1} +\setfont\shortcontbf\bfshape{10}{\magstep1}{OT1} % no cmb12 +\setfont\shortcontsl\slshape{12}{1000}{OT1} +\setfont\shortconttt\ttshape{12}{1000}{OT1TT} + +% Define these just so they can be easily changed for other fonts. +\def\angleleft{$\langle$} +\def\angleright{$\rangle$} + +% Set the fonts to use with the @small... environments. +\let\smallexamplefonts = \smallfonts + +% About \smallexamplefonts. If we use \smallfonts (9pt), @smallexample +% can fit this many characters: +% 8.5x11=86 smallbook=72 a4=90 a5=69 +% If we use \scriptfonts (8pt), then we can fit this many characters: +% 8.5x11=90+ smallbook=80 a4=90+ a5=77 +% For me, subjectively, the few extra characters that fit aren't worth +% the additional smallness of 8pt. So I'm making the default 9pt. +% +% By the way, for comparison, here's what fits with @example (10pt): +% 8.5x11=71 smallbook=60 a4=75 a5=58 +% --karl, 24jan03. + +% Set up the default fonts, so we can use them for creating boxes. +% +\definetextfontsizexi + + +\message{markup,} + +% Check if we are currently using a typewriter font. Since all the +% Computer Modern typewriter fonts have zero interword stretch (and +% shrink), and it is reasonable to expect all typewriter fonts to have +% this property, we can check that font parameter. +% +\def\ifmonospace{\ifdim\fontdimen3\font=0pt } + +% Markup style infrastructure. \defmarkupstylesetup\INITMACRO will +% define and register \INITMACRO to be called on markup style changes. +% \INITMACRO can check \currentmarkupstyle for the innermost +% style and the set of \ifmarkupSTYLE switches for all styles +% currently in effect. +\newif\ifmarkupvar +\newif\ifmarkupsamp +\newif\ifmarkupkey +%\newif\ifmarkupfile % @file == @samp. +%\newif\ifmarkupoption % @option == @samp. +\newif\ifmarkupcode +\newif\ifmarkupkbd +%\newif\ifmarkupenv % @env == @code. +%\newif\ifmarkupcommand % @command == @code. +\newif\ifmarkuptex % @tex (and part of @math, for now). +\newif\ifmarkupexample +\newif\ifmarkupverb +\newif\ifmarkupverbatim + +\let\currentmarkupstyle\empty + +\def\setupmarkupstyle#1{% + \csname markup#1true\endcsname + \def\currentmarkupstyle{#1}% + \markupstylesetup +} + +\let\markupstylesetup\empty + +\def\defmarkupstylesetup#1{% + \expandafter\def\expandafter\markupstylesetup + \expandafter{\markupstylesetup #1}% + \def#1% +} + +% Markup style setup for left and right quotes. +\defmarkupstylesetup\markupsetuplq{% + \expandafter\let\expandafter \temp + \csname markupsetuplq\currentmarkupstyle\endcsname + \ifx\temp\relax \markupsetuplqdefault \else \temp \fi +} + +\defmarkupstylesetup\markupsetuprq{% + \expandafter\let\expandafter \temp + \csname markupsetuprq\currentmarkupstyle\endcsname + \ifx\temp\relax \markupsetuprqdefault \else \temp \fi +} + +{ +\catcode`\'=\active +\catcode`\`=\active + +\gdef\markupsetuplqdefault{\let`\lq} +\gdef\markupsetuprqdefault{\let'\rq} + +\gdef\markupsetcodequoteleft{\let`\codequoteleft} +\gdef\markupsetcodequoteright{\let'\codequoteright} +} + +\let\markupsetuplqcode \markupsetcodequoteleft +\let\markupsetuprqcode \markupsetcodequoteright +% +\let\markupsetuplqexample \markupsetcodequoteleft +\let\markupsetuprqexample \markupsetcodequoteright +% +\let\markupsetuplqkbd \markupsetcodequoteleft +\let\markupsetuprqkbd \markupsetcodequoteright +% +\let\markupsetuplqsamp \markupsetcodequoteleft +\let\markupsetuprqsamp \markupsetcodequoteright +% +\let\markupsetuplqverb \markupsetcodequoteleft +\let\markupsetuprqverb \markupsetcodequoteright +% +\let\markupsetuplqverbatim \markupsetcodequoteleft +\let\markupsetuprqverbatim \markupsetcodequoteright + +% Allow an option to not use regular directed right quote/apostrophe +% (char 0x27), but instead the undirected quote from cmtt (char 0x0d). +% The undirected quote is ugly, so don't make it the default, but it +% works for pasting with more pdf viewers (at least evince), the +% lilypond developers report. xpdf does work with the regular 0x27. +% +\def\codequoteright{% + \expandafter\ifx\csname SETtxicodequoteundirected\endcsname\relax + \expandafter\ifx\csname SETcodequoteundirected\endcsname\relax + '% + \else \char'15 \fi + \else \char'15 \fi +} +% +% and a similar option for the left quote char vs. a grave accent. +% Modern fonts display ASCII 0x60 as a grave accent, so some people like +% the code environments to do likewise. +% +\def\codequoteleft{% + \expandafter\ifx\csname SETtxicodequotebacktick\endcsname\relax + \expandafter\ifx\csname SETcodequotebacktick\endcsname\relax + % [Knuth] pp. 380,381,391 + % \relax disables Spanish ligatures ?` and !` of \tt font. + \relax`% + \else \char'22 \fi + \else \char'22 \fi +} + +% Commands to set the quote options. +% +\parseargdef\codequoteundirected{% + \def\temp{#1}% + \ifx\temp\onword + \expandafter\let\csname SETtxicodequoteundirected\endcsname + = t% + \else\ifx\temp\offword + \expandafter\let\csname SETtxicodequoteundirected\endcsname + = \relax + \else + \errhelp = \EMsimple + \errmessage{Unknown @codequoteundirected value `\temp', must be on|off}% + \fi\fi +} +% +\parseargdef\codequotebacktick{% + \def\temp{#1}% + \ifx\temp\onword + \expandafter\let\csname SETtxicodequotebacktick\endcsname + = t% + \else\ifx\temp\offword + \expandafter\let\csname SETtxicodequotebacktick\endcsname + = \relax + \else + \errhelp = \EMsimple + \errmessage{Unknown @codequotebacktick value `\temp', must be on|off}% + \fi\fi +} + +% [Knuth] pp. 380,381,391, disable Spanish ligatures ?` and !` of \tt font. +\def\noligaturesquoteleft{\relax\lq} + +% Count depth in font-changes, for error checks +\newcount\fontdepth \fontdepth=0 + +% Font commands. + +% #1 is the font command (\sl or \it), #2 is the text to slant. +% If we are in a monospaced environment, however, 1) always use \ttsl, +% and 2) do not add an italic correction. +\def\dosmartslant#1#2{% + \ifusingtt + {{\ttsl #2}\let\next=\relax}% + {\def\next{{#1#2}\futurelet\next\smartitaliccorrection}}% + \next +} +\def\smartslanted{\dosmartslant\sl} +\def\smartitalic{\dosmartslant\it} + +% Output an italic correction unless \next (presumed to be the following +% character) is such as not to need one. +\def\smartitaliccorrection{% + \ifx\next,% + \else\ifx\next-% + \else\ifx\next.% + \else\ifx\next\.% + \else\ifx\next\comma% + \else\ptexslash + \fi\fi\fi\fi\fi + \aftersmartic +} + +% Unconditional use \ttsl, and no ic. @var is set to this for defuns. +\def\ttslanted#1{{\ttsl #1}} + +% @cite is like \smartslanted except unconditionally use \sl. We never want +% ttsl for book titles, do we? +\def\cite#1{{\sl #1}\futurelet\next\smartitaliccorrection} + +\def\aftersmartic{} +\def\var#1{% + \let\saveaftersmartic = \aftersmartic + \def\aftersmartic{\null\let\aftersmartic=\saveaftersmartic}% + \smartslanted{#1}% +} + +\let\i=\smartitalic +\let\slanted=\smartslanted +\let\dfn=\smartslanted +\let\emph=\smartitalic + +% Explicit font changes: @r, @sc, undocumented @ii. +\def\r#1{{\rm #1}} % roman font +\def\sc#1{{\smallcaps#1}} % smallcaps font +\def\ii#1{{\it #1}} % italic font + +% @b, explicit bold. Also @strong. +\def\b#1{{\bf #1}} +\let\strong=\b + +% @sansserif, explicit sans. +\def\sansserif#1{{\sf #1}} + +% We can't just use \exhyphenpenalty, because that only has effect at +% the end of a paragraph. Restore normal hyphenation at the end of the +% group within which \nohyphenation is presumably called. +% +\def\nohyphenation{\hyphenchar\font = -1 \aftergroup\restorehyphenation} +\def\restorehyphenation{\hyphenchar\font = `- } + +% Set sfcode to normal for the chars that usually have another value. +% Can't use plain's \frenchspacing because it uses the `\x notation, and +% sometimes \x has an active definition that messes things up. +% +\catcode`@=11 + \def\plainfrenchspacing{% + \sfcode`\.=\@m \sfcode`\?=\@m \sfcode`\!=\@m + \sfcode`\:=\@m \sfcode`\;=\@m \sfcode`\,=\@m + \def\endofsentencespacefactor{1000}% for @. and friends + } + \def\plainnonfrenchspacing{% + \sfcode`\.3000\sfcode`\?3000\sfcode`\!3000 + \sfcode`\:2000\sfcode`\;1500\sfcode`\,1250 + \def\endofsentencespacefactor{3000}% for @. and friends + } +\catcode`@=\other +\def\endofsentencespacefactor{3000}% default + +% @t, explicit typewriter. +\def\t#1{% + {\tt \rawbackslash \plainfrenchspacing #1}% + \null +} + +% @samp. +\def\samp#1{{\setupmarkupstyle{samp}\lq\tclose{#1}\rq\null}} + +% @indicateurl is \samp, that is, with quotes. +\let\indicateurl=\samp + +% @code (and similar) prints in typewriter, but with spaces the same +% size as normal in the surrounding text, without hyphenation, etc. +% This is a subroutine for that. +\def\tclose#1{% + {% + % Change normal interword space to be same as for the current font. + \spaceskip = \fontdimen2\font + % + % Switch to typewriter. + \tt + % + % But `\ ' produces the large typewriter interword space. + \def\ {{\spaceskip = 0pt{} }}% + % + % Turn off hyphenation. + \nohyphenation + % + \rawbackslash + \plainfrenchspacing + #1% + }% + \null % reset spacefactor to 1000 +} + +% We *must* turn on hyphenation at `-' and `_' in @code. +% (But see \codedashfinish below.) +% Otherwise, it is too hard to avoid overfull hboxes +% in the Emacs manual, the Library manual, etc. +% +% Unfortunately, TeX uses one parameter (\hyphenchar) to control +% both hyphenation at - and hyphenation within words. +% We must therefore turn them both off (\tclose does that) +% and arrange explicitly to hyphenate at a dash. -- rms. +{ + \catcode`\-=\active \catcode`\_=\active + \catcode`\'=\active \catcode`\`=\active + \global\let'=\rq \global\let`=\lq % default definitions + % + \global\def\code{\begingroup + \setupmarkupstyle{code}% + % The following should really be moved into \setupmarkupstyle handlers. + \catcode\dashChar=\active \catcode\underChar=\active + \ifallowcodebreaks + \let-\codedash + \let_\codeunder + \else + \let-\normaldash + \let_\realunder + \fi + % Given -foo (with a single dash), we do not want to allow a break + % after the hyphen. + \global\let\codedashprev=\codedash + % + \codex + } + % + \gdef\codedash{\futurelet\next\codedashfinish} + \gdef\codedashfinish{% + \normaldash % always output the dash character itself. + % + % Now, output a discretionary to allow a line break, unless + % (a) the next character is a -, or + % (b) the preceding character is a -. + % E.g., given --posix, we do not want to allow a break after either -. + % Given --foo-bar, we do want to allow a break between the - and the b. + \ifx\next\codedash \else + \ifx\codedashprev\codedash + \else \discretionary{}{}{}\fi + \fi + % we need the space after the = for the case when \next itself is a + % space token; it would get swallowed otherwise. As in @code{- a}. + \global\let\codedashprev= \next + } +} +\def\normaldash{-} +% +\def\codex #1{\tclose{#1}\endgroup} + +\def\codeunder{% + % this is all so @math{@code{var_name}+1} can work. In math mode, _ + % is "active" (mathcode"8000) and \normalunderscore (or \char95, etc.) + % will therefore expand the active definition of _, which is us + % (inside @code that is), therefore an endless loop. + \ifusingtt{\ifmmode + \mathchar"075F % class 0=ordinary, family 7=ttfam, pos 0x5F=_. + \else\normalunderscore \fi + \discretionary{}{}{}}% + {\_}% +} + +% An additional complication: the above will allow breaks after, e.g., +% each of the four underscores in __typeof__. This is bad. +% @allowcodebreaks provides a document-level way to turn breaking at - +% and _ on and off. +% +\newif\ifallowcodebreaks \allowcodebreakstrue + +\def\keywordtrue{true} +\def\keywordfalse{false} + +\parseargdef\allowcodebreaks{% + \def\txiarg{#1}% + \ifx\txiarg\keywordtrue + \allowcodebreakstrue + \else\ifx\txiarg\keywordfalse + \allowcodebreaksfalse + \else + \errhelp = \EMsimple + \errmessage{Unknown @allowcodebreaks option `\txiarg', must be true|false}% + \fi\fi +} + +% For @command, @env, @file, @option quotes seem unnecessary, +% so use \code rather than \samp. +\let\command=\code +\let\env=\code +\let\file=\code +\let\option=\code + +% @uref (abbreviation for `urlref') aka @url takes an optional +% (comma-separated) second argument specifying the text to display and +% an optional third arg as text to display instead of (rather than in +% addition to) the url itself. First (mandatory) arg is the url. + +% TeX-only option to allow changing PDF output to show only the second +% arg (if given), and not the url (which is then just the link target). +\newif\ifurefurlonlylink + +% The main macro is \urefbreak, which allows breaking at expected +% places within the url. (There used to be another version, which +% didn't support automatic breaking.) +\def\urefbreak{\begingroup \urefcatcodes \dourefbreak} +\let\uref=\urefbreak +% +\def\dourefbreak#1{\urefbreakfinish #1,,,\finish} +\def\urefbreakfinish#1,#2,#3,#4\finish{% doesn't work in @example + \unsepspaces + \pdfurl{#1}% + \setbox0 = \hbox{\ignorespaces #3}% + \ifdim\wd0 > 0pt + \unhbox0 % third arg given, show only that + \else + \setbox0 = \hbox{\ignorespaces #2}% look for second arg + \ifdim\wd0 > 0pt + \ifpdf + \ifurefurlonlylink + % PDF plus option to not display url, show just arg + \unhbox0 + \else + % PDF, normally display both arg and url for consistency, + % visibility, if the pdf is eventually used to print, etc. + \unhbox0\ (\urefcode{#1})% + \fi + \else + \unhbox0\ (\urefcode{#1})% DVI, always show arg and url + \fi + \else + \urefcode{#1}% only url given, so show it + \fi + \fi + \endlink +\endgroup} + +% Allow line breaks around only a few characters (only). +\def\urefcatcodes{% + \catcode`\&=\active \catcode`\.=\active + \catcode`\#=\active \catcode`\?=\active + \catcode`\/=\active +} +{ + \urefcatcodes + % + \global\def\urefcode{\begingroup + \setupmarkupstyle{code}% + \urefcatcodes + \let&\urefcodeamp + \let.\urefcodedot + \let#\urefcodehash + \let?\urefcodequest + \let/\urefcodeslash + \codex + } + % + % By default, they are just regular characters. + \global\def&{\normalamp} + \global\def.{\normaldot} + \global\def#{\normalhash} + \global\def?{\normalquest} + \global\def/{\normalslash} +} + +% we put a little stretch before and after the breakable chars, to help +% line breaking of long url's. The unequal skips make look better in +% cmtt at least, especially for dots. +\def\urefprestretchamount{.13em} +\def\urefpoststretchamount{.1em} +\def\urefprestretch{\urefprebreak \hskip0pt plus\urefprestretchamount\relax} +\def\urefpoststretch{\urefpostbreak \hskip0pt plus\urefprestretchamount\relax} +% +\def\urefcodeamp{\urefprestretch \&\urefpoststretch} +\def\urefcodedot{\urefprestretch .\urefpoststretch} +\def\urefcodehash{\urefprestretch \#\urefpoststretch} +\def\urefcodequest{\urefprestretch ?\urefpoststretch} +\def\urefcodeslash{\futurelet\next\urefcodeslashfinish} +{ + \catcode`\/=\active + \global\def\urefcodeslashfinish{% + \urefprestretch \slashChar + % Allow line break only after the final / in a sequence of + % slashes, to avoid line break between the slashes in http://. + \ifx\next/\else \urefpoststretch \fi + } +} + +% One more complication: by default we'll break after the special +% characters, but some people like to break before the special chars, so +% allow that. Also allow no breaking at all, for manual control. +% +\parseargdef\urefbreakstyle{% + \def\txiarg{#1}% + \ifx\txiarg\wordnone + \def\urefprebreak{\nobreak}\def\urefpostbreak{\nobreak} + \else\ifx\txiarg\wordbefore + \def\urefprebreak{\allowbreak}\def\urefpostbreak{\nobreak} + \else\ifx\txiarg\wordafter + \def\urefprebreak{\nobreak}\def\urefpostbreak{\allowbreak} + \else + \errhelp = \EMsimple + \errmessage{Unknown @urefbreakstyle setting `\txiarg'}% + \fi\fi\fi +} +\def\wordafter{after} +\def\wordbefore{before} +\def\wordnone{none} + +\urefbreakstyle after + +% @url synonym for @uref, since that's how everyone uses it. +% +\let\url=\uref + +% rms does not like angle brackets --karl, 17may97. +% So now @email is just like @uref, unless we are pdf. +% +%\def\email#1{\angleleft{\tt #1}\angleright} +\ifpdf + \def\email#1{\doemail#1,,\finish} + \def\doemail#1,#2,#3\finish{\begingroup + \unsepspaces + \pdfurl{mailto:#1}% + \setbox0 = \hbox{\ignorespaces #2}% + \ifdim\wd0>0pt\unhbox0\else\code{#1}\fi + \endlink + \endgroup} +\else + \let\email=\uref +\fi + +% @kbdinputstyle -- arg is `distinct' (@kbd uses slanted tty font always), +% `example' (@kbd uses ttsl only inside of @example and friends), +% or `code' (@kbd uses normal tty font always). +\parseargdef\kbdinputstyle{% + \def\txiarg{#1}% + \ifx\txiarg\worddistinct + \gdef\kbdexamplefont{\ttsl}\gdef\kbdfont{\ttsl}% + \else\ifx\txiarg\wordexample + \gdef\kbdexamplefont{\ttsl}\gdef\kbdfont{\tt}% + \else\ifx\txiarg\wordcode + \gdef\kbdexamplefont{\tt}\gdef\kbdfont{\tt}% + \else + \errhelp = \EMsimple + \errmessage{Unknown @kbdinputstyle setting `\txiarg'}% + \fi\fi\fi +} +\def\worddistinct{distinct} +\def\wordexample{example} +\def\wordcode{code} + +% Default is `distinct'. +\kbdinputstyle distinct + +% @kbd is like @code, except that if the argument is just one @key command, +% then @kbd has no effect. +\def\kbd#1{{\def\look{#1}\expandafter\kbdsub\look??\par}} + +\def\xkey{\key} +\def\kbdsub#1#2#3\par{% + \def\one{#1}\def\three{#3}\def\threex{??}% + \ifx\one\xkey\ifx\threex\three \key{#2}% + \else{\tclose{\kbdfont\setupmarkupstyle{kbd}\look}}\fi + \else{\tclose{\kbdfont\setupmarkupstyle{kbd}\look}}\fi +} + +% definition of @key that produces a lozenge. Doesn't adjust to text size. +%\setfont\keyrm\rmshape{8}{1000}{OT1} +%\font\keysy=cmsy9 +%\def\key#1{{\keyrm\textfont2=\keysy \leavevmode\hbox{% +% \raise0.4pt\hbox{\angleleft}\kern-.08em\vtop{% +% \vbox{\hrule\kern-0.4pt +% \hbox{\raise0.4pt\hbox{\vphantom{\angleleft}}#1}}% +% \kern-0.4pt\hrule}% +% \kern-.06em\raise0.4pt\hbox{\angleright}}}} + +% definition of @key with no lozenge. If the current font is already +% monospace, don't change it; that way, we respect @kbdinputstyle. But +% if it isn't monospace, then use \tt. +% +\def\key#1{{\setupmarkupstyle{key}% + \nohyphenation + \ifmonospace\else\tt\fi + #1}\null} + +% @clicksequence{File @click{} Open ...} +\def\clicksequence#1{\begingroup #1\endgroup} + +% @clickstyle @arrow (by default) +\parseargdef\clickstyle{\def\click{#1}} +\def\click{\arrow} + +% Typeset a dimension, e.g., `in' or `pt'. The only reason for the +% argument is to make the input look right: @dmn{pt} instead of @dmn{}pt. +% +\def\dmn#1{\thinspace #1} + +% @acronym for "FBI", "NATO", and the like. +% We print this one point size smaller, since it's intended for +% all-uppercase. +% +\def\acronym#1{\doacronym #1,,\finish} +\def\doacronym#1,#2,#3\finish{% + {\selectfonts\lsize #1}% + \def\temp{#2}% + \ifx\temp\empty \else + \space ({\unsepspaces \ignorespaces \temp \unskip})% + \fi + \null % reset \spacefactor=1000 +} + +% @abbr for "Comput. J." and the like. +% No font change, but don't do end-of-sentence spacing. +% +\def\abbr#1{\doabbr #1,,\finish} +\def\doabbr#1,#2,#3\finish{% + {\plainfrenchspacing #1}% + \def\temp{#2}% + \ifx\temp\empty \else + \space ({\unsepspaces \ignorespaces \temp \unskip})% + \fi + \null % reset \spacefactor=1000 +} + +% @asis just yields its argument. Used with @table, for example. +% +\def\asis#1{#1} + +% @math outputs its argument in math mode. +% +% One complication: _ usually means subscripts, but it could also mean +% an actual _ character, as in @math{@var{some_variable} + 1}. So make +% _ active, and distinguish by seeing if the current family is \slfam, +% which is what @var uses. +{ + \catcode`\_ = \active + \gdef\mathunderscore{% + \catcode`\_=\active + \def_{\ifnum\fam=\slfam \_\else\sb\fi}% + } +} +% Another complication: we want \\ (and @\) to output a math (or tt) \. +% FYI, plain.tex uses \\ as a temporary control sequence (for no +% particular reason), but this is not advertised and we don't care. +% +% The \mathchar is class=0=ordinary, family=7=ttfam, position=5C=\. +\def\mathbackslash{\ifnum\fam=\ttfam \mathchar"075C \else\backslash \fi} +% +\def\math{% + \ifmmode\else % only go into math if not in math mode already + \tex + \mathunderscore + \let\\ = \mathbackslash + \mathactive + % make the texinfo accent commands work in math mode + \let\"=\ddot + \let\'=\acute + \let\==\bar + \let\^=\hat + \let\`=\grave + \let\u=\breve + \let\v=\check + \let\~=\tilde + \let\dotaccent=\dot + % have to provide another name for sup operator + \let\mathopsup=\sup + $\expandafter\finishmath\fi +} +\def\finishmath#1{#1$\endgroup} % Close the group opened by \tex. + +% Some active characters (such as <) are spaced differently in math. +% We have to reset their definitions in case the @math was an argument +% to a command which sets the catcodes (such as @item or @section). +% +{ + \catcode`^ = \active + \catcode`< = \active + \catcode`> = \active + \catcode`+ = \active + \catcode`' = \active + \gdef\mathactive{% + \let^ = \ptexhat + \let< = \ptexless + \let> = \ptexgtr + \let+ = \ptexplus + \let' = \ptexquoteright + } +} + +% for @sub and @sup, if in math mode, just do a normal sub/superscript. +% If in text, use math to place as sub/superscript, but switch +% into text mode, with smaller fonts. This is a different font than the +% one used for real math sub/superscripts (8pt vs. 7pt), but let's not +% fix it (significant additions to font machinery) until someone notices. +% +\def\sub{\ifmmode \expandafter\sb \else \expandafter\finishsub\fi} +\def\finishsub#1{$\sb{\hbox{\selectfonts\lllsize #1}}$}% +% +\def\sup{\ifmmode \expandafter\ptexsp \else \expandafter\finishsup\fi} +\def\finishsup#1{$\ptexsp{\hbox{\selectfonts\lllsize #1}}$}% + +% @inlinefmt{FMTNAME,PROCESSED-TEXT} and @inlineraw{FMTNAME,RAW-TEXT}. +% Ignore unless FMTNAME == tex; then it is like @iftex and @tex, +% except specified as a normal braced arg, so no newlines to worry about. +% +\def\outfmtnametex{tex} +% +\long\def\inlinefmt#1{\doinlinefmt #1,\finish} +\long\def\doinlinefmt#1,#2,\finish{% + \def\inlinefmtname{#1}% + \ifx\inlinefmtname\outfmtnametex \ignorespaces #2\fi +} +% +% @inlinefmtifelse{FMTNAME,THEN-TEXT,ELSE-TEXT} expands THEN-TEXT if +% FMTNAME is tex, else ELSE-TEXT. +\long\def\inlinefmtifelse#1{\doinlinefmtifelse #1,,,\finish} +\long\def\doinlinefmtifelse#1,#2,#3,#4,\finish{% + \def\inlinefmtname{#1}% + \ifx\inlinefmtname\outfmtnametex \ignorespaces #2\else \ignorespaces #3\fi +} +% +% For raw, must switch into @tex before parsing the argument, to avoid +% setting catcodes prematurely. Doing it this way means that, for +% example, @inlineraw{html, foo{bar} gets a parse error instead of being +% ignored. But this isn't important because if people want a literal +% *right* brace they would have to use a command anyway, so they may as +% well use a command to get a left brace too. We could re-use the +% delimiter character idea from \verb, but it seems like overkill. +% +\long\def\inlineraw{\tex \doinlineraw} +\long\def\doinlineraw#1{\doinlinerawtwo #1,\finish} +\def\doinlinerawtwo#1,#2,\finish{% + \def\inlinerawname{#1}% + \ifx\inlinerawname\outfmtnametex \ignorespaces #2\fi + \endgroup % close group opened by \tex. +} + +% @inlineifset{VAR, TEXT} expands TEXT if VAR is @set. +% +\long\def\inlineifset#1{\doinlineifset #1,\finish} +\long\def\doinlineifset#1,#2,\finish{% + \def\inlinevarname{#1}% + \expandafter\ifx\csname SET\inlinevarname\endcsname\relax + \else\ignorespaces#2\fi +} + +% @inlineifclear{VAR, TEXT} expands TEXT if VAR is not @set. +% +\long\def\inlineifclear#1{\doinlineifclear #1,\finish} +\long\def\doinlineifclear#1,#2,\finish{% + \def\inlinevarname{#1}% + \expandafter\ifx\csname SET\inlinevarname\endcsname\relax \ignorespaces#2\fi +} + + +\message{glyphs,} +% and logos. + +% @@ prints an @, as does @atchar{}. +\def\@{\char64 } +\let\atchar=\@ + +% @{ @} @lbracechar{} @rbracechar{} all generate brace characters. +% Unless we're in typewriter, use \ecfont because the CM text fonts do +% not have braces, and we don't want to switch into math. +\def\mylbrace{{\ifmonospace\else\ecfont\fi \char123}} +\def\myrbrace{{\ifmonospace\else\ecfont\fi \char125}} +\let\{=\mylbrace \let\lbracechar=\{ +\let\}=\myrbrace \let\rbracechar=\} +\begingroup + % Definitions to produce \{ and \} commands for indices, + % and @{ and @} for the aux/toc files. + \catcode`\{ = \other \catcode`\} = \other + \catcode`\[ = 1 \catcode`\] = 2 + \catcode`\! = 0 \catcode`\\ = \other + !gdef!lbracecmd[\{]% + !gdef!rbracecmd[\}]% + !gdef!lbraceatcmd[@{]% + !gdef!rbraceatcmd[@}]% +!endgroup + +% @comma{} to avoid , parsing problems. +\let\comma = , + +% Accents: @, @dotaccent @ringaccent @ubaraccent @udotaccent +% Others are defined by plain TeX: @` @' @" @^ @~ @= @u @v @H. +\let\, = \ptexc +\let\dotaccent = \ptexdot +\def\ringaccent#1{{\accent23 #1}} +\let\tieaccent = \ptext +\let\ubaraccent = \ptexb +\let\udotaccent = \d + +% Other special characters: @questiondown @exclamdown @ordf @ordm +% Plain TeX defines: @AA @AE @O @OE @L (plus lowercase versions) @ss. +\def\questiondown{?`} +\def\exclamdown{!`} +\def\ordf{\leavevmode\raise1ex\hbox{\selectfonts\lllsize \underbar{a}}} +\def\ordm{\leavevmode\raise1ex\hbox{\selectfonts\lllsize \underbar{o}}} + +% Dotless i and dotless j, used for accents. +\def\imacro{i} +\def\jmacro{j} +\def\dotless#1{% + \def\temp{#1}% + \ifx\temp\imacro \ifmmode\imath \else\ptexi \fi + \else\ifx\temp\jmacro \ifmmode\jmath \else\j \fi + \else \errmessage{@dotless can be used only with i or j}% + \fi\fi +} + +% The \TeX{} logo, as in plain, but resetting the spacing so that a +% period following counts as ending a sentence. (Idea found in latex.) +% +\edef\TeX{\TeX \spacefactor=1000 } + +% @LaTeX{} logo. Not quite the same results as the definition in +% latex.ltx, since we use a different font for the raised A; it's most +% convenient for us to use an explicitly smaller font, rather than using +% the \scriptstyle font (since we don't reset \scriptstyle and +% \scriptscriptstyle). +% +\def\LaTeX{% + L\kern-.36em + {\setbox0=\hbox{T}% + \vbox to \ht0{\hbox{% + \ifx\textnominalsize\xwordpt + % for 10pt running text, \lllsize (8pt) is too small for the A in LaTeX. + % Revert to plain's \scriptsize, which is 7pt. + \count255=\the\fam $\fam\count255 \scriptstyle A$% + \else + % For 11pt, we can use our lllsize. + \selectfonts\lllsize A% + \fi + }% + \vss + }}% + \kern-.15em + \TeX +} + +% Some math mode symbols. Define \ensuremath to switch into math mode +% unless we are already there. Expansion tricks may not be needed here, +% but safer, and can't hurt. +\def\ensuremath{\ifmmode \expandafter\asis \else\expandafter\ensuredmath \fi} +\def\ensuredmath#1{$\relax#1$} +% +\def\bullet{\ensuremath\ptexbullet} +\def\geq{\ensuremath\ge} +\def\leq{\ensuremath\le} +\def\minus{\ensuremath-} + +% @dots{} outputs an ellipsis using the current font. +% We do .5em per period so that it has the same spacing in the cm +% typewriter fonts as three actual period characters; on the other hand, +% in other typewriter fonts three periods are wider than 1.5em. So do +% whichever is larger. +% +\def\dots{% + \leavevmode + \setbox0=\hbox{...}% get width of three periods + \ifdim\wd0 > 1.5em + \dimen0 = \wd0 + \else + \dimen0 = 1.5em + \fi + \hbox to \dimen0{% + \hskip 0pt plus.25fil + .\hskip 0pt plus1fil + .\hskip 0pt plus1fil + .\hskip 0pt plus.5fil + }% +} + +% @enddots{} is an end-of-sentence ellipsis. +% +\def\enddots{% + \dots + \spacefactor=\endofsentencespacefactor +} + +% @point{}, @result{}, @expansion{}, @print{}, @equiv{}. +% +% Since these characters are used in examples, they should be an even number of +% \tt widths. Each \tt character is 1en, so two makes it 1em. +% +\def\point{$\star$} +\def\arrow{\leavevmode\raise.05ex\hbox to 1em{\hfil$\rightarrow$\hfil}} +\def\result{\leavevmode\raise.05ex\hbox to 1em{\hfil$\Rightarrow$\hfil}} +\def\expansion{\leavevmode\hbox to 1em{\hfil$\mapsto$\hfil}} +\def\print{\leavevmode\lower.1ex\hbox to 1em{\hfil$\dashv$\hfil}} +\def\equiv{\leavevmode\hbox to 1em{\hfil$\ptexequiv$\hfil}} + +% The @error{} command. +% Adapted from the TeXbook's \boxit. +% +\newbox\errorbox +% +{\tentt \global\dimen0 = 3em}% Width of the box. +\dimen2 = .55pt % Thickness of rules +% The text. (`r' is open on the right, `e' somewhat less so on the left.) +\setbox0 = \hbox{\kern-.75pt \reducedsf \putworderror\kern-1.5pt} +% +\setbox\errorbox=\hbox to \dimen0{\hfil + \hsize = \dimen0 \advance\hsize by -5.8pt % Space to left+right. + \advance\hsize by -2\dimen2 % Rules. + \vbox{% + \hrule height\dimen2 + \hbox{\vrule width\dimen2 \kern3pt % Space to left of text. + \vtop{\kern2.4pt \box0 \kern2.4pt}% Space above/below. + \kern3pt\vrule width\dimen2}% Space to right. + \hrule height\dimen2} + \hfil} +% +\def\error{\leavevmode\lower.7ex\copy\errorbox} + +% @pounds{} is a sterling sign, which Knuth put in the CM italic font. +% +\def\pounds{{\it\$}} + +% @euro{} comes from a separate font, depending on the current style. +% We use the free feym* fonts from the eurosym package by Henrik +% Theiling, which support regular, slanted, bold and bold slanted (and +% "outlined" (blackboard board, sort of) versions, which we don't need). +% It is available from http://www.ctan.org/tex-archive/fonts/eurosym. +% +% Although only regular is the truly official Euro symbol, we ignore +% that. The Euro is designed to be slightly taller than the regular +% font height. +% +% feymr - regular +% feymo - slanted +% feybr - bold +% feybo - bold slanted +% +% There is no good (free) typewriter version, to my knowledge. +% A feymr10 euro is ~7.3pt wide, while a normal cmtt10 char is ~5.25pt wide. +% Hmm. +% +% Also doesn't work in math. Do we need to do math with euro symbols? +% Hope not. +% +% +\def\euro{{\eurofont e}} +\def\eurofont{% + % We set the font at each command, rather than predefining it in + % \textfonts and the other font-switching commands, so that + % installations which never need the symbol don't have to have the + % font installed. + % + % There is only one designed size (nominal 10pt), so we always scale + % that to the current nominal size. + % + % By the way, simply using "at 1em" works for cmr10 and the like, but + % does not work for cmbx10 and other extended/shrunken fonts. + % + \def\eurosize{\csname\curfontsize nominalsize\endcsname}% + % + \ifx\curfontstyle\bfstylename + % bold: + \font\thiseurofont = \ifusingit{feybo10}{feybr10} at \eurosize + \else + % regular: + \font\thiseurofont = \ifusingit{feymo10}{feymr10} at \eurosize + \fi + \thiseurofont +} + +% Glyphs from the EC fonts. We don't use \let for the aliases, because +% sometimes we redefine the original macro, and the alias should reflect +% the redefinition. +% +% Use LaTeX names for the Icelandic letters. +\def\DH{{\ecfont \char"D0}} % Eth +\def\dh{{\ecfont \char"F0}} % eth +\def\TH{{\ecfont \char"DE}} % Thorn +\def\th{{\ecfont \char"FE}} % thorn +% +\def\guillemetleft{{\ecfont \char"13}} +\def\guillemotleft{\guillemetleft} +\def\guillemetright{{\ecfont \char"14}} +\def\guillemotright{\guillemetright} +\def\guilsinglleft{{\ecfont \char"0E}} +\def\guilsinglright{{\ecfont \char"0F}} +\def\quotedblbase{{\ecfont \char"12}} +\def\quotesinglbase{{\ecfont \char"0D}} +% +% This positioning is not perfect (see the ogonek LaTeX package), but +% we have the precomposed glyphs for the most common cases. We put the +% tests to use those glyphs in the single \ogonek macro so we have fewer +% dummy definitions to worry about for index entries, etc. +% +% ogonek is also used with other letters in Lithuanian (IOU), but using +% the precomposed glyphs for those is not so easy since they aren't in +% the same EC font. +\def\ogonek#1{{% + \def\temp{#1}% + \ifx\temp\macrocharA\Aogonek + \else\ifx\temp\macrochara\aogonek + \else\ifx\temp\macrocharE\Eogonek + \else\ifx\temp\macrochare\eogonek + \else + \ecfont \setbox0=\hbox{#1}% + \ifdim\ht0=1ex\accent"0C #1% + \else\ooalign{\unhbox0\crcr\hidewidth\char"0C \hidewidth}% + \fi + \fi\fi\fi\fi + }% +} +\def\Aogonek{{\ecfont \char"81}}\def\macrocharA{A} +\def\aogonek{{\ecfont \char"A1}}\def\macrochara{a} +\def\Eogonek{{\ecfont \char"86}}\def\macrocharE{E} +\def\eogonek{{\ecfont \char"A6}}\def\macrochare{e} +% +% Use the European Computer Modern fonts (cm-super in outline format) +% for non-CM glyphs. That is ec* for regular text and tc* for the text +% companion symbols (LaTeX TS1 encoding). Both are part of the ec +% package and follow the same conventions. +% +\def\ecfont{\etcfont{e}} +\def\tcfont{\etcfont{t}} +% +\def\etcfont#1{% + % We can't distinguish serif/sans and italic/slanted, but this + % is used for crude hacks anyway (like adding French and German + % quotes to documents typeset with CM, where we lose kerning), so + % hopefully nobody will notice/care. + \edef\ecsize{\csname\curfontsize ecsize\endcsname}% + \edef\nominalsize{\csname\curfontsize nominalsize\endcsname}% + \ifmonospace + % typewriter: + \font\thisecfont = #1ctt\ecsize \space at \nominalsize + \else + \ifx\curfontstyle\bfstylename + % bold: + \font\thisecfont = #1cb\ifusingit{i}{x}\ecsize \space at \nominalsize + \else + % regular: + \font\thisecfont = #1c\ifusingit{ti}{rm}\ecsize \space at \nominalsize + \fi + \fi + \thisecfont +} + +% @registeredsymbol - R in a circle. The font for the R should really +% be smaller yet, but lllsize is the best we can do for now. +% Adapted from the plain.tex definition of \copyright. +% +\def\registeredsymbol{% + $^{{\ooalign{\hfil\raise.07ex\hbox{\selectfonts\lllsize R}% + \hfil\crcr\Orb}}% + }$% +} + +% @textdegree - the normal degrees sign. +% +\def\textdegree{$^\circ$} + +% Laurent Siebenmann reports \Orb undefined with: +% Textures 1.7.7 (preloaded format=plain 93.10.14) (68K) 16 APR 2004 02:38 +% so we'll define it if necessary. +% +\ifx\Orb\thisisundefined +\def\Orb{\mathhexbox20D} +\fi + +% Quotes. +\chardef\quotedblleft="5C +\chardef\quotedblright=`\" +\chardef\quoteleft=`\` +\chardef\quoteright=`\' + + +\message{page headings,} + +\newskip\titlepagetopglue \titlepagetopglue = 1.5in +\newskip\titlepagebottomglue \titlepagebottomglue = 2pc + +% First the title page. Must do @settitle before @titlepage. +\newif\ifseenauthor +\newif\iffinishedtitlepage + +% Do an implicit @contents or @shortcontents after @end titlepage if the +% user says @setcontentsaftertitlepage or @setshortcontentsaftertitlepage. +% +\newif\ifsetcontentsaftertitlepage + \let\setcontentsaftertitlepage = \setcontentsaftertitlepagetrue +\newif\ifsetshortcontentsaftertitlepage + \let\setshortcontentsaftertitlepage = \setshortcontentsaftertitlepagetrue + +\parseargdef\shorttitlepage{% + \begingroup \hbox{}\vskip 1.5in \chaprm \centerline{#1}% + \endgroup\page\hbox{}\page} + +\envdef\titlepage{% + % Open one extra group, as we want to close it in the middle of \Etitlepage. + \begingroup + \parindent=0pt \textfonts + % Leave some space at the very top of the page. + \vglue\titlepagetopglue + % No rule at page bottom unless we print one at the top with @title. + \finishedtitlepagetrue + % + % Most title ``pages'' are actually two pages long, with space + % at the top of the second. We don't want the ragged left on the second. + \let\oldpage = \page + \def\page{% + \iffinishedtitlepage\else + \finishtitlepage + \fi + \let\page = \oldpage + \page + \null + }% +} + +\def\Etitlepage{% + \iffinishedtitlepage\else + \finishtitlepage + \fi + % It is important to do the page break before ending the group, + % because the headline and footline are only empty inside the group. + % If we use the new definition of \page, we always get a blank page + % after the title page, which we certainly don't want. + \oldpage + \endgroup + % + % Need this before the \...aftertitlepage checks so that if they are + % in effect the toc pages will come out with page numbers. + \HEADINGSon + % + % If they want short, they certainly want long too. + \ifsetshortcontentsaftertitlepage + \shortcontents + \contents + \global\let\shortcontents = \relax + \global\let\contents = \relax + \fi + % + \ifsetcontentsaftertitlepage + \contents + \global\let\contents = \relax + \global\let\shortcontents = \relax + \fi +} + +\def\finishtitlepage{% + \vskip4pt \hrule height 2pt width \hsize + \vskip\titlepagebottomglue + \finishedtitlepagetrue +} + +% Settings used for typesetting titles: no hyphenation, no indentation, +% don't worry much about spacing, ragged right. This should be used +% inside a \vbox, and fonts need to be set appropriately first. Because +% it is always used for titles, nothing else, we call \rmisbold. \par +% should be specified before the end of the \vbox, since a vbox is a group. +% +\def\raggedtitlesettings{% + \rmisbold + \hyphenpenalty=10000 + \parindent=0pt + \tolerance=5000 + \ptexraggedright +} + +% Macros to be used within @titlepage: + +\let\subtitlerm=\tenrm +\def\subtitlefont{\subtitlerm \normalbaselineskip = 13pt \normalbaselines} + +\parseargdef\title{% + \checkenv\titlepage + \vbox{\titlefonts \raggedtitlesettings #1\par}% + % print a rule at the page bottom also. + \finishedtitlepagefalse + \vskip4pt \hrule height 4pt width \hsize \vskip4pt +} + +\parseargdef\subtitle{% + \checkenv\titlepage + {\subtitlefont \rightline{#1}}% +} + +% @author should come last, but may come many times. +% It can also be used inside @quotation. +% +\parseargdef\author{% + \def\temp{\quotation}% + \ifx\thisenv\temp + \def\quotationauthor{#1}% printed in \Equotation. + \else + \checkenv\titlepage + \ifseenauthor\else \vskip 0pt plus 1filll \seenauthortrue \fi + {\secfonts\rmisbold \leftline{#1}}% + \fi +} + + +% Set up page headings and footings. + +\let\thispage=\folio + +\newtoks\evenheadline % headline on even pages +\newtoks\oddheadline % headline on odd pages +\newtoks\evenfootline % footline on even pages +\newtoks\oddfootline % footline on odd pages + +% Now make \makeheadline and \makefootline in Plain TeX use those variables +\headline={{\textfonts\rm \ifodd\pageno \the\oddheadline + \else \the\evenheadline \fi}} +\footline={{\textfonts\rm \ifodd\pageno \the\oddfootline + \else \the\evenfootline \fi}\HEADINGShook} +\let\HEADINGShook=\relax + +% Commands to set those variables. +% For example, this is what @headings on does +% @evenheading @thistitle|@thispage|@thischapter +% @oddheading @thischapter|@thispage|@thistitle +% @evenfooting @thisfile|| +% @oddfooting ||@thisfile + + +\def\evenheading{\parsearg\evenheadingxxx} +\def\evenheadingxxx #1{\evenheadingyyy #1\|\|\|\|\finish} +\def\evenheadingyyy #1\|#2\|#3\|#4\finish{% +\global\evenheadline={\rlap{\centerline{#2}}\line{#1\hfil#3}}} + +\def\oddheading{\parsearg\oddheadingxxx} +\def\oddheadingxxx #1{\oddheadingyyy #1\|\|\|\|\finish} +\def\oddheadingyyy #1\|#2\|#3\|#4\finish{% +\global\oddheadline={\rlap{\centerline{#2}}\line{#1\hfil#3}}} + +\parseargdef\everyheading{\oddheadingxxx{#1}\evenheadingxxx{#1}}% + +\def\evenfooting{\parsearg\evenfootingxxx} +\def\evenfootingxxx #1{\evenfootingyyy #1\|\|\|\|\finish} +\def\evenfootingyyy #1\|#2\|#3\|#4\finish{% +\global\evenfootline={\rlap{\centerline{#2}}\line{#1\hfil#3}}} + +\def\oddfooting{\parsearg\oddfootingxxx} +\def\oddfootingxxx #1{\oddfootingyyy #1\|\|\|\|\finish} +\def\oddfootingyyy #1\|#2\|#3\|#4\finish{% + \global\oddfootline = {\rlap{\centerline{#2}}\line{#1\hfil#3}}% + % + % Leave some space for the footline. Hopefully ok to assume + % @evenfooting will not be used by itself. + \global\advance\pageheight by -12pt + \global\advance\vsize by -12pt +} + +\parseargdef\everyfooting{\oddfootingxxx{#1}\evenfootingxxx{#1}} + +% @evenheadingmarks top \thischapter <- chapter at the top of a page +% @evenheadingmarks bottom \thischapter <- chapter at the bottom of a page +% +% The same set of arguments for: +% +% @oddheadingmarks +% @evenfootingmarks +% @oddfootingmarks +% @everyheadingmarks +% @everyfootingmarks + +% These define \getoddheadingmarks, \getevenheadingmarks, +% \getoddfootingmarks, and \getevenfootingmarks, each to one of +% \gettopheadingmarks, \getbottomheadingmarks. +% +\def\evenheadingmarks{\headingmarks{even}{heading}} +\def\oddheadingmarks{\headingmarks{odd}{heading}} +\def\evenfootingmarks{\headingmarks{even}{footing}} +\def\oddfootingmarks{\headingmarks{odd}{footing}} +\def\everyheadingmarks#1 {\headingmarks{even}{heading}{#1} + \headingmarks{odd}{heading}{#1} } +\def\everyfootingmarks#1 {\headingmarks{even}{footing}{#1} + \headingmarks{odd}{footing}{#1} } +% #1 = even/odd, #2 = heading/footing, #3 = top/bottom. +\def\headingmarks#1#2#3 {% + \expandafter\let\expandafter\temp \csname get#3headingmarks\endcsname + \global\expandafter\let\csname get#1#2marks\endcsname \temp +} + +\everyheadingmarks bottom +\everyfootingmarks bottom + +% @headings double turns headings on for double-sided printing. +% @headings single turns headings on for single-sided printing. +% @headings off turns them off. +% @headings on same as @headings double, retained for compatibility. +% @headings after turns on double-sided headings after this page. +% @headings doubleafter turns on double-sided headings after this page. +% @headings singleafter turns on single-sided headings after this page. +% By default, they are off at the start of a document, +% and turned `on' after @end titlepage. + +\def\headings #1 {\csname HEADINGS#1\endcsname} + +\def\headingsoff{% non-global headings elimination + \evenheadline={\hfil}\evenfootline={\hfil}% + \oddheadline={\hfil}\oddfootline={\hfil}% +} + +\def\HEADINGSoff{{\globaldefs=1 \headingsoff}} % global setting +\HEADINGSoff % it's the default + +% When we turn headings on, set the page number to 1. +% For double-sided printing, put current file name in lower left corner, +% chapter name on inside top of right hand pages, document +% title on inside top of left hand pages, and page numbers on outside top +% edge of all pages. +\def\HEADINGSdouble{% +\global\pageno=1 +\global\evenfootline={\hfil} +\global\oddfootline={\hfil} +\global\evenheadline={\line{\folio\hfil\thistitle}} +\global\oddheadline={\line{\thischapterheading\hfil\folio}} +\global\let\contentsalignmacro = \chapoddpage +} +\let\contentsalignmacro = \chappager + +% For single-sided printing, chapter title goes across top left of page, +% page number on top right. +\def\HEADINGSsingle{% +\global\pageno=1 +\global\evenfootline={\hfil} +\global\oddfootline={\hfil} +\global\evenheadline={\line{\thischapterheading\hfil\folio}} +\global\oddheadline={\line{\thischapterheading\hfil\folio}} +\global\let\contentsalignmacro = \chappager +} +\def\HEADINGSon{\HEADINGSdouble} + +\def\HEADINGSafter{\let\HEADINGShook=\HEADINGSdoublex} +\let\HEADINGSdoubleafter=\HEADINGSafter +\def\HEADINGSdoublex{% +\global\evenfootline={\hfil} +\global\oddfootline={\hfil} +\global\evenheadline={\line{\folio\hfil\thistitle}} +\global\oddheadline={\line{\thischapterheading\hfil\folio}} +\global\let\contentsalignmacro = \chapoddpage +} + +\def\HEADINGSsingleafter{\let\HEADINGShook=\HEADINGSsinglex} +\def\HEADINGSsinglex{% +\global\evenfootline={\hfil} +\global\oddfootline={\hfil} +\global\evenheadline={\line{\thischapterheading\hfil\folio}} +\global\oddheadline={\line{\thischapterheading\hfil\folio}} +\global\let\contentsalignmacro = \chappager +} + +% Subroutines used in generating headings +% This produces Day Month Year style of output. +% Only define if not already defined, in case a txi-??.tex file has set +% up a different format (e.g., txi-cs.tex does this). +\ifx\today\thisisundefined +\def\today{% + \number\day\space + \ifcase\month + \or\putwordMJan\or\putwordMFeb\or\putwordMMar\or\putwordMApr + \or\putwordMMay\or\putwordMJun\or\putwordMJul\or\putwordMAug + \or\putwordMSep\or\putwordMOct\or\putwordMNov\or\putwordMDec + \fi + \space\number\year} +\fi + +% @settitle line... specifies the title of the document, for headings. +% It generates no output of its own. +\def\thistitle{\putwordNoTitle} +\def\settitle{\parsearg{\gdef\thistitle}} + + +\message{tables,} +% Tables -- @table, @ftable, @vtable, @item(x). + +% default indentation of table text +\newdimen\tableindent \tableindent=.8in +% default indentation of @itemize and @enumerate text +\newdimen\itemindent \itemindent=.3in +% margin between end of table item and start of table text. +\newdimen\itemmargin \itemmargin=.1in + +% used internally for \itemindent minus \itemmargin +\newdimen\itemmax + +% Note @table, @ftable, and @vtable define @item, @itemx, etc., with +% these defs. +% They also define \itemindex +% to index the item name in whatever manner is desired (perhaps none). + +\newif\ifitemxneedsnegativevskip + +\def\itemxpar{\par\ifitemxneedsnegativevskip\nobreak\vskip-\parskip\nobreak\fi} + +\def\internalBitem{\smallbreak \parsearg\itemzzz} +\def\internalBitemx{\itemxpar \parsearg\itemzzz} + +\def\itemzzz #1{\begingroup % + \advance\hsize by -\rightskip + \advance\hsize by -\tableindent + \setbox0=\hbox{\itemindicate{#1}}% + \itemindex{#1}% + \nobreak % This prevents a break before @itemx. + % + % If the item text does not fit in the space we have, put it on a line + % by itself, and do not allow a page break either before or after that + % line. We do not start a paragraph here because then if the next + % command is, e.g., @kindex, the whatsit would get put into the + % horizontal list on a line by itself, resulting in extra blank space. + \ifdim \wd0>\itemmax + % + % Make this a paragraph so we get the \parskip glue and wrapping, + % but leave it ragged-right. + \begingroup + \advance\leftskip by-\tableindent + \advance\hsize by\tableindent + \advance\rightskip by0pt plus1fil\relax + \leavevmode\unhbox0\par + \endgroup + % + % We're going to be starting a paragraph, but we don't want the + % \parskip glue -- logically it's part of the @item we just started. + \nobreak \vskip-\parskip + % + % Stop a page break at the \parskip glue coming up. However, if + % what follows is an environment such as @example, there will be no + % \parskip glue; then the negative vskip we just inserted would + % cause the example and the item to crash together. So we use this + % bizarre value of 10001 as a signal to \aboveenvbreak to insert + % \parskip glue after all. Section titles are handled this way also. + % + \penalty 10001 + \endgroup + \itemxneedsnegativevskipfalse + \else + % The item text fits into the space. Start a paragraph, so that the + % following text (if any) will end up on the same line. + \noindent + % Do this with kerns and \unhbox so that if there is a footnote in + % the item text, it can migrate to the main vertical list and + % eventually be printed. + \nobreak\kern-\tableindent + \dimen0 = \itemmax \advance\dimen0 by \itemmargin \advance\dimen0 by -\wd0 + \unhbox0 + \nobreak\kern\dimen0 + \endgroup + \itemxneedsnegativevskiptrue + \fi +} + +\def\item{\errmessage{@item while not in a list environment}} +\def\itemx{\errmessage{@itemx while not in a list environment}} + +% @table, @ftable, @vtable. +\envdef\table{% + \let\itemindex\gobble + \tablecheck{table}% +} +\envdef\ftable{% + \def\itemindex ##1{\doind {fn}{\code{##1}}}% + \tablecheck{ftable}% +} +\envdef\vtable{% + \def\itemindex ##1{\doind {vr}{\code{##1}}}% + \tablecheck{vtable}% +} +\def\tablecheck#1{% + \ifnum \the\catcode`\^^M=\active + \endgroup + \errmessage{This command won't work in this context; perhaps the problem is + that we are \inenvironment\thisenv}% + \def\next{\doignore{#1}}% + \else + \let\next\tablex + \fi + \next +} +\def\tablex#1{% + \def\itemindicate{#1}% + \parsearg\tabley +} +\def\tabley#1{% + {% + \makevalueexpandable + \edef\temp{\noexpand\tablez #1\space\space\space}% + \expandafter + }\temp \endtablez +} +\def\tablez #1 #2 #3 #4\endtablez{% + \aboveenvbreak + \ifnum 0#1>0 \advance \leftskip by #1\mil \fi + \ifnum 0#2>0 \tableindent=#2\mil \fi + \ifnum 0#3>0 \advance \rightskip by #3\mil \fi + \itemmax=\tableindent + \advance \itemmax by -\itemmargin + \advance \leftskip by \tableindent + \exdentamount=\tableindent + \parindent = 0pt + \parskip = \smallskipamount + \ifdim \parskip=0pt \parskip=2pt \fi + \let\item = \internalBitem + \let\itemx = \internalBitemx +} +\def\Etable{\endgraf\afterenvbreak} +\let\Eftable\Etable +\let\Evtable\Etable +\let\Eitemize\Etable +\let\Eenumerate\Etable + +% This is the counter used by @enumerate, which is really @itemize + +\newcount \itemno + +\envdef\itemize{\parsearg\doitemize} + +\def\doitemize#1{% + \aboveenvbreak + \itemmax=\itemindent + \advance\itemmax by -\itemmargin + \advance\leftskip by \itemindent + \exdentamount=\itemindent + \parindent=0pt + \parskip=\smallskipamount + \ifdim\parskip=0pt \parskip=2pt \fi + % + % Try typesetting the item mark so that if the document erroneously says + % something like @itemize @samp (intending @table), there's an error + % right away at the @itemize. It's not the best error message in the + % world, but it's better than leaving it to the @item. This means if + % the user wants an empty mark, they have to say @w{} not just @w. + \def\itemcontents{#1}% + \setbox0 = \hbox{\itemcontents}% + % + % @itemize with no arg is equivalent to @itemize @bullet. + \ifx\itemcontents\empty\def\itemcontents{\bullet}\fi + % + \let\item=\itemizeitem +} + +% Definition of @item while inside @itemize and @enumerate. +% +\def\itemizeitem{% + \advance\itemno by 1 % for enumerations + {\let\par=\endgraf \smallbreak}% reasonable place to break + {% + % If the document has an @itemize directly after a section title, a + % \nobreak will be last on the list, and \sectionheading will have + % done a \vskip-\parskip. In that case, we don't want to zero + % parskip, or the item text will crash with the heading. On the + % other hand, when there is normal text preceding the item (as there + % usually is), we do want to zero parskip, or there would be too much + % space. In that case, we won't have a \nobreak before. At least + % that's the theory. + \ifnum\lastpenalty<10000 \parskip=0in \fi + \noindent + \hbox to 0pt{\hss \itemcontents \kern\itemmargin}% + % + \ifinner\else + \vadjust{\penalty 1200}% not good to break after first line of item. + \fi + % We can be in inner vertical mode in a footnote, although an + % @itemize looks awful there. + }% + \flushcr +} + +% \splitoff TOKENS\endmark defines \first to be the first token in +% TOKENS, and \rest to be the remainder. +% +\def\splitoff#1#2\endmark{\def\first{#1}\def\rest{#2}}% + +% Allow an optional argument of an uppercase letter, lowercase letter, +% or number, to specify the first label in the enumerated list. No +% argument is the same as `1'. +% +\envparseargdef\enumerate{\enumeratey #1 \endenumeratey} +\def\enumeratey #1 #2\endenumeratey{% + % If we were given no argument, pretend we were given `1'. + \def\thearg{#1}% + \ifx\thearg\empty \def\thearg{1}\fi + % + % Detect if the argument is a single token. If so, it might be a + % letter. Otherwise, the only valid thing it can be is a number. + % (We will always have one token, because of the test we just made. + % This is a good thing, since \splitoff doesn't work given nothing at + % all -- the first parameter is undelimited.) + \expandafter\splitoff\thearg\endmark + \ifx\rest\empty + % Only one token in the argument. It could still be anything. + % A ``lowercase letter'' is one whose \lccode is nonzero. + % An ``uppercase letter'' is one whose \lccode is both nonzero, and + % not equal to itself. + % Otherwise, we assume it's a number. + % + % We need the \relax at the end of the \ifnum lines to stop TeX from + % continuing to look for a . + % + \ifnum\lccode\expandafter`\thearg=0\relax + \numericenumerate % a number (we hope) + \else + % It's a letter. + \ifnum\lccode\expandafter`\thearg=\expandafter`\thearg\relax + \lowercaseenumerate % lowercase letter + \else + \uppercaseenumerate % uppercase letter + \fi + \fi + \else + % Multiple tokens in the argument. We hope it's a number. + \numericenumerate + \fi +} + +% An @enumerate whose labels are integers. The starting integer is +% given in \thearg. +% +\def\numericenumerate{% + \itemno = \thearg + \startenumeration{\the\itemno}% +} + +% The starting (lowercase) letter is in \thearg. +\def\lowercaseenumerate{% + \itemno = \expandafter`\thearg + \startenumeration{% + % Be sure we're not beyond the end of the alphabet. + \ifnum\itemno=0 + \errmessage{No more lowercase letters in @enumerate; get a bigger + alphabet}% + \fi + \char\lccode\itemno + }% +} + +% The starting (uppercase) letter is in \thearg. +\def\uppercaseenumerate{% + \itemno = \expandafter`\thearg + \startenumeration{% + % Be sure we're not beyond the end of the alphabet. + \ifnum\itemno=0 + \errmessage{No more uppercase letters in @enumerate; get a bigger + alphabet} + \fi + \char\uccode\itemno + }% +} + +% Call \doitemize, adding a period to the first argument and supplying the +% common last two arguments. Also subtract one from the initial value in +% \itemno, since @item increments \itemno. +% +\def\startenumeration#1{% + \advance\itemno by -1 + \doitemize{#1.}\flushcr +} + +% @alphaenumerate and @capsenumerate are abbreviations for giving an arg +% to @enumerate. +% +\def\alphaenumerate{\enumerate{a}} +\def\capsenumerate{\enumerate{A}} +\def\Ealphaenumerate{\Eenumerate} +\def\Ecapsenumerate{\Eenumerate} + + +% @multitable macros +% Amy Hendrickson, 8/18/94, 3/6/96 +% +% @multitable ... @end multitable will make as many columns as desired. +% Contents of each column will wrap at width given in preamble. Width +% can be specified either with sample text given in a template line, +% or in percent of \hsize, the current width of text on page. + +% Table can continue over pages but will only break between lines. + +% To make preamble: +% +% Either define widths of columns in terms of percent of \hsize: +% @multitable @columnfractions .25 .3 .45 +% @item ... +% +% Numbers following @columnfractions are the percent of the total +% current hsize to be used for each column. You may use as many +% columns as desired. + + +% Or use a template: +% @multitable {Column 1 template} {Column 2 template} {Column 3 template} +% @item ... +% using the widest term desired in each column. + +% Each new table line starts with @item, each subsequent new column +% starts with @tab. Empty columns may be produced by supplying @tab's +% with nothing between them for as many times as empty columns are needed, +% ie, @tab@tab@tab will produce two empty columns. + +% @item, @tab do not need to be on their own lines, but it will not hurt +% if they are. + +% Sample multitable: + +% @multitable {Column 1 template} {Column 2 template} {Column 3 template} +% @item first col stuff @tab second col stuff @tab third col +% @item +% first col stuff +% @tab +% second col stuff +% @tab +% third col +% @item first col stuff @tab second col stuff +% @tab Many paragraphs of text may be used in any column. +% +% They will wrap at the width determined by the template. +% @item@tab@tab This will be in third column. +% @end multitable + +% Default dimensions may be reset by user. +% @multitableparskip is vertical space between paragraphs in table. +% @multitableparindent is paragraph indent in table. +% @multitablecolmargin is horizontal space to be left between columns. +% @multitablelinespace is space to leave between table items, baseline +% to baseline. +% 0pt means it depends on current normal line spacing. +% +\newskip\multitableparskip +\newskip\multitableparindent +\newdimen\multitablecolspace +\newskip\multitablelinespace +\multitableparskip=0pt +\multitableparindent=6pt +\multitablecolspace=12pt +\multitablelinespace=0pt + +% Macros used to set up halign preamble: +% +\let\endsetuptable\relax +\def\xendsetuptable{\endsetuptable} +\let\columnfractions\relax +\def\xcolumnfractions{\columnfractions} +\newif\ifsetpercent + +% #1 is the @columnfraction, usually a decimal number like .5, but might +% be just 1. We just use it, whatever it is. +% +\def\pickupwholefraction#1 {% + \global\advance\colcount by 1 + \expandafter\xdef\csname col\the\colcount\endcsname{#1\hsize}% + \setuptable +} + +\newcount\colcount +\def\setuptable#1{% + \def\firstarg{#1}% + \ifx\firstarg\xendsetuptable + \let\go = \relax + \else + \ifx\firstarg\xcolumnfractions + \global\setpercenttrue + \else + \ifsetpercent + \let\go\pickupwholefraction + \else + \global\advance\colcount by 1 + \setbox0=\hbox{#1\unskip\space}% Add a normal word space as a + % separator; typically that is always in the input, anyway. + \expandafter\xdef\csname col\the\colcount\endcsname{\the\wd0}% + \fi + \fi + \ifx\go\pickupwholefraction + % Put the argument back for the \pickupwholefraction call, so + % we'll always have a period there to be parsed. + \def\go{\pickupwholefraction#1}% + \else + \let\go = \setuptable + \fi% + \fi + \go +} + +% multitable-only commands. +% +% @headitem starts a heading row, which we typeset in bold. Assignments +% have to be global since we are inside the implicit group of an +% alignment entry. \everycr below resets \everytab so we don't have to +% undo it ourselves. +\def\headitemfont{\b}% for people to use in the template row; not changeable +\def\headitem{% + \checkenv\multitable + \crcr + \gdef\headitemcrhook{\nobreak}% attempt to avoid page break after headings + \global\everytab={\bf}% can't use \headitemfont since the parsing differs + \the\everytab % for the first item +}% +% +% default for tables with no headings. +\let\headitemcrhook=\relax +% +% A \tab used to include \hskip1sp. But then the space in a template +% line is not enough. That is bad. So let's go back to just `&' until +% we again encounter the problem the 1sp was intended to solve. +% --karl, nathan@acm.org, 20apr99. +\def\tab{\checkenv\multitable &\the\everytab}% + +% @multitable ... @end multitable definitions: +% +\newtoks\everytab % insert after every tab. +% +\envdef\multitable{% + \vskip\parskip + \startsavinginserts + % + % @item within a multitable starts a normal row. + % We use \def instead of \let so that if one of the multitable entries + % contains an @itemize, we don't choke on the \item (seen as \crcr aka + % \endtemplate) expanding \doitemize. + \def\item{\crcr}% + % + \tolerance=9500 + \hbadness=9500 + \setmultitablespacing + \parskip=\multitableparskip + \parindent=\multitableparindent + \overfullrule=0pt + \global\colcount=0 + % + \everycr = {% + \noalign{% + \global\everytab={}% Reset from possible headitem. + \global\colcount=0 % Reset the column counter. + % + % Check for saved footnotes, etc.: + \checkinserts + % + % Perhaps a \nobreak, then reset: + \headitemcrhook + \global\let\headitemcrhook=\relax + }% + }% + % + \parsearg\domultitable +} +\def\domultitable#1{% + % To parse everything between @multitable and @item: + \setuptable#1 \endsetuptable + % + % This preamble sets up a generic column definition, which will + % be used as many times as user calls for columns. + % \vtop will set a single line and will also let text wrap and + % continue for many paragraphs if desired. + \halign\bgroup &% + \global\advance\colcount by 1 + \multistrut + \vtop{% + % Use the current \colcount to find the correct column width: + \hsize=\expandafter\csname col\the\colcount\endcsname + % + % In order to keep entries from bumping into each other + % we will add a \leftskip of \multitablecolspace to all columns after + % the first one. + % + % If a template has been used, we will add \multitablecolspace + % to the width of each template entry. + % + % If the user has set preamble in terms of percent of \hsize we will + % use that dimension as the width of the column, and the \leftskip + % will keep entries from bumping into each other. Table will start at + % left margin and final column will justify at right margin. + % + % Make sure we don't inherit \rightskip from the outer environment. + \rightskip=0pt + \ifnum\colcount=1 + % The first column will be indented with the surrounding text. + \advance\hsize by\leftskip + \else + \ifsetpercent \else + % If user has not set preamble in terms of percent of \hsize + % we will advance \hsize by \multitablecolspace. + \advance\hsize by \multitablecolspace + \fi + % In either case we will make \leftskip=\multitablecolspace: + \leftskip=\multitablecolspace + \fi + % Ignoring space at the beginning and end avoids an occasional spurious + % blank line, when TeX decides to break the line at the space before the + % box from the multistrut, so the strut ends up on a line by itself. + % For example: + % @multitable @columnfractions .11 .89 + % @item @code{#} + % @tab Legal holiday which is valid in major parts of the whole country. + % Is automatically provided with highlighting sequences respectively + % marking characters. + \noindent\ignorespaces##\unskip\multistrut + }\cr +} +\def\Emultitable{% + \crcr + \egroup % end the \halign + \global\setpercentfalse +} + +\def\setmultitablespacing{% + \def\multistrut{\strut}% just use the standard line spacing + % + % Compute \multitablelinespace (if not defined by user) for use in + % \multitableparskip calculation. We used define \multistrut based on + % this, but (ironically) that caused the spacing to be off. + % See bug-texinfo report from Werner Lemberg, 31 Oct 2004 12:52:20 +0100. +\ifdim\multitablelinespace=0pt +\setbox0=\vbox{X}\global\multitablelinespace=\the\baselineskip +\global\advance\multitablelinespace by-\ht0 +\fi +% Test to see if parskip is larger than space between lines of +% table. If not, do nothing. +% If so, set to same dimension as multitablelinespace. +\ifdim\multitableparskip>\multitablelinespace +\global\multitableparskip=\multitablelinespace +\global\advance\multitableparskip-7pt % to keep parskip somewhat smaller + % than skip between lines in the table. +\fi% +\ifdim\multitableparskip=0pt +\global\multitableparskip=\multitablelinespace +\global\advance\multitableparskip-7pt % to keep parskip somewhat smaller + % than skip between lines in the table. +\fi} + + +\message{conditionals,} + +% @iftex, @ifnotdocbook, @ifnothtml, @ifnotinfo, @ifnotplaintext, +% @ifnotxml always succeed. They currently do nothing; we don't +% attempt to check whether the conditionals are properly nested. But we +% have to remember that they are conditionals, so that @end doesn't +% attempt to close an environment group. +% +\def\makecond#1{% + \expandafter\let\csname #1\endcsname = \relax + \expandafter\let\csname iscond.#1\endcsname = 1 +} +\makecond{iftex} +\makecond{ifnotdocbook} +\makecond{ifnothtml} +\makecond{ifnotinfo} +\makecond{ifnotplaintext} +\makecond{ifnotxml} + +% Ignore @ignore, @ifhtml, @ifinfo, and the like. +% +\def\direntry{\doignore{direntry}} +\def\documentdescription{\doignore{documentdescription}} +\def\docbook{\doignore{docbook}} +\def\html{\doignore{html}} +\def\ifdocbook{\doignore{ifdocbook}} +\def\ifhtml{\doignore{ifhtml}} +\def\ifinfo{\doignore{ifinfo}} +\def\ifnottex{\doignore{ifnottex}} +\def\ifplaintext{\doignore{ifplaintext}} +\def\ifxml{\doignore{ifxml}} +\def\ignore{\doignore{ignore}} +\def\menu{\doignore{menu}} +\def\xml{\doignore{xml}} + +% Ignore text until a line `@end #1', keeping track of nested conditionals. +% +% A count to remember the depth of nesting. +\newcount\doignorecount + +\def\doignore#1{\begingroup + % Scan in ``verbatim'' mode: + \obeylines + \catcode`\@ = \other + \catcode`\{ = \other + \catcode`\} = \other + % + % Make sure that spaces turn into tokens that match what \doignoretext wants. + \spaceisspace + % + % Count number of #1's that we've seen. + \doignorecount = 0 + % + % Swallow text until we reach the matching `@end #1'. + \dodoignore{#1}% +} + +{ \catcode`_=11 % We want to use \_STOP_ which cannot appear in texinfo source. + \obeylines % + % + \gdef\dodoignore#1{% + % #1 contains the command name as a string, e.g., `ifinfo'. + % + % Define a command to find the next `@end #1'. + \long\def\doignoretext##1^^M@end #1{% + \doignoretextyyy##1^^M@#1\_STOP_}% + % + % And this command to find another #1 command, at the beginning of a + % line. (Otherwise, we would consider a line `@c @ifset', for + % example, to count as an @ifset for nesting.) + \long\def\doignoretextyyy##1^^M@#1##2\_STOP_{\doignoreyyy{##2}\_STOP_}% + % + % And now expand that command. + \doignoretext ^^M% + }% +} + +\def\doignoreyyy#1{% + \def\temp{#1}% + \ifx\temp\empty % Nothing found. + \let\next\doignoretextzzz + \else % Found a nested condition, ... + \advance\doignorecount by 1 + \let\next\doignoretextyyy % ..., look for another. + % If we're here, #1 ends with ^^M\ifinfo (for example). + \fi + \next #1% the token \_STOP_ is present just after this macro. +} + +% We have to swallow the remaining "\_STOP_". +% +\def\doignoretextzzz#1{% + \ifnum\doignorecount = 0 % We have just found the outermost @end. + \let\next\enddoignore + \else % Still inside a nested condition. + \advance\doignorecount by -1 + \let\next\doignoretext % Look for the next @end. + \fi + \next +} + +% Finish off ignored text. +{ \obeylines% + % Ignore anything after the last `@end #1'; this matters in verbatim + % environments, where otherwise the newline after an ignored conditional + % would result in a blank line in the output. + \gdef\enddoignore#1^^M{\endgroup\ignorespaces}% +} + + +% @set VAR sets the variable VAR to an empty value. +% @set VAR REST-OF-LINE sets VAR to the value REST-OF-LINE. +% +% Since we want to separate VAR from REST-OF-LINE (which might be +% empty), we can't just use \parsearg; we have to insert a space of our +% own to delimit the rest of the line, and then take it out again if we +% didn't need it. +% We rely on the fact that \parsearg sets \catcode`\ =10. +% +\parseargdef\set{\setyyy#1 \endsetyyy} +\def\setyyy#1 #2\endsetyyy{% + {% + \makevalueexpandable + \def\temp{#2}% + \edef\next{\gdef\makecsname{SET#1}}% + \ifx\temp\empty + \next{}% + \else + \setzzz#2\endsetzzz + \fi + }% +} +% Remove the trailing space \setxxx inserted. +\def\setzzz#1 \endsetzzz{\next{#1}} + +% @clear VAR clears (i.e., unsets) the variable VAR. +% +\parseargdef\clear{% + {% + \makevalueexpandable + \global\expandafter\let\csname SET#1\endcsname=\relax + }% +} + +% @value{foo} gets the text saved in variable foo. +\def\value{\begingroup\makevalueexpandable\valuexxx} +\def\valuexxx#1{\expandablevalue{#1}\endgroup} +{ + \catcode`\-=\active \catcode`\_=\active + % + \gdef\makevalueexpandable{% + \let\value = \expandablevalue + % We don't want these characters active, ... + \catcode`\-=\other \catcode`\_=\other + % ..., but we might end up with active ones in the argument if + % we're called from @code, as @code{@value{foo-bar_}}, though. + % So \let them to their normal equivalents. + \let-\normaldash \let_\normalunderscore + } +} + +% We have this subroutine so that we can handle at least some @value's +% properly in indexes (we call \makevalueexpandable in \indexdummies). +% The command has to be fully expandable (if the variable is set), since +% the result winds up in the index file. This means that if the +% variable's value contains other Texinfo commands, it's almost certain +% it will fail (although perhaps we could fix that with sufficient work +% to do a one-level expansion on the result, instead of complete). +% +% Unfortunately, this has the consequence that when _ is in the *value* +% of an @set, it does not print properly in the roman fonts (get the cmr +% dot accent at position 126 instead). No fix comes to mind, and it's +% been this way since 2003 or earlier, so just ignore it. +% +\def\expandablevalue#1{% + \expandafter\ifx\csname SET#1\endcsname\relax + {[No value for ``#1'']}% + \message{Variable `#1', used in @value, is not set.}% + \else + \csname SET#1\endcsname + \fi +} + +% @ifset VAR ... @end ifset reads the `...' iff VAR has been defined +% with @set. +% +% To get the special treatment we need for `@end ifset,' we call +% \makecond and then redefine. +% +\makecond{ifset} +\def\ifset{\parsearg{\doifset{\let\next=\ifsetfail}}} +\def\doifset#1#2{% + {% + \makevalueexpandable + \let\next=\empty + \expandafter\ifx\csname SET#2\endcsname\relax + #1% If not set, redefine \next. + \fi + \expandafter + }\next +} +\def\ifsetfail{\doignore{ifset}} + +% @ifclear VAR ... @end executes the `...' iff VAR has never been +% defined with @set, or has been undefined with @clear. +% +% The `\else' inside the `\doifset' parameter is a trick to reuse the +% above code: if the variable is not set, do nothing, if it is set, +% then redefine \next to \ifclearfail. +% +\makecond{ifclear} +\def\ifclear{\parsearg{\doifset{\else \let\next=\ifclearfail}}} +\def\ifclearfail{\doignore{ifclear}} + +% @ifcommandisdefined CMD ... @end executes the `...' if CMD (written +% without the @) is in fact defined. We can only feasibly check at the +% TeX level, so something like `mathcode' is going to considered +% defined even though it is not a Texinfo command. +% +\makecond{ifcommanddefined} +\def\ifcommanddefined{\parsearg{\doifcmddefined{\let\next=\ifcmddefinedfail}}} +% +\def\doifcmddefined#1#2{{% + \makevalueexpandable + \let\next=\empty + \expandafter\ifx\csname #2\endcsname\relax + #1% If not defined, \let\next as above. + \fi + \expandafter + }\next +} +\def\ifcmddefinedfail{\doignore{ifcommanddefined}} + +% @ifcommandnotdefined CMD ... handled similar to @ifclear above. +\makecond{ifcommandnotdefined} +\def\ifcommandnotdefined{% + \parsearg{\doifcmddefined{\else \let\next=\ifcmdnotdefinedfail}}} +\def\ifcmdnotdefinedfail{\doignore{ifcommandnotdefined}} + +% Set the `txicommandconditionals' variable, so documents have a way to +% test if the @ifcommand...defined conditionals are available. +\set txicommandconditionals + +% @dircategory CATEGORY -- specify a category of the dir file +% which this file should belong to. Ignore this in TeX. +\let\dircategory=\comment + +% @defininfoenclose. +\let\definfoenclose=\comment + + +\message{indexing,} +% Index generation facilities + +% Define \newwrite to be identical to plain tex's \newwrite +% except not \outer, so it can be used within macros and \if's. +\edef\newwrite{\makecsname{ptexnewwrite}} + +% \newindex {foo} defines an index named IX. +% It automatically defines \IXindex such that +% \IXindex ...rest of line... puts an entry in the index IX. +% It also defines \IXindfile to be the number of the output channel for +% the file that accumulates this index. The file's extension is IX. +% The name of an index should be no more than 2 characters long +% for the sake of vms. +% +\def\newindex#1{% + \expandafter\chardef\csname#1indfile\endcsname=0 + \expandafter\xdef\csname#1index\endcsname{% % Define @#1index + \noexpand\doindex{#1}} +} + +% @defindex foo == \newindex{foo} +% +\def\defindex{\parsearg\newindex} + +% Define @defcodeindex, like @defindex except put all entries in @code. +% +\def\defcodeindex{\parsearg\newcodeindex} +% +\def\newcodeindex#1{% + \expandafter\chardef\csname#1indfile\endcsname=0 + \expandafter\xdef\csname#1index\endcsname{% + \noexpand\docodeindex{#1}}% +} + +% The default indices: +\newindex{cp}% concepts, +\newcodeindex{fn}% functions, +\newcodeindex{vr}% variables, +\newcodeindex{tp}% types, +\newcodeindex{ky}% keys +\newcodeindex{pg}% and programs. + + +% @synindex foo bar makes index foo feed into index bar. +% Do this instead of @defindex foo if you don't want it as a separate index. +% +% @syncodeindex foo bar similar, but put all entries made for index foo +% inside @code. +% +\def\synindex#1 #2 {\dosynindex\doindex{#1}{#2}} +\def\syncodeindex#1 #2 {\dosynindex\docodeindex{#1}{#2}} + +% #1 is \doindex or \docodeindex, #2 the index getting redefined (foo), +% #3 the target index (bar). +\def\dosynindex#1#2#3{% + % Only do \closeout if we haven't already done it, else we'll end up + % closing the target index. + \expandafter \ifx\csname donesynindex#2\endcsname \relax + % The \closeout helps reduce unnecessary open files; the limit on the + % Acorn RISC OS is a mere 16 files. + \expandafter\closeout\csname#2indfile\endcsname + \expandafter\let\csname donesynindex#2\endcsname = 1 + \fi + % redefine \fooindfile: + \expandafter\let\expandafter\temp\expandafter=\csname#3indfile\endcsname + \expandafter\let\csname#2indfile\endcsname=\temp + % redefine \fooindex: + \expandafter\xdef\csname#2index\endcsname{\noexpand#1{#3}}% +} + +% Define \doindex, the driver for all index macros. +% Argument #1 is generated by the calling \fooindex macro, +% and it the two-letter name of the index. + +\def\doindex#1{\edef\indexname{#1}\parsearg\doindexxxx} +\def\doindexxxx #1{\doind{\indexname}{#1}} + +% like the previous two, but they put @code around the argument. +\def\docodeindex#1{\edef\indexname{#1}\parsearg\docodeindexxxx} +\def\docodeindexxxx #1{\doind{\indexname}{\code{#1}}} + +% Used when writing an index entry out to an index file, to prevent +% expansion of Texinfo commands that can appear in an index entry. +% +\def\indexdummies{% + \escapechar = `\\ % use backslash in output files. + \def\@{@}% change to @@ when we switch to @ as escape char in index files. + \def\ {\realbackslash\space }% + % + % Need these unexpandable (because we define \tt as a dummy) + % definitions when @{ or @} appear in index entry text. Also, more + % complicated, when \tex is in effect and \{ is a \delimiter again. + % We can't use \lbracecmd and \rbracecmd because texindex assumes + % braces and backslashes are used only as delimiters. Perhaps we + % should use @lbracechar and @rbracechar? + \def\{{{\tt\char123}}% + \def\}{{\tt\char125}}% + % + % Do the redefinitions. + \commondummies +} + +% For the aux and toc files, @ is the escape character. So we want to +% redefine everything using @ as the escape character (instead of +% \realbackslash, still used for index files). When everything uses @, +% this will be simpler. +% +\def\atdummies{% + \def\@{@@}% + \def\ {@ }% + \let\{ = \lbraceatcmd + \let\} = \rbraceatcmd + % + % Do the redefinitions. + \commondummies + \otherbackslash +} + +% Called from \indexdummies and \atdummies. +% +\def\commondummies{% + % \definedummyword defines \#1 as \string\#1\space, thus effectively + % preventing its expansion. This is used only for control words, + % not control letters, because the \space would be incorrect for + % control characters, but is needed to separate the control word + % from whatever follows. + % + % For control letters, we have \definedummyletter, which omits the + % space. + % + % These can be used both for control words that take an argument and + % those that do not. If it is followed by {arg} in the input, then + % that will dutifully get written to the index (or wherever). + % + \def\definedummyword ##1{\def##1{\string##1\space}}% + \def\definedummyletter##1{\def##1{\string##1}}% + \let\definedummyaccent\definedummyletter + % + \commondummiesnofonts + % + \definedummyletter\_% + \definedummyletter\-% + % + % Non-English letters. + \definedummyword\AA + \definedummyword\AE + \definedummyword\DH + \definedummyword\L + \definedummyword\O + \definedummyword\OE + \definedummyword\TH + \definedummyword\aa + \definedummyword\ae + \definedummyword\dh + \definedummyword\exclamdown + \definedummyword\l + \definedummyword\o + \definedummyword\oe + \definedummyword\ordf + \definedummyword\ordm + \definedummyword\questiondown + \definedummyword\ss + \definedummyword\th + % + % Although these internal commands shouldn't show up, sometimes they do. + \definedummyword\bf + \definedummyword\gtr + \definedummyword\hat + \definedummyword\less + \definedummyword\sf + \definedummyword\sl + \definedummyword\tclose + \definedummyword\tt + % + \definedummyword\LaTeX + \definedummyword\TeX + % + % Assorted special characters. + \definedummyword\arrow + \definedummyword\bullet + \definedummyword\comma + \definedummyword\copyright + \definedummyword\registeredsymbol + \definedummyword\dots + \definedummyword\enddots + \definedummyword\entrybreak + \definedummyword\equiv + \definedummyword\error + \definedummyword\euro + \definedummyword\expansion + \definedummyword\geq + \definedummyword\guillemetleft + \definedummyword\guillemetright + \definedummyword\guilsinglleft + \definedummyword\guilsinglright + \definedummyword\lbracechar + \definedummyword\leq + \definedummyword\mathopsup + \definedummyword\minus + \definedummyword\ogonek + \definedummyword\pounds + \definedummyword\point + \definedummyword\print + \definedummyword\quotedblbase + \definedummyword\quotedblleft + \definedummyword\quotedblright + \definedummyword\quoteleft + \definedummyword\quoteright + \definedummyword\quotesinglbase + \definedummyword\rbracechar + \definedummyword\result + \definedummyword\sub + \definedummyword\sup + \definedummyword\textdegree + % + % We want to disable all macros so that they are not expanded by \write. + \macrolist + % + \normalturnoffactive + % + % Handle some cases of @value -- where it does not contain any + % (non-fully-expandable) commands. + \makevalueexpandable +} + +% \commondummiesnofonts: common to \commondummies and \indexnofonts. +% Define \definedumyletter, \definedummyaccent and \definedummyword before +% using. +% +\def\commondummiesnofonts{% + % Control letters and accents. + \definedummyletter\!% + \definedummyaccent\"% + \definedummyaccent\'% + \definedummyletter\*% + \definedummyaccent\,% + \definedummyletter\.% + \definedummyletter\/% + \definedummyletter\:% + \definedummyaccent\=% + \definedummyletter\?% + \definedummyaccent\^% + \definedummyaccent\`% + \definedummyaccent\~% + \definedummyword\u + \definedummyword\v + \definedummyword\H + \definedummyword\dotaccent + \definedummyword\ogonek + \definedummyword\ringaccent + \definedummyword\tieaccent + \definedummyword\ubaraccent + \definedummyword\udotaccent + \definedummyword\dotless + % + % Texinfo font commands. + \definedummyword\b + \definedummyword\i + \definedummyword\r + \definedummyword\sansserif + \definedummyword\sc + \definedummyword\slanted + \definedummyword\t + % + % Commands that take arguments. + \definedummyword\abbr + \definedummyword\acronym + \definedummyword\anchor + \definedummyword\cite + \definedummyword\code + \definedummyword\command + \definedummyword\dfn + \definedummyword\dmn + \definedummyword\email + \definedummyword\emph + \definedummyword\env + \definedummyword\file + \definedummyword\image + \definedummyword\indicateurl + \definedummyword\inforef + \definedummyword\kbd + \definedummyword\key + \definedummyword\math + \definedummyword\option + \definedummyword\pxref + \definedummyword\ref + \definedummyword\samp + \definedummyword\strong + \definedummyword\tie + \definedummyword\U + \definedummyword\uref + \definedummyword\url + \definedummyword\var + \definedummyword\verb + \definedummyword\w + \definedummyword\xref +} + +% For testing: output @{ and @} in index sort strings as \{ and \}. +\newif\ifusebracesinindexes + +\let\indexlbrace\relax +\let\indexrbrace\relax + +{\catcode`\@=0 +\catcode`\\=13 + @gdef@backslashdisappear{@def\{}} +} + +{ +\catcode`\<=13 +\catcode`\-=13 +\catcode`\`=13 + \gdef\indexnonalnumdisappear{% + \expandafter\ifx\csname SETtxiindexlquoteignore\endcsname\relax\else + % @set txiindexlquoteignore makes us ignore left quotes in the sort term. + % (Introduced for FSFS 2nd ed.) + \let`=\empty + \fi + % + \expandafter\ifx\csname SETtxiindexbackslashignore\endcsname\relax\else + \backslashdisappear + \fi + % + \expandafter\ifx\csname SETtxiindexhyphenignore\endcsname\relax\else + \def-{}% + \fi + \expandafter\ifx\csname SETtxiindexlessthanignore\endcsname\relax\else + \def<{}% + \fi + \expandafter\ifx\csname SETtxiindexatsignignore\endcsname\relax\else + \def\@{}% + \fi + } + + \gdef\indexnonalnumreappear{% + \useindexbackslash + \let-\normaldash + \let<\normalless + \def\@{@}% + } +} + + +% \indexnofonts is used when outputting the strings to sort the index +% by, and when constructing control sequence names. It eliminates all +% control sequences and just writes whatever the best ASCII sort string +% would be for a given command (usually its argument). +% +\def\indexnofonts{% + % Accent commands should become @asis. + \def\definedummyaccent##1{\let##1\asis}% + % We can just ignore other control letters. + \def\definedummyletter##1{\let##1\empty}% + % All control words become @asis by default; overrides below. + \let\definedummyword\definedummyaccent + \commondummiesnofonts + % + % Don't no-op \tt, since it isn't a user-level command + % and is used in the definitions of the active chars like <, >, |, etc. + % Likewise with the other plain tex font commands. + %\let\tt=\asis + % + \def\ { }% + \def\@{@}% + \def\_{\normalunderscore}% + \def\-{}% @- shouldn't affect sorting + % + \uccode`\1=`\{ \uppercase{\def\{{1}}% + \uccode`\1=`\} \uppercase{\def\}{1}}% + \let\lbracechar\{% + \let\rbracechar\}% + % + % Non-English letters. + \def\AA{AA}% + \def\AE{AE}% + \def\DH{DZZ}% + \def\L{L}% + \def\OE{OE}% + \def\O{O}% + \def\TH{TH}% + \def\aa{aa}% + \def\ae{ae}% + \def\dh{dzz}% + \def\exclamdown{!}% + \def\l{l}% + \def\oe{oe}% + \def\ordf{a}% + \def\ordm{o}% + \def\o{o}% + \def\questiondown{?}% + \def\ss{ss}% + \def\th{th}% + % + \def\LaTeX{LaTeX}% + \def\TeX{TeX}% + % + % Assorted special characters. + % (The following {} will end up in the sort string, but that's ok.) + \def\arrow{->}% + \def\bullet{bullet}% + \def\comma{,}% + \def\copyright{copyright}% + \def\dots{...}% + \def\enddots{...}% + \def\equiv{==}% + \def\error{error}% + \def\euro{euro}% + \def\expansion{==>}% + \def\geq{>=}% + \def\guillemetleft{<<}% + \def\guillemetright{>>}% + \def\guilsinglleft{<}% + \def\guilsinglright{>}% + \def\leq{<=}% + \def\minus{-}% + \def\point{.}% + \def\pounds{pounds}% + \def\print{-|}% + \def\quotedblbase{"}% + \def\quotedblleft{"}% + \def\quotedblright{"}% + \def\quoteleft{`}% + \def\quoteright{'}% + \def\quotesinglbase{,}% + \def\registeredsymbol{R}% + \def\result{=>}% + \def\textdegree{o}% + % + % We need to get rid of all macros, leaving only the arguments (if present). + % Of course this is not nearly correct, but it is the best we can do for now. + % makeinfo does not expand macros in the argument to @deffn, which ends up + % writing an index entry, and texindex isn't prepared for an index sort entry + % that starts with \. + % + % Since macro invocations are followed by braces, we can just redefine them + % to take a single TeX argument. The case of a macro invocation that + % goes to end-of-line is not handled. + % + \macrolist +} + + +\let\SETmarginindex=\relax % put index entries in margin (undocumented)? + +% Most index entries go through here, but \dosubind is the general case. +% #1 is the index name, #2 is the entry text. +\def\doind#1#2{\dosubind{#1}{#2}{}} + +% There is also \dosubind {index}{topic}{subtopic} +% which makes an entry in a two-level index such as the operation index. +% TODO: Two-level index? Operation index? + +% Workhorse for all indexes. +% #1 is name of index, #2 is stuff to put there, #3 is subentry -- +% empty if called from \doind, as we usually are (the main exception +% is with most defuns, which call us directly). +% +\def\dosubind#1#2#3{% + \iflinks + {% + \requireopenindexfile{#1}% + % Store the main index entry text (including the third arg). + \toks0 = {#2}% + % If third arg is present, precede it with a space. + \def\thirdarg{#3}% + \ifx\thirdarg\empty \else + \toks0 = \expandafter{\the\toks0 \space #3}% + \fi + % + \edef\writeto{\csname#1indfile\endcsname}% + % + \safewhatsit\dosubindwrite + }% + \fi +} + +% Check if an index file has been opened, and if not, open it. +\def\requireopenindexfile#1{% +\ifnum\csname #1indfile\endcsname=0 + \expandafter\newwrite \csname#1indfile\endcsname + \edef\suffix{#1}% + % A .fls suffix would conflict with the file extension for the output + % of -recorder, so use .f1s instead. + \ifx\suffix\indexisfl\def\suffix{f1}\fi + % Open the file + \immediate\openout\csname#1indfile\endcsname \jobname.\suffix + % Using \immediate here prevents an object entering into the current box, + % which could confound checks such as those in \safewhatsit for preceding + % skips. +\fi} +\def\indexisfl{fl} + +% Output \ as {\indexbackslash}, because \ is an escape character in +% the index files. +\let\indexbackslash=\relax +{\catcode`\@=0 \catcode`\\=\active + @gdef@useindexbackslash{@def\{{@indexbackslash}}} +} + +% Definition for writing index entry text. +\def\sortas#1{\ignorespaces}% + +% Definition for writing index entry sort key. Should occur at the at +% the beginning of the index entry, like +% @cindex @sortas{september} \september +% The \ignorespaces takes care of following space, but there's no way +% to remove space before it. +{ +\catcode`\-=13 +\gdef\indexwritesortas{% + \begingroup + \indexnonalnumreappear + \indexwritesortasxxx} +\gdef\indexwritesortasxxx#1{% + \xdef\indexsortkey{#1}\endgroup} +} + + +% Write the entry in \toks0 to the index file. +% +\def\dosubindwrite{% + % Put the index entry in the margin if desired. + \ifx\SETmarginindex\relax\else + \insert\margin{\hbox{\vrule height8pt depth3pt width0pt \the\toks0}}% + \fi + % + % Remember, we are within a group. + \indexdummies % Must do this here, since \bf, etc expand at this stage + \useindexbackslash % \indexbackslash isn't defined now so it will be output + % as is; and it will print as backslash. + % The braces around \indexbrace are recognized by texindex. + % + % Get the string to sort by, by processing the index entry with all + % font commands turned off. + {\indexnofonts + \def\lbracechar{{\indexlbrace}}% + \def\rbracechar{{\indexrbrace}}% + \let\{=\lbracechar + \let\}=\rbracechar + \indexnonalnumdisappear + \xdef\indexsortkey{}% + \let\sortas=\indexwritesortas + \edef\temp{\the\toks0}% + \setbox\dummybox = \hbox{\temp}% Make sure to execute any \sortas + \ifx\indexsortkey\empty + \xdef\indexsortkey{\temp}% + \ifx\indexsortkey\empty\xdef\indexsortkey{ }\fi + \fi + }% + % + % Set up the complete index entry, with both the sort key and + % the original text, including any font commands. We write + % three arguments to \entry to the .?? file (four in the + % subentry case), texindex reduces to two when writing the .??s + % sorted result. + \edef\temp{% + \write\writeto{% + \string\entry{\indexsortkey}{\noexpand\folio}{\the\toks0}}% + }% + \temp +} +\newbox\dummybox % used above + +% Take care of unwanted page breaks/skips around a whatsit: +% +% If a skip is the last thing on the list now, preserve it +% by backing up by \lastskip, doing the \write, then inserting +% the skip again. Otherwise, the whatsit generated by the +% \write or \pdfdest will make \lastskip zero. The result is that +% sequences like this: +% @end defun +% @tindex whatever +% @defun ... +% will have extra space inserted, because the \medbreak in the +% start of the @defun won't see the skip inserted by the @end of +% the previous defun. +% +% But don't do any of this if we're not in vertical mode. We +% don't want to do a \vskip and prematurely end a paragraph. +% +% Avoid page breaks due to these extra skips, too. +% +% But wait, there is a catch there: +% We'll have to check whether \lastskip is zero skip. \ifdim is not +% sufficient for this purpose, as it ignores stretch and shrink parts +% of the skip. The only way seems to be to check the textual +% representation of the skip. +% +% The following is almost like \def\zeroskipmacro{0.0pt} except that +% the ``p'' and ``t'' characters have catcode \other, not 11 (letter). +% +\edef\zeroskipmacro{\expandafter\the\csname z@skip\endcsname} +% +\newskip\whatsitskip +\newcount\whatsitpenalty +% +% ..., ready, GO: +% +\def\safewhatsit#1{\ifhmode + #1% + \else + % \lastskip and \lastpenalty cannot both be nonzero simultaneously. + \whatsitskip = \lastskip + \edef\lastskipmacro{\the\lastskip}% + \whatsitpenalty = \lastpenalty + % + % If \lastskip is nonzero, that means the last item was a + % skip. And since a skip is discardable, that means this + % -\whatsitskip glue we're inserting is preceded by a + % non-discardable item, therefore it is not a potential + % breakpoint, therefore no \nobreak needed. + \ifx\lastskipmacro\zeroskipmacro + \else + \vskip-\whatsitskip + \fi + % + #1% + % + \ifx\lastskipmacro\zeroskipmacro + % If \lastskip was zero, perhaps the last item was a penalty, and + % perhaps it was >=10000, e.g., a \nobreak. In that case, we want + % to re-insert the same penalty (values >10000 are used for various + % signals); since we just inserted a non-discardable item, any + % following glue (such as a \parskip) would be a breakpoint. For example: + % @deffn deffn-whatever + % @vindex index-whatever + % Description. + % would allow a break between the index-whatever whatsit + % and the "Description." paragraph. + \ifnum\whatsitpenalty>9999 \penalty\whatsitpenalty \fi + \else + % On the other hand, if we had a nonzero \lastskip, + % this make-up glue would be preceded by a non-discardable item + % (the whatsit from the \write), so we must insert a \nobreak. + \nobreak\vskip\whatsitskip + \fi +\fi} + +% The index entry written in the file actually looks like +% \entry {sortstring}{page}{topic} +% or +% \entry {sortstring}{page}{topic}{subtopic} +% The texindex program reads in these files and writes files +% containing these kinds of lines: +% \initial {c} +% before the first topic whose initial is c +% \entry {topic}{pagelist} +% for a topic that is used without subtopics +% \primary {topic} +% for the beginning of a topic that is used with subtopics +% \secondary {subtopic}{pagelist} +% for each subtopic. + +% Define the user-accessible indexing commands +% @findex, @vindex, @kindex, @cindex. + +\def\findex {\fnindex} +\def\kindex {\kyindex} +\def\cindex {\cpindex} +\def\vindex {\vrindex} +\def\tindex {\tpindex} +\def\pindex {\pgindex} + +\def\cindexsub {\begingroup\obeylines\cindexsub} +{\obeylines % +\gdef\cindexsub "#1" #2^^M{\endgroup % +\dosubind{cp}{#2}{#1}}} + +% Define the macros used in formatting output of the sorted index material. + +% @printindex causes a particular index (the ??s file) to get printed. +% It does not print any chapter heading (usually an @unnumbered). +% +\parseargdef\printindex{\begingroup + \dobreak \chapheadingskip{10000}% + % + \smallfonts \rm + \tolerance = 9500 + \plainfrenchspacing + \everypar = {}% don't want the \kern\-parindent from indentation suppression. + % + % See if the index file exists and is nonempty. + % Change catcode of @ here so that if the index file contains + % \initial {@} + % as its first line, TeX doesn't complain about mismatched braces + % (because it thinks @} is a control sequence). + \catcode`\@ = 11 + % See comment in \requireopenindexfile. + \def\indexname{#1}\ifx\indexname\indexisfl\def\indexname{f1}\fi + \openin 1 \jobname.\indexname s + \ifeof 1 + % \enddoublecolumns gets confused if there is no text in the index, + % and it loses the chapter title and the aux file entries for the + % index. The easiest way to prevent this problem is to make sure + % there is some text. + \putwordIndexNonexistent + \else + \catcode`\\ = 0 + \escapechar = `\\ + % + % If the index file exists but is empty, then \openin leaves \ifeof + % false. We have to make TeX try to read something from the file, so + % it can discover if there is anything in it. + \read 1 to \thisline + \ifeof 1 + \putwordIndexIsEmpty + \else + % Index files are almost Texinfo source, but we use \ as the escape + % character. It would be better to use @, but that's too big a change + % to make right now. + \def\indexbackslash{\ttbackslash}% + \let\indexlbrace\{ % Likewise, set these sequences for braces + \let\indexrbrace\} % used in the sort key. + \begindoublecolumns + \let\entryorphanpenalty=\indexorphanpenalty + % + % Read input from the index file line by line. + \loopdo + \ifeof1 + \let\firsttoken\relax + \else + \read 1 to \nextline + \edef\act{\gdef\noexpand\firsttoken{\getfirsttoken\nextline}}% + \act + \fi + \thisline + % + \ifeof1\else + \let\thisline\nextline + \repeat + %% + \enddoublecolumns + \fi + \fi + \closein 1 +\endgroup} + +\def\getfirsttoken#1{\expandafter\getfirsttokenx#1\endfirsttoken} +\long\def\getfirsttokenx#1#2\endfirsttoken{\noexpand#1} + +\def\loopdo#1\repeat{\def\body{#1}\loopdoxxx} +\def\loopdoxxx{\let\next=\relax\body\let\next=\loopdoxxx\fi\next} + +% These macros are used by the sorted index file itself. +% Change them to control the appearance of the index. + +{\catcode`\/=13 \catcode`\-=13 \catcode`\^=13 \catcode`\~=13 \catcode`\_=13 +\catcode`\|=13 \catcode`\<=13 \catcode`\>=13 \catcode`\+=13 \catcode`\"=13 +\catcode`\$=3 +\gdef\initialglyphs{% + % Some changes for non-alphabetic characters. Using the glyphs from the + % math fonts looks more consistent than the typewriter font used elsewhere + % for these characters. + \def\indexbackslash{\math{\backslash}}% + \let\\=\indexbackslash + % + % Can't get bold backslash so don't use bold forward slash + \catcode`\/=13 + \def/{{\secrmnotbold \normalslash}}% + \def-{{\normaldash\normaldash}}% en dash `--' + \def^{{\chapbf \normalcaret}}% + \def~{{\chapbf \normaltilde}}% + \def\_{% + \leavevmode \kern.07em \vbox{\hrule width.3em height.1ex}\kern .07em }% + \def|{$\vert$}% + \def<{$\less$}% + \def>{$\gtr$}% + \def+{$\normalplus$}% +}} + +\def\initial{% + \bgroup + \initialglyphs + \initialx +} + +\def\initialx#1{% + % Remove any glue we may have, we'll be inserting our own. + \removelastskip + % + % We like breaks before the index initials, so insert a bonus. + % The glue before the bonus allows a little bit of space at the + % bottom of a column to reduce an increase in inter-line spacing. + \nobreak + \vskip 0pt plus 5\baselineskip + \penalty -300 + \vskip 0pt plus -5\baselineskip + % + % Typeset the initial. Making this add up to a whole number of + % baselineskips increases the chance of the dots lining up from column + % to column. It still won't often be perfect, because of the stretch + % we need before each entry, but it's better. + % + % No shrink because it confuses \balancecolumns. + \vskip 1.67\baselineskip plus 1\baselineskip + \leftline{\secfonts \kern-0.05em \secbf #1}% + % \secfonts is inside the argument of \leftline so that the change of + % \baselineskip will not affect any glue inserted before the vbox that + % \leftline creates. + % Do our best not to break after the initial. + \nobreak + \vskip .33\baselineskip plus .1\baselineskip + \egroup % \initialglyphs +} + +\newdimen\entryrightmargin +\entryrightmargin=0pt + +% \entry typesets a paragraph consisting of the text (#1), dot leaders, and +% then page number (#2) flushed to the right margin. It is used for index +% and table of contents entries. The paragraph is indented by \leftskip. +% +\def\entry{% + \begingroup + % + % Start a new paragraph if necessary, so our assignments below can't + % affect previous text. + \par + % + % No extra space above this paragraph. + \parskip = 0in + % + % When reading the text of entry, convert explicit line breaks + % from @* into spaces. The user might give these in long section + % titles, for instance. + \def\*{\unskip\space\ignorespaces}% + \def\entrybreak{\hfil\break}% An undocumented command + % + % A bit of stretch before each entry for the benefit of balancing + % columns. + \vskip 0pt plus0.5pt + % + % Swallow the left brace of the text (first parameter): + \afterassignment\doentry + \let\temp = +} +\def\entrybreak{\unskip\space\ignorespaces}% +\def\doentry{% + % Save the text of the entry + \global\setbox\boxA=\hbox\bgroup + \bgroup % Instead of the swallowed brace. + \noindent + \aftergroup\finishentry + % And now comes the text of the entry. + % Not absorbing as a macro argument reduces the chance of problems + % with catcodes occurring. +} +{\catcode`\@=11 +\gdef\finishentry#1{% + \egroup % end box A + \dimen@ = \wd\boxA % Length of text of entry + \global\setbox\boxA=\hbox\bgroup\unhbox\boxA + % #1 is the page number. + % + % Get the width of the page numbers, and only use + % leaders if they are present. + \global\setbox\boxB = \hbox{#1}% + \ifdim\wd\boxB = 0pt + \null\nobreak\hfill\ % + \else + % + \null\nobreak\indexdotfill % Have leaders before the page number. + % + \ifpdf + \pdfgettoks#1.% + \bgroup\let\domark\relax + \hskip\skip\thinshrinkable\the\toksA + \egroup + % The redefinion of \domark stops marks being added in \pdflink to + % preserve coloured links across page boundaries. Otherwise the marks + % would get in the way of \lastbox in \insertindexentrybox. + \else + \hskip\skip\thinshrinkable #1% + \fi + \fi + \egroup % end \boxA + \ifdim\wd\boxB = 0pt + \global\setbox\entryindexbox=\vbox{\unhbox\boxA}% + \else + \global\setbox\entryindexbox=\vbox\bgroup + \prevdepth=\entrylinedepth + \noindent + % We want the text of the entries to be aligned to the left, and the + % page numbers to be aligned to the right. + % + \advance\leftskip by 0pt plus 1fil + \advance\leftskip by 0pt plus -1fill + \rightskip = 0pt plus -1fil + \advance\rightskip by 0pt plus 1fill + % Cause last line, which could consist of page numbers on their own + % if the list of page numbers is long, to be aligned to the right. + \parfillskip=0pt plus -1fill + % + \hangindent=1em + % + \advance\rightskip by \entryrightmargin + % Determine how far we can stretch into the margin. + % This allows, e.g., "Appendix H GNU Free Documentation License" to + % fit on one line in @letterpaper format. + \ifdim\entryrightmargin>2.1em + \dimen@i=2.1em + \else + \dimen@i=0em + \fi + \advance \parfillskip by 0pt minus 1\dimen@i + % + \dimen@ii = \hsize + \advance\dimen@ii by -1\leftskip + \advance\dimen@ii by -1\entryrightmargin + \advance\dimen@ii by 1\dimen@i + \ifdim\wd\boxA > \dimen@ii % If the entry doesn't fit in one line + \ifdim\dimen@ > 0.8\dimen@ii % due to long index text + \dimen@ = 0.7\dimen@ % Try to split the text roughly evenly + \dimen@ii = \hsize + \advance \dimen@ii by -1em + \ifnum\dimen@>\dimen@ii + % If the entry is too long, use the whole line + \dimen@ = \dimen@ii + \fi + \advance\leftskip by 0pt plus 1fill % ragged right + \advance \dimen@ by 1\rightskip + \parshape = 2 0pt \dimen@ 1em \dimen@ii + % Ideally we'd add a finite glue at the end of the first line only, but + % TeX doesn't seem to provide a way to do such a thing. + \fi\fi + \unhbox\boxA + % + % Do not prefer a separate line ending with a hyphen to fewer lines. + \finalhyphendemerits = 0 + % + % Word spacing - no stretch + \spaceskip=\fontdimen2\font minus \fontdimen4\font + % + \linepenalty=1000 % Discourage line breaks. + \hyphenpenalty=5000 % Discourage hyphenation. + % + \par % format the paragraph + \egroup % The \vbox + \fi + \endgroup + % delay text of entry until after penalty + \bgroup\aftergroup\insertindexentrybox + \entryorphanpenalty +}} + +\newskip\thinshrinkable +\skip\thinshrinkable=.15em minus .15em + +\newbox\entryindexbox +\def\insertindexentrybox{% + \copy\entryindexbox + % The following gets the depth of the last box. This is for even + % line spacing when entries span several lines. + \setbox\dummybox\vbox{% + \unvbox\entryindexbox + \nointerlineskip + \lastbox + \global\entrylinedepth=\prevdepth + }% + % Note that we couldn't simply \unvbox\entryindexbox followed by + % \nointerlineskip\lastbox to remove the last box and then reinstate it, + % because this resets how far the box has been \moveleft'ed to 0. \unvbox + % doesn't affect \prevdepth either. +} +\newdimen\entrylinedepth + +% Default is no penalty +\let\entryorphanpenalty\egroup + +% Used from \printindex. \firsttoken should be the first token +% after the \entry. If it's not another \entry, we are at the last +% line of a group of index entries, so insert a penalty to discourage +% orphaned index entries. +\long\def\indexorphanpenalty{% + \def\isentry{\entry}% + \ifx\firsttoken\isentry + \else + \unskip\penalty 9000 + % The \unskip here stops breaking before the glue. It relies on the + % \vskip above being there, otherwise there is an error + % "You can't use `\unskip' in vertical mode". There has to be glue + % in the current vertical list that hasn't been added to the + % "current page". See Chapter 24 of the TeXbook. This contradicts + % Section 8.3.7 in "TeX by Topic," though. + \fi + \egroup % now comes the box added with \aftergroup +} + +% Like plain.tex's \dotfill, except uses up at least 1 em. +% The filll stretch here overpowers both the fil and fill stretch to push +% the page number to the right. +\def\indexdotfill{\cleaders + \hbox{$\mathsurround=0pt \mkern1.5mu.\mkern1.5mu$}\hskip 1em plus 1filll} + + +\def\primary #1{\line{#1\hfil}} + +\newskip\secondaryindent \secondaryindent=0.5cm +\def\secondary#1#2{{% + \parfillskip=0in + \parskip=0in + \hangindent=1in + \hangafter=1 + \noindent\hskip\secondaryindent\hbox{#1}\indexdotfill + \ifpdf + \pdfgettoks#2.\ \the\toksA % The page number ends the paragraph. + \else + #2 + \fi + \par +}} + +% Define two-column mode, which we use to typeset indexes. +% Adapted from the TeXbook, page 416, which is to say, +% the manmac.tex format used to print the TeXbook itself. +\catcode`\@=11 % private names + +\newbox\partialpage +\newdimen\doublecolumnhsize +\newdimen\doublecolumntopgap +\doublecolumntopgap = 0pt + +% Use inside an output routine to save \topmark and \firstmark +\def\savemarks{% + \global\savedtopmark=\expandafter{\topmark }% + \global\savedfirstmark=\expandafter{\firstmark }% +} +\newtoks\savedtopmark +\newtoks\savedfirstmark + +% Set \topmark and \firstmark for next time \output runs. +% Can't be run from withinside \output (because any material +% added while an output routine is active, including +% penalties, is saved for after it finishes). The page so far +% should be empty, otherwise what's on it will be thrown away. +\def\restoremarks{% + \mark{\the\savedtopmark}% + \bgroup\output = {% + \setbox\dummybox=\box\PAGE + }abc\eject\egroup + % "abc" because output routine doesn't fire for a completely empty page. + \mark{\the\savedfirstmark}% +} + +\def\begindoublecolumns{\begingroup % ended by \enddoublecolumns + % If not much space left on page, start a new page. + \ifdim\pagetotal>0.8\vsize\vfill\eject\fi + % + % Grab any single-column material above us. + \output = {% + % + % Here is a possibility not foreseen in manmac: if we accumulate a + % whole lot of material, we might end up calling this \output + % routine twice in a row (see the doublecol-lose test, which is + % essentially a couple of indexes with @setchapternewpage off). In + % that case we just ship out what is in \partialpage with the normal + % output routine. Generally, \partialpage will be empty when this + % runs and this will be a no-op. See the indexspread.tex test case. + \ifvoid\partialpage \else + \onepageout{\pagecontents\partialpage}% + \fi + % + \global\setbox\partialpage = \vbox{% + % Unvbox the main output page. + \unvbox\PAGE + \kern-\topskip \kern\baselineskip + }% + \savemarks + }% + \eject % run that output routine to set \partialpage + \restoremarks + % + % We recover the two marks that the last output routine saved in order + % to propagate the information in marks added around a chapter heading, + % which could be otherwise be lost by the time the final page is output. + % + % + % Use the double-column output routine for subsequent pages. + \output = {\doublecolumnout}% + % + % Change the page size parameters. We could do this once outside this + % routine, in each of @smallbook, @afourpaper, and the default 8.5x11 + % format, but then we repeat the same computation. Repeating a couple + % of assignments once per index is clearly meaningless for the + % execution time, so we may as well do it in one place. + % + % First we halve the line length, less a little for the gutter between + % the columns. We compute the gutter based on the line length, so it + % changes automatically with the paper format. The magic constant + % below is chosen so that the gutter has the same value (well, +-<1pt) + % as it did when we hard-coded it. + % + % We put the result in a separate register, \doublecolumhsize, so we + % can restore it in \pagesofar, after \hsize itself has (potentially) + % been clobbered. + % + \doublecolumnhsize = \hsize + \advance\doublecolumnhsize by -.04154\hsize + \divide\doublecolumnhsize by 2 + \hsize = \doublecolumnhsize + % + % Double the \vsize as well. (We don't need a separate register here, + % since nobody clobbers \vsize.) + \global\doublecolumntopgap = \topskip + \global\advance\doublecolumntopgap by -1\baselineskip + \advance\vsize by -1\doublecolumntopgap + \vsize = 2\vsize + \topskip=0pt + \global\entrylinedepth=0pt\relax +} + +% The double-column output routine for all double-column pages except +% the last, which is done by \balancecolumns. +% +\def\doublecolumnout{% + % + \splittopskip=\topskip \splitmaxdepth=\maxdepth + % Get the available space for the double columns -- the normal + % (undoubled) page height minus any material left over from the + % previous page. + \dimen@ = \vsize + \divide\dimen@ by 2 + \advance\dimen@ by -\ht\partialpage + % + % box0 will be the left-hand column, box2 the right. + \setbox0=\vsplit255 to\dimen@ \setbox2=\vsplit255 to\dimen@ + \onepageout\pagesofar + \unvbox255 + \penalty\outputpenalty +} +% +% Re-output the contents of the output page -- any previous material, +% followed by the two boxes we just split, in box0 and box2. +\def\pagesofar{% + \unvbox\partialpage + % + \hsize = \doublecolumnhsize + \wd0=\hsize \wd2=\hsize + \vbox{% + \vskip\doublecolumntopgap + \hbox to\pagewidth{\box0\hfil\box2}}% +} + + +% Finished with with double columns. +\def\enddoublecolumns{% + % The following penalty ensures that the page builder is exercised + % _before_ we change the output routine. This is necessary in the + % following situation: + % + % The last section of the index consists only of a single entry. + % Before this section, \pagetotal is less than \pagegoal, so no + % break occurs before the last section starts. However, the last + % section, consisting of \initial and the single \entry, does not + % fit on the page and has to be broken off. Without the following + % penalty the page builder will not be exercised until \eject + % below, and by that time we'll already have changed the output + % routine to the \balancecolumns version, so the next-to-last + % double-column page will be processed with \balancecolumns, which + % is wrong: The two columns will go to the main vertical list, with + % the broken-off section in the recent contributions. As soon as + % the output routine finishes, TeX starts reconsidering the page + % break. The two columns and the broken-off section both fit on the + % page, because the two columns now take up only half of the page + % goal. When TeX sees \eject from below which follows the final + % section, it invokes the new output routine that we've set after + % \balancecolumns below; \onepageout will try to fit the two columns + % and the final section into the vbox of \pageheight (see + % \pagebody), causing an overfull box. + % + % Note that glue won't work here, because glue does not exercise the + % page builder, unlike penalties (see The TeXbook, pp. 280-281). + \penalty0 + % + \output = {% + % Split the last of the double-column material. + \savemarks + \balancecolumns + % + % Having called \balancecolumns once, we do not + % want to call it again. Therefore, reset \output to its normal + % definition right away. + \global\output = {\onepageout{\pagecontents\PAGE}}% + }% + \eject + \endgroup % started in \begindoublecolumns + \restoremarks + % Leave the double-column material on the current page, no automatic + % page break. + \box\balancedcolumns + % + % \pagegoal was set to the doubled \vsize above, since we restarted + % the current page. We're now back to normal single-column + % typesetting, so reset \pagegoal to the normal \vsize (after the + % \endgroup where \vsize got restored). + \pagegoal = \vsize +} +\newbox\balancedcolumns +\setbox\balancedcolumns=\vbox{shouldnt see this}% +% +% Only called for the last of the double column material. \doublecolumnout +% does the others. +\def\balancecolumns{% + \setbox0 = \vbox{\unvbox255}% like \box255 but more efficient, see p.120. + \dimen@ = \ht0 + \advance\dimen@ by \topskip + \advance\dimen@ by-\baselineskip + \ifdim\dimen@<14\baselineskip + % Don't split a short final column in two. + \setbox2=\vbox{}% + \else + \divide\dimen@ by 2 % target to split to + \dimen@ii = \dimen@ + \splittopskip = \topskip + % Loop until the second column is no higher than the first + {% + \vbadness = 10000 + \loop + \global\setbox3 = \copy0 + \global\setbox1 = \vsplit3 to \dimen@ + % Remove glue from bottom of first column to + % make sure it is higher than the second. + \global\setbox1 = \vbox{\unvbox1\unpenalty\unskip}% + \ifdim\ht3>\ht1 + \global\advance\dimen@ by 1pt + \repeat + }% + \multiply\dimen@ii by 4 + \divide\dimen@ii by 5 + \ifdim\ht3<\dimen@ii + % Column heights are too different, so don't make their bottoms + % flush with each other. The glue at the end of the second column + % allows a second column to stretch, reducing the difference in + % height between the two. + \setbox0=\vbox to\dimen@{\unvbox1\vfill}% + \setbox2=\vbox to\dimen@{\unvbox3\vskip 0pt plus 0.3\ht0}% + \else + \setbox0=\vbox to\dimen@{\unvbox1}% + \setbox2=\vbox to\dimen@{\unvbox3}% + \fi + \fi + % + \global\setbox\balancedcolumns=\vbox{\pagesofar}% +} +\catcode`\@ = \other + + +\message{sectioning,} +% Chapters, sections, etc. + +% Let's start with @part. +\outer\parseargdef\part{\partzzz{#1}} +\def\partzzz#1{% + \chapoddpage + \null + \vskip.3\vsize % move it down on the page a bit + \begingroup + \noindent \titlefonts\rmisbold #1\par % the text + \let\lastnode=\empty % no node to associate with + \writetocentry{part}{#1}{}% but put it in the toc + \headingsoff % no headline or footline on the part page + % This outputs a mark at the end of the page that clears \thischapter + % and \thissection, as is done in \startcontents. + \let\pchapsepmacro\relax + \chapmacro{}{Yomitfromtoc}{}% + \chapoddpage + \endgroup +} + +% \unnumberedno is an oxymoron. But we count the unnumbered +% sections so that we can refer to them unambiguously in the pdf +% outlines by their "section number". We avoid collisions with chapter +% numbers by starting them at 10000. (If a document ever has 10000 +% chapters, we're in trouble anyway, I'm sure.) +\newcount\unnumberedno \unnumberedno = 10000 +\newcount\chapno +\newcount\secno \secno=0 +\newcount\subsecno \subsecno=0 +\newcount\subsubsecno \subsubsecno=0 + +% This counter is funny since it counts through charcodes of letters A, B, ... +\newcount\appendixno \appendixno = `\@ +% +% \def\appendixletter{\char\the\appendixno} +% We do the following ugly conditional instead of the above simple +% construct for the sake of pdftex, which needs the actual +% letter in the expansion, not just typeset. +% +\def\appendixletter{% + \ifnum\appendixno=`A A% + \else\ifnum\appendixno=`B B% + \else\ifnum\appendixno=`C C% + \else\ifnum\appendixno=`D D% + \else\ifnum\appendixno=`E E% + \else\ifnum\appendixno=`F F% + \else\ifnum\appendixno=`G G% + \else\ifnum\appendixno=`H H% + \else\ifnum\appendixno=`I I% + \else\ifnum\appendixno=`J J% + \else\ifnum\appendixno=`K K% + \else\ifnum\appendixno=`L L% + \else\ifnum\appendixno=`M M% + \else\ifnum\appendixno=`N N% + \else\ifnum\appendixno=`O O% + \else\ifnum\appendixno=`P P% + \else\ifnum\appendixno=`Q Q% + \else\ifnum\appendixno=`R R% + \else\ifnum\appendixno=`S S% + \else\ifnum\appendixno=`T T% + \else\ifnum\appendixno=`U U% + \else\ifnum\appendixno=`V V% + \else\ifnum\appendixno=`W W% + \else\ifnum\appendixno=`X X% + \else\ifnum\appendixno=`Y Y% + \else\ifnum\appendixno=`Z Z% + % The \the is necessary, despite appearances, because \appendixletter is + % expanded while writing the .toc file. \char\appendixno is not + % expandable, thus it is written literally, thus all appendixes come out + % with the same letter (or @) in the toc without it. + \else\char\the\appendixno + \fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi + \fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi} + +% Each @chapter defines these (using marks) as the number+name, number +% and name of the chapter. Page headings and footings can use +% these. @section does likewise. +\def\thischapter{} +\def\thischapternum{} +\def\thischaptername{} +\def\thissection{} +\def\thissectionnum{} +\def\thissectionname{} + +\newcount\absseclevel % used to calculate proper heading level +\newcount\secbase\secbase=0 % @raisesections/@lowersections modify this count + +% @raisesections: treat @section as chapter, @subsection as section, etc. +\def\raisesections{\global\advance\secbase by -1} +\let\up=\raisesections % original BFox name + +% @lowersections: treat @chapter as section, @section as subsection, etc. +\def\lowersections{\global\advance\secbase by 1} +\let\down=\lowersections % original BFox name + +% we only have subsub. +\chardef\maxseclevel = 3 +% +% A numbered section within an unnumbered changes to unnumbered too. +% To achieve this, remember the "biggest" unnum. sec. we are currently in: +\chardef\unnlevel = \maxseclevel +% +% Trace whether the current chapter is an appendix or not: +% \chapheadtype is "N" or "A", unnumbered chapters are ignored. +\def\chapheadtype{N} + +% Choose a heading macro +% #1 is heading type +% #2 is heading level +% #3 is text for heading +\def\genhead#1#2#3{% + % Compute the abs. sec. level: + \absseclevel=#2 + \advance\absseclevel by \secbase + % Make sure \absseclevel doesn't fall outside the range: + \ifnum \absseclevel < 0 + \absseclevel = 0 + \else + \ifnum \absseclevel > 3 + \absseclevel = 3 + \fi + \fi + % The heading type: + \def\headtype{#1}% + \if \headtype U% + \ifnum \absseclevel < \unnlevel + \chardef\unnlevel = \absseclevel + \fi + \else + % Check for appendix sections: + \ifnum \absseclevel = 0 + \edef\chapheadtype{\headtype}% + \else + \if \headtype A\if \chapheadtype N% + \errmessage{@appendix... within a non-appendix chapter}% + \fi\fi + \fi + % Check for numbered within unnumbered: + \ifnum \absseclevel > \unnlevel + \def\headtype{U}% + \else + \chardef\unnlevel = 3 + \fi + \fi + % Now print the heading: + \if \headtype U% + \ifcase\absseclevel + \unnumberedzzz{#3}% + \or \unnumberedseczzz{#3}% + \or \unnumberedsubseczzz{#3}% + \or \unnumberedsubsubseczzz{#3}% + \fi + \else + \if \headtype A% + \ifcase\absseclevel + \appendixzzz{#3}% + \or \appendixsectionzzz{#3}% + \or \appendixsubseczzz{#3}% + \or \appendixsubsubseczzz{#3}% + \fi + \else + \ifcase\absseclevel + \chapterzzz{#3}% + \or \seczzz{#3}% + \or \numberedsubseczzz{#3}% + \or \numberedsubsubseczzz{#3}% + \fi + \fi + \fi + \suppressfirstparagraphindent +} + +% an interface: +\def\numhead{\genhead N} +\def\apphead{\genhead A} +\def\unnmhead{\genhead U} + +% @chapter, @appendix, @unnumbered. Increment top-level counter, reset +% all lower-level sectioning counters to zero. +% +% Also set \chaplevelprefix, which we prepend to @float sequence numbers +% (e.g., figures), q.v. By default (before any chapter), that is empty. +\let\chaplevelprefix = \empty +% +\outer\parseargdef\chapter{\numhead0{#1}} % normally numhead0 calls chapterzzz +\def\chapterzzz#1{% + % section resetting is \global in case the chapter is in a group, such + % as an @include file. + \global\secno=0 \global\subsecno=0 \global\subsubsecno=0 + \global\advance\chapno by 1 + % + % Used for \float. + \gdef\chaplevelprefix{\the\chapno.}% + \resetallfloatnos + % + % \putwordChapter can contain complex things in translations. + \toks0=\expandafter{\putwordChapter}% + \message{\the\toks0 \space \the\chapno}% + % + % Write the actual heading. + \chapmacro{#1}{Ynumbered}{\the\chapno}% + % + % So @section and the like are numbered underneath this chapter. + \global\let\section = \numberedsec + \global\let\subsection = \numberedsubsec + \global\let\subsubsection = \numberedsubsubsec +} + +\outer\parseargdef\appendix{\apphead0{#1}} % normally calls appendixzzz +% +\def\appendixzzz#1{% + \global\secno=0 \global\subsecno=0 \global\subsubsecno=0 + \global\advance\appendixno by 1 + \gdef\chaplevelprefix{\appendixletter.}% + \resetallfloatnos + % + % \putwordAppendix can contain complex things in translations. + \toks0=\expandafter{\putwordAppendix}% + \message{\the\toks0 \space \appendixletter}% + % + \chapmacro{#1}{Yappendix}{\appendixletter}% + % + \global\let\section = \appendixsec + \global\let\subsection = \appendixsubsec + \global\let\subsubsection = \appendixsubsubsec +} + +% normally unnmhead0 calls unnumberedzzz: +\outer\parseargdef\unnumbered{\unnmhead0{#1}} +\def\unnumberedzzz#1{% + \global\secno=0 \global\subsecno=0 \global\subsubsecno=0 + \global\advance\unnumberedno by 1 + % + % Since an unnumbered has no number, no prefix for figures. + \global\let\chaplevelprefix = \empty + \resetallfloatnos + % + % This used to be simply \message{#1}, but TeX fully expands the + % argument to \message. Therefore, if #1 contained @-commands, TeX + % expanded them. For example, in `@unnumbered The @cite{Book}', TeX + % expanded @cite (which turns out to cause errors because \cite is meant + % to be executed, not expanded). + % + % Anyway, we don't want the fully-expanded definition of @cite to appear + % as a result of the \message, we just want `@cite' itself. We use + % \the to achieve this: TeX expands \the only once, + % simply yielding the contents of . (We also do this for + % the toc entries.) + \toks0 = {#1}% + \message{(\the\toks0)}% + % + \chapmacro{#1}{Ynothing}{\the\unnumberedno}% + % + \global\let\section = \unnumberedsec + \global\let\subsection = \unnumberedsubsec + \global\let\subsubsection = \unnumberedsubsubsec +} + +% @centerchap is like @unnumbered, but the heading is centered. +\outer\parseargdef\centerchap{% + \let\centerparametersmaybe = \centerparameters + \unnmhead0{#1}% + \let\centerparametersmaybe = \relax +} + +% @top is like @unnumbered. +\let\top\unnumbered + +% Sections. +% +\outer\parseargdef\numberedsec{\numhead1{#1}} % normally calls seczzz +\def\seczzz#1{% + \global\subsecno=0 \global\subsubsecno=0 \global\advance\secno by 1 + \sectionheading{#1}{sec}{Ynumbered}{\the\chapno.\the\secno}% +} + +% normally calls appendixsectionzzz: +\outer\parseargdef\appendixsection{\apphead1{#1}} +\def\appendixsectionzzz#1{% + \global\subsecno=0 \global\subsubsecno=0 \global\advance\secno by 1 + \sectionheading{#1}{sec}{Yappendix}{\appendixletter.\the\secno}% +} +\let\appendixsec\appendixsection + +% normally calls unnumberedseczzz: +\outer\parseargdef\unnumberedsec{\unnmhead1{#1}} +\def\unnumberedseczzz#1{% + \global\subsecno=0 \global\subsubsecno=0 \global\advance\secno by 1 + \sectionheading{#1}{sec}{Ynothing}{\the\unnumberedno.\the\secno}% +} + +% Subsections. +% +% normally calls numberedsubseczzz: +\outer\parseargdef\numberedsubsec{\numhead2{#1}} +\def\numberedsubseczzz#1{% + \global\subsubsecno=0 \global\advance\subsecno by 1 + \sectionheading{#1}{subsec}{Ynumbered}{\the\chapno.\the\secno.\the\subsecno}% +} + +% normally calls appendixsubseczzz: +\outer\parseargdef\appendixsubsec{\apphead2{#1}} +\def\appendixsubseczzz#1{% + \global\subsubsecno=0 \global\advance\subsecno by 1 + \sectionheading{#1}{subsec}{Yappendix}% + {\appendixletter.\the\secno.\the\subsecno}% +} + +% normally calls unnumberedsubseczzz: +\outer\parseargdef\unnumberedsubsec{\unnmhead2{#1}} +\def\unnumberedsubseczzz#1{% + \global\subsubsecno=0 \global\advance\subsecno by 1 + \sectionheading{#1}{subsec}{Ynothing}% + {\the\unnumberedno.\the\secno.\the\subsecno}% +} + +% Subsubsections. +% +% normally numberedsubsubseczzz: +\outer\parseargdef\numberedsubsubsec{\numhead3{#1}} +\def\numberedsubsubseczzz#1{% + \global\advance\subsubsecno by 1 + \sectionheading{#1}{subsubsec}{Ynumbered}% + {\the\chapno.\the\secno.\the\subsecno.\the\subsubsecno}% +} + +% normally appendixsubsubseczzz: +\outer\parseargdef\appendixsubsubsec{\apphead3{#1}} +\def\appendixsubsubseczzz#1{% + \global\advance\subsubsecno by 1 + \sectionheading{#1}{subsubsec}{Yappendix}% + {\appendixletter.\the\secno.\the\subsecno.\the\subsubsecno}% +} + +% normally unnumberedsubsubseczzz: +\outer\parseargdef\unnumberedsubsubsec{\unnmhead3{#1}} +\def\unnumberedsubsubseczzz#1{% + \global\advance\subsubsecno by 1 + \sectionheading{#1}{subsubsec}{Ynothing}% + {\the\unnumberedno.\the\secno.\the\subsecno.\the\subsubsecno}% +} + +% These macros control what the section commands do, according +% to what kind of chapter we are in (ordinary, appendix, or unnumbered). +% Define them by default for a numbered chapter. +\let\section = \numberedsec +\let\subsection = \numberedsubsec +\let\subsubsection = \numberedsubsubsec + +% Define @majorheading, @heading and @subheading + +\def\majorheading{% + {\advance\chapheadingskip by 10pt \chapbreak }% + \parsearg\chapheadingzzz +} + +\def\chapheading{\chapbreak \parsearg\chapheadingzzz} +\def\chapheadingzzz#1{% + \vbox{\chapfonts \raggedtitlesettings #1\par}% + \nobreak\bigskip \nobreak + \suppressfirstparagraphindent +} + +% @heading, @subheading, @subsubheading. +\parseargdef\heading{\sectionheading{#1}{sec}{Yomitfromtoc}{} + \suppressfirstparagraphindent} +\parseargdef\subheading{\sectionheading{#1}{subsec}{Yomitfromtoc}{} + \suppressfirstparagraphindent} +\parseargdef\subsubheading{\sectionheading{#1}{subsubsec}{Yomitfromtoc}{} + \suppressfirstparagraphindent} + +% These macros generate a chapter, section, etc. heading only +% (including whitespace, linebreaking, etc. around it), +% given all the information in convenient, parsed form. + +% Args are the skip and penalty (usually negative) +\def\dobreak#1#2{\par\ifdim\lastskip<#1\removelastskip\penalty#2\vskip#1\fi} + +% Parameter controlling skip before chapter headings (if needed) +\newskip\chapheadingskip + +% Define plain chapter starts, and page on/off switching for it. +\def\chapbreak{\dobreak \chapheadingskip {-4000}} + +% Start a new page +\def\chappager{\par\vfill\supereject} + +% \chapoddpage - start on an odd page for a new chapter +% Because \domark is called before \chapoddpage, the filler page will +% get the headings for the next chapter, which is wrong. But we don't +% care -- we just disable all headings on the filler page. +\def\chapoddpage{% + \chappager + \ifodd\pageno \else + \begingroup + \headingsoff + \null + \chappager + \endgroup + \fi +} + +\def\setchapternewpage #1 {\csname CHAPPAG#1\endcsname} + +\def\CHAPPAGoff{% +\global\let\contentsalignmacro = \chappager +\global\let\pchapsepmacro=\chapbreak +\global\let\pagealignmacro=\chappager} + +\def\CHAPPAGon{% +\global\let\contentsalignmacro = \chappager +\global\let\pchapsepmacro=\chappager +\global\let\pagealignmacro=\chappager +\global\def\HEADINGSon{\HEADINGSsingle}} + +\def\CHAPPAGodd{% +\global\let\contentsalignmacro = \chapoddpage +\global\let\pchapsepmacro=\chapoddpage +\global\let\pagealignmacro=\chapoddpage +\global\def\HEADINGSon{\HEADINGSdouble}} + +\CHAPPAGon + +% \chapmacro - Chapter opening. +% +% #1 is the text, #2 is the section type (Ynumbered, Ynothing, +% Yappendix, Yomitfromtoc), #3 the chapter number. +% Not used for @heading series. +% +% To test against our argument. +\def\Ynothingkeyword{Ynothing} +\def\Yappendixkeyword{Yappendix} +\def\Yomitfromtockeyword{Yomitfromtoc} +% +\def\chapmacro#1#2#3{% + \expandafter\ifx\thisenv\titlepage\else + \checkenv{}% chapters, etc., should not start inside an environment. + \fi + % FIXME: \chapmacro is currently called from inside \titlepage when + % \setcontentsaftertitlepage to print the "Table of Contents" heading, but + % this should probably be done by \sectionheading with an option to print + % in chapter size. + % + % Insert the first mark before the heading break (see notes for \domark). + \let\prevchapterdefs=\lastchapterdefs + \let\prevsectiondefs=\lastsectiondefs + \gdef\lastsectiondefs{\gdef\thissectionname{}\gdef\thissectionnum{}% + \gdef\thissection{}}% + % + \def\temptype{#2}% + \ifx\temptype\Ynothingkeyword + \gdef\lastchapterdefs{\gdef\thischaptername{#1}\gdef\thischapternum{}% + \gdef\thischapter{\thischaptername}}% + \else\ifx\temptype\Yomitfromtockeyword + \gdef\lastchapterdefs{\gdef\thischaptername{#1}\gdef\thischapternum{}% + \gdef\thischapter{}}% + \else\ifx\temptype\Yappendixkeyword + \toks0={#1}% + \xdef\lastchapterdefs{% + \gdef\noexpand\thischaptername{\the\toks0}% + \gdef\noexpand\thischapternum{\appendixletter}% + % \noexpand\putwordAppendix avoids expanding indigestible + % commands in some of the translations. + \gdef\noexpand\thischapter{\noexpand\putwordAppendix{} + \noexpand\thischapternum: + \noexpand\thischaptername}% + }% + \else + \toks0={#1}% + \xdef\lastchapterdefs{% + \gdef\noexpand\thischaptername{\the\toks0}% + \gdef\noexpand\thischapternum{\the\chapno}% + % \noexpand\putwordChapter avoids expanding indigestible + % commands in some of the translations. + \gdef\noexpand\thischapter{\noexpand\putwordChapter{} + \noexpand\thischapternum: + \noexpand\thischaptername}% + }% + \fi\fi\fi + % + % Output the mark. Pass it through \safewhatsit, to take care of + % the preceding space. + \safewhatsit\domark + % + % Insert the chapter heading break. + \pchapsepmacro + % + % Now the second mark, after the heading break. No break points + % between here and the heading. + \let\prevchapterdefs=\lastchapterdefs + \let\prevsectiondefs=\lastsectiondefs + \domark + % + {% + \chapfonts \rmisbold + \let\footnote=\errfootnoteheading % give better error message + % + % Have to define \lastsection before calling \donoderef, because the + % xref code eventually uses it. On the other hand, it has to be called + % after \pchapsepmacro, or the headline will change too soon. + \gdef\lastsection{#1}% + % + % Only insert the separating space if we have a chapter/appendix + % number, and don't print the unnumbered ``number''. + \ifx\temptype\Ynothingkeyword + \setbox0 = \hbox{}% + \def\toctype{unnchap}% + \else\ifx\temptype\Yomitfromtockeyword + \setbox0 = \hbox{}% contents like unnumbered, but no toc entry + \def\toctype{omit}% + \else\ifx\temptype\Yappendixkeyword + \setbox0 = \hbox{\putwordAppendix{} #3\enspace}% + \def\toctype{app}% + \else + \setbox0 = \hbox{#3\enspace}% + \def\toctype{numchap}% + \fi\fi\fi + % + % Write the toc entry for this chapter. Must come before the + % \donoderef, because we include the current node name in the toc + % entry, and \donoderef resets it to empty. + \writetocentry{\toctype}{#1}{#3}% + % + % For pdftex, we have to write out the node definition (aka, make + % the pdfdest) after any page break, but before the actual text has + % been typeset. If the destination for the pdf outline is after the + % text, then jumping from the outline may wind up with the text not + % being visible, for instance under high magnification. + \donoderef{#2}% + % + % Typeset the actual heading. + \nobreak % Avoid page breaks at the interline glue. + \vbox{\raggedtitlesettings \hangindent=\wd0 \centerparametersmaybe + \unhbox0 #1\par}% + }% + \nobreak\bigskip % no page break after a chapter title + \nobreak +} + +% @centerchap -- centered and unnumbered. +\let\centerparametersmaybe = \relax +\def\centerparameters{% + \advance\rightskip by 3\rightskip + \leftskip = \rightskip + \parfillskip = 0pt +} + + +% I don't think this chapter style is supported any more, so I'm not +% updating it with the new noderef stuff. We'll see. --karl, 11aug03. +% +\def\setchapterstyle #1 {\csname CHAPF#1\endcsname} +% +\def\unnchfopen #1{% + \chapoddpage + \vbox{\chapfonts \raggedtitlesettings #1\par}% + \nobreak\bigskip\nobreak +} +\def\chfopen #1#2{\chapoddpage {\chapfonts +\vbox to 3in{\vfil \hbox to\hsize{\hfil #2} \hbox to\hsize{\hfil #1} \vfil}}% +\par\penalty 5000 % +} +\def\centerchfopen #1{% + \chapoddpage + \vbox{\chapfonts \raggedtitlesettings \hfill #1\hfill}% + \nobreak\bigskip \nobreak +} +\def\CHAPFopen{% + \global\let\chapmacro=\chfopen + \global\let\centerchapmacro=\centerchfopen} + + +% Section titles. These macros combine the section number parts and +% call the generic \sectionheading to do the printing. +% +\newskip\secheadingskip +\def\secheadingbreak{\dobreak \secheadingskip{-1000}} + +% Subsection titles. +\newskip\subsecheadingskip +\def\subsecheadingbreak{\dobreak \subsecheadingskip{-500}} + +% Subsubsection titles. +\def\subsubsecheadingskip{\subsecheadingskip} +\def\subsubsecheadingbreak{\subsecheadingbreak} + + +% Print any size, any type, section title. +% +% #1 is the text of the title, +% #2 is the section level (sec/subsec/subsubsec), +% #3 is the section type (Ynumbered, Ynothing, Yappendix, Yomitfromtoc), +% #4 is the section number. +% +\def\seckeyword{sec} +% +\def\sectionheading#1#2#3#4{% + {% + \def\sectionlevel{#2}% + \def\temptype{#3}% + % + % It is ok for the @heading series commands to appear inside an + % environment (it's been historically allowed, though the logic is + % dubious), but not the others. + \ifx\temptype\Yomitfromtockeyword\else + \checkenv{}% non-@*heading should not be in an environment. + \fi + \let\footnote=\errfootnoteheading + % + % Switch to the right set of fonts. + \csname #2fonts\endcsname \rmisbold + % + % Insert first mark before the heading break (see notes for \domark). + \let\prevsectiondefs=\lastsectiondefs + \ifx\temptype\Ynothingkeyword + \ifx\sectionlevel\seckeyword + \gdef\lastsectiondefs{\gdef\thissectionname{#1}\gdef\thissectionnum{}% + \gdef\thissection{\thissectionname}}% + \fi + \else\ifx\temptype\Yomitfromtockeyword + % Don't redefine \thissection. + \else\ifx\temptype\Yappendixkeyword + \ifx\sectionlevel\seckeyword + \toks0={#1}% + \xdef\lastsectiondefs{% + \gdef\noexpand\thissectionname{\the\toks0}% + \gdef\noexpand\thissectionnum{#4}% + % \noexpand\putwordSection avoids expanding indigestible + % commands in some of the translations. + \gdef\noexpand\thissection{\noexpand\putwordSection{} + \noexpand\thissectionnum: + \noexpand\thissectionname}% + }% + \fi + \else + \ifx\sectionlevel\seckeyword + \toks0={#1}% + \xdef\lastsectiondefs{% + \gdef\noexpand\thissectionname{\the\toks0}% + \gdef\noexpand\thissectionnum{#4}% + % \noexpand\putwordSection avoids expanding indigestible + % commands in some of the translations. + \gdef\noexpand\thissection{\noexpand\putwordSection{} + \noexpand\thissectionnum: + \noexpand\thissectionname}% + }% + \fi + \fi\fi\fi + % + % Go into vertical mode. Usually we'll already be there, but we + % don't want the following whatsit to end up in a preceding paragraph + % if the document didn't happen to have a blank line. + \par + % + % Output the mark. Pass it through \safewhatsit, to take care of + % the preceding space. + \safewhatsit\domark + % + % Insert space above the heading. + \csname #2headingbreak\endcsname + % + % Now the second mark, after the heading break. No break points + % between here and the heading. + \global\let\prevsectiondefs=\lastsectiondefs + \domark + % + % Only insert the space after the number if we have a section number. + \ifx\temptype\Ynothingkeyword + \setbox0 = \hbox{}% + \def\toctype{unn}% + \gdef\lastsection{#1}% + \else\ifx\temptype\Yomitfromtockeyword + % for @headings -- no section number, don't include in toc, + % and don't redefine \lastsection. + \setbox0 = \hbox{}% + \def\toctype{omit}% + \let\sectionlevel=\empty + \else\ifx\temptype\Yappendixkeyword + \setbox0 = \hbox{#4\enspace}% + \def\toctype{app}% + \gdef\lastsection{#1}% + \else + \setbox0 = \hbox{#4\enspace}% + \def\toctype{num}% + \gdef\lastsection{#1}% + \fi\fi\fi + % + % Write the toc entry (before \donoderef). See comments in \chapmacro. + \writetocentry{\toctype\sectionlevel}{#1}{#4}% + % + % Write the node reference (= pdf destination for pdftex). + % Again, see comments in \chapmacro. + \donoderef{#3}% + % + % Interline glue will be inserted when the vbox is completed. + % That glue will be a valid breakpoint for the page, since it'll be + % preceded by a whatsit (usually from the \donoderef, or from the + % \writetocentry if there was no node). We don't want to allow that + % break, since then the whatsits could end up on page n while the + % section is on page n+1, thus toc/etc. are wrong. Debian bug 276000. + \nobreak + % + % Output the actual section heading. + \vbox{\hyphenpenalty=10000 \tolerance=5000 \parindent=0pt \ptexraggedright + \hangindent=\wd0 % zero if no section number + \unhbox0 #1}% + }% + % Add extra space after the heading -- half of whatever came above it. + % Don't allow stretch, though. + \kern .5 \csname #2headingskip\endcsname + % + % Do not let the kern be a potential breakpoint, as it would be if it + % was followed by glue. + \nobreak + % + % We'll almost certainly start a paragraph next, so don't let that + % glue accumulate. (Not a breakpoint because it's preceded by a + % discardable item.) However, when a paragraph is not started next + % (\startdefun, \cartouche, \center, etc.), this needs to be wiped out + % or the negative glue will cause weirdly wrong output, typically + % obscuring the section heading with something else. + \vskip-\parskip + % + % This is so the last item on the main vertical list is a known + % \penalty > 10000, so \startdefun, etc., can recognize the situation + % and do the needful. + \penalty 10001 +} + + +\message{toc,} +% Table of contents. +\newwrite\tocfile + +% Write an entry to the toc file, opening it if necessary. +% Called from @chapter, etc. +% +% Example usage: \writetocentry{sec}{Section Name}{\the\chapno.\the\secno} +% We append the current node name (if any) and page number as additional +% arguments for the \{chap,sec,...}entry macros which will eventually +% read this. The node name is used in the pdf outlines as the +% destination to jump to. +% +% We open the .toc file for writing here instead of at @setfilename (or +% any other fixed time) so that @contents can be anywhere in the document. +% But if #1 is `omit', then we don't do anything. This is used for the +% table of contents chapter openings themselves. +% +\newif\iftocfileopened +\def\omitkeyword{omit}% +% +\def\writetocentry#1#2#3{% + \edef\writetoctype{#1}% + \ifx\writetoctype\omitkeyword \else + \iftocfileopened\else + \immediate\openout\tocfile = \jobname.toc + \global\tocfileopenedtrue + \fi + % + \iflinks + {\atdummies + \edef\temp{% + \write\tocfile{@#1entry{#2}{#3}{\lastnode}{\noexpand\folio}}}% + \temp + }% + \fi + \fi + % + % Tell \shipout to create a pdf destination on each page, if we're + % writing pdf. These are used in the table of contents. We can't + % just write one on every page because the title pages are numbered + % 1 and 2 (the page numbers aren't printed), and so are the first + % two pages of the document. Thus, we'd have two destinations named + % `1', and two named `2'. + \ifpdf \global\pdfmakepagedesttrue \fi +} + + +% These characters do not print properly in the Computer Modern roman +% fonts, so we must take special care. This is more or less redundant +% with the Texinfo input format setup at the end of this file. +% +\def\activecatcodes{% + \catcode`\"=\active + \catcode`\$=\active + \catcode`\<=\active + \catcode`\>=\active + \catcode`\\=\active + \catcode`\^=\active + \catcode`\_=\active + \catcode`\|=\active + \catcode`\~=\active +} + + +% Read the toc file, which is essentially Texinfo input. +\def\readtocfile{% + \setupdatafile + \activecatcodes + \input \tocreadfilename +} + +\newskip\contentsrightmargin \contentsrightmargin=1in +\newcount\savepageno +\newcount\lastnegativepageno \lastnegativepageno = -1 + +% Prepare to read what we've written to \tocfile. +% +\def\startcontents#1{% + % If @setchapternewpage on, and @headings double, the contents should + % start on an odd page, unlike chapters. Thus, we maintain + % \contentsalignmacro in parallel with \pagealignmacro. + % From: Torbjorn Granlund + \contentsalignmacro + \immediate\closeout\tocfile + % + % Don't need to put `Contents' or `Short Contents' in the headline. + % It is abundantly clear what they are. + \chapmacro{#1}{Yomitfromtoc}{}% + % + \savepageno = \pageno + \begingroup % Set up to handle contents files properly. + \raggedbottom % Worry more about breakpoints than the bottom. + \entryrightmargin=\contentsrightmargin % Don't use the full line length. + % + % Roman numerals for page numbers. + \ifnum \pageno>0 \global\pageno = \lastnegativepageno \fi +} + +% redefined for the two-volume lispref. We always output on +% \jobname.toc even if this is redefined. +% +\def\tocreadfilename{\jobname.toc} + +% Normal (long) toc. +% +\def\contents{% + \startcontents{\putwordTOC}% + \openin 1 \tocreadfilename\space + \ifeof 1 \else + \readtocfile + \fi + \vfill \eject + \contentsalignmacro % in case @setchapternewpage odd is in effect + \ifeof 1 \else + \pdfmakeoutlines + \fi + \closein 1 + \endgroup + \lastnegativepageno = \pageno + \global\pageno = \savepageno +} + +% And just the chapters. +\def\summarycontents{% + \startcontents{\putwordShortTOC}% + % + \let\partentry = \shortpartentry + \let\numchapentry = \shortchapentry + \let\appentry = \shortchapentry + \let\unnchapentry = \shortunnchapentry + % We want a true roman here for the page numbers. + \secfonts + \let\rm=\shortcontrm \let\bf=\shortcontbf + \let\sl=\shortcontsl \let\tt=\shortconttt + \rm + \hyphenpenalty = 10000 + \advance\baselineskip by 1pt % Open it up a little. + \def\numsecentry##1##2##3##4{} + \let\appsecentry = \numsecentry + \let\unnsecentry = \numsecentry + \let\numsubsecentry = \numsecentry + \let\appsubsecentry = \numsecentry + \let\unnsubsecentry = \numsecentry + \let\numsubsubsecentry = \numsecentry + \let\appsubsubsecentry = \numsecentry + \let\unnsubsubsecentry = \numsecentry + \openin 1 \tocreadfilename\space + \ifeof 1 \else + \readtocfile + \fi + \closein 1 + \vfill \eject + \contentsalignmacro % in case @setchapternewpage odd is in effect + \endgroup + \lastnegativepageno = \pageno + \global\pageno = \savepageno +} +\let\shortcontents = \summarycontents + +% Typeset the label for a chapter or appendix for the short contents. +% The arg is, e.g., `A' for an appendix, or `3' for a chapter. +% +\def\shortchaplabel#1{% + % This space should be enough, since a single number is .5em, and the + % widest letter (M) is 1em, at least in the Computer Modern fonts. + % But use \hss just in case. + % (This space doesn't include the extra space that gets added after + % the label; that gets put in by \shortchapentry above.) + % + % We'd like to right-justify chapter numbers, but that looks strange + % with appendix letters. And right-justifying numbers and + % left-justifying letters looks strange when there is less than 10 + % chapters. Have to read the whole toc once to know how many chapters + % there are before deciding ... + \hbox to 1em{#1\hss}% +} + +% These macros generate individual entries in the table of contents. +% The first argument is the chapter or section name. +% The last argument is the page number. +% The arguments in between are the chapter number, section number, ... + +% Parts, in the main contents. Replace the part number, which doesn't +% exist, with an empty box. Let's hope all the numbers have the same width. +% Also ignore the page number, which is conventionally not printed. +\def\numeralbox{\setbox0=\hbox{8}\hbox to \wd0{\hfil}} +\def\partentry#1#2#3#4{\dochapentry{\numeralbox\labelspace#1}{}} +% +% Parts, in the short toc. +\def\shortpartentry#1#2#3#4{% + \penalty-300 + \vskip.5\baselineskip plus.15\baselineskip minus.1\baselineskip + \shortchapentry{{\bf #1}}{\numeralbox}{}{}% +} + +% Chapters, in the main contents. +\def\numchapentry#1#2#3#4{\dochapentry{#2\labelspace#1}{#4}} + +% Chapters, in the short toc. +% See comments in \dochapentry re vbox and related settings. +\def\shortchapentry#1#2#3#4{% + \tocentry{\shortchaplabel{#2}\labelspace #1}{\doshortpageno\bgroup#4\egroup}% +} + +% Appendices, in the main contents. +% Need the word Appendix, and a fixed-size box. +% +\def\appendixbox#1{% + % We use M since it's probably the widest letter. + \setbox0 = \hbox{\putwordAppendix{} M}% + \hbox to \wd0{\putwordAppendix{} #1\hss}} +% +\def\appentry#1#2#3#4{\dochapentry{\appendixbox{#2}\hskip.7em#1}{#4}} + +% Unnumbered chapters. +\def\unnchapentry#1#2#3#4{\dochapentry{#1}{#4}} +\def\shortunnchapentry#1#2#3#4{\tocentry{#1}{\doshortpageno\bgroup#4\egroup}} + +% Sections. +\def\numsecentry#1#2#3#4{\dosecentry{#2\labelspace#1}{#4}} +\let\appsecentry=\numsecentry +\def\unnsecentry#1#2#3#4{\dosecentry{#1}{#4}} + +% Subsections. +\def\numsubsecentry#1#2#3#4{\dosubsecentry{#2\labelspace#1}{#4}} +\let\appsubsecentry=\numsubsecentry +\def\unnsubsecentry#1#2#3#4{\dosubsecentry{#1}{#4}} + +% And subsubsections. +\def\numsubsubsecentry#1#2#3#4{\dosubsubsecentry{#2\labelspace#1}{#4}} +\let\appsubsubsecentry=\numsubsubsecentry +\def\unnsubsubsecentry#1#2#3#4{\dosubsubsecentry{#1}{#4}} + +% This parameter controls the indentation of the various levels. +% Same as \defaultparindent. +\newdimen\tocindent \tocindent = 15pt + +% Now for the actual typesetting. In all these, #1 is the text and #2 is the +% page number. +% +% If the toc has to be broken over pages, we want it to be at chapters +% if at all possible; hence the \penalty. +\def\dochapentry#1#2{% + \penalty-300 \vskip1\baselineskip plus.33\baselineskip minus.25\baselineskip + \begingroup + % Move the page numbers slightly to the right + \advance\entryrightmargin by -0.05em + \chapentryfonts + \tocentry{#1}{\dopageno\bgroup#2\egroup}% + \endgroup + \nobreak\vskip .25\baselineskip plus.1\baselineskip +} + +\def\dosecentry#1#2{\begingroup + \secentryfonts \leftskip=\tocindent + \tocentry{#1}{\dopageno\bgroup#2\egroup}% +\endgroup} + +\def\dosubsecentry#1#2{\begingroup + \subsecentryfonts \leftskip=2\tocindent + \tocentry{#1}{\dopageno\bgroup#2\egroup}% +\endgroup} + +\def\dosubsubsecentry#1#2{\begingroup + \subsubsecentryfonts \leftskip=3\tocindent + \tocentry{#1}{\dopageno\bgroup#2\egroup}% +\endgroup} + +% We use the same \entry macro as for the index entries. +\let\tocentry = \entry + +% Space between chapter (or whatever) number and the title. +\def\labelspace{\hskip1em \relax} + +\def\dopageno#1{{\rm #1}} +\def\doshortpageno#1{{\rm #1}} + +\def\chapentryfonts{\secfonts \rm} +\def\secentryfonts{\textfonts} +\def\subsecentryfonts{\textfonts} +\def\subsubsecentryfonts{\textfonts} + + +\message{environments,} +% @foo ... @end foo. + +% @tex ... @end tex escapes into raw TeX temporarily. +% One exception: @ is still an escape character, so that @end tex works. +% But \@ or @@ will get a plain @ character. + +\envdef\tex{% + \setupmarkupstyle{tex}% + \catcode `\\=0 \catcode `\{=1 \catcode `\}=2 + \catcode `\$=3 \catcode `\&=4 \catcode `\#=6 + \catcode `\^=7 \catcode `\_=8 \catcode `\~=\active \let~=\tie + \catcode `\%=14 + \catcode `\+=\other + \catcode `\"=\other + \catcode `\|=\other + \catcode `\<=\other + \catcode `\>=\other + \catcode `\`=\other + \catcode `\'=\other + \escapechar=`\\ + % + % ' is active in math mode (mathcode"8000). So reset it, and all our + % other math active characters (just in case), to plain's definitions. + \mathactive + % + % Inverse of the list at the beginning of the file. + \let\b=\ptexb + \let\bullet=\ptexbullet + \let\c=\ptexc + \let\,=\ptexcomma + \let\.=\ptexdot + \let\dots=\ptexdots + \let\equiv=\ptexequiv + \let\!=\ptexexclam + \let\i=\ptexi + \let\indent=\ptexindent + \let\noindent=\ptexnoindent + \let\{=\ptexlbrace + \let\+=\tabalign + \let\}=\ptexrbrace + \let\/=\ptexslash + \let\sp=\ptexsp + \let\*=\ptexstar + %\let\sup=\ptexsup % do not redefine, we want @sup to work in math mode + \let\t=\ptext + \expandafter \let\csname top\endcsname=\ptextop % we've made it outer + \let\frenchspacing=\plainfrenchspacing + % + \def\endldots{\mathinner{\ldots\ldots\ldots\ldots}}% + \def\enddots{\relax\ifmmode\endldots\else$\mathsurround=0pt \endldots\,$\fi}% + \def\@{@}% +} +% There is no need to define \Etex. + +% Define @lisp ... @end lisp. +% @lisp environment forms a group so it can rebind things, +% including the definition of @end lisp (which normally is erroneous). + +% Amount to narrow the margins by for @lisp. +\newskip\lispnarrowing \lispnarrowing=0.4in + +% This is the definition that ^^M gets inside @lisp, @example, and other +% such environments. \null is better than a space, since it doesn't +% have any width. +\def\lisppar{\null\endgraf} + +% This space is always present above and below environments. +\newskip\envskipamount \envskipamount = 0pt + +% Make spacing and below environment symmetrical. We use \parskip here +% to help in doing that, since in @example-like environments \parskip +% is reset to zero; thus the \afterenvbreak inserts no space -- but the +% start of the next paragraph will insert \parskip. +% +\def\aboveenvbreak{{% + % =10000 instead of <10000 because of a special case in \itemzzz and + % \sectionheading, q.v. + \ifnum \lastpenalty=10000 \else + \advance\envskipamount by \parskip + \endgraf + \ifdim\lastskip<\envskipamount + \removelastskip + \ifnum\lastpenalty<10000 + % Penalize breaking before the environment, because preceding text + % often leads into it. + \penalty100 + \fi + \vskip\envskipamount + \fi + \fi +}} + +\def\afterenvbreak{{% + % =10000 instead of <10000 because of a special case in \itemzzz and + % \sectionheading, q.v. + \ifnum \lastpenalty=10000 \else + \advance\envskipamount by \parskip + \endgraf + \ifdim\lastskip<\envskipamount + \removelastskip + % it's not a good place to break if the last penalty was \nobreak + % or better ... + \ifnum\lastpenalty<10000 \penalty-50 \fi + \vskip\envskipamount + \fi + \fi +}} + +% \nonarrowing is a flag. If "set", @lisp etc don't narrow margins; it will +% also clear it, so that its embedded environments do the narrowing again. +\let\nonarrowing=\relax + +% @cartouche ... @end cartouche: draw rectangle w/rounded corners around +% environment contents. +\font\circle=lcircle10 +\newdimen\circthick +\newdimen\cartouter\newdimen\cartinner +\newskip\normbskip\newskip\normpskip\newskip\normlskip +\circthick=\fontdimen8\circle +% +\def\ctl{{\circle\char'013\hskip -6pt}}% 6pt from pl file: 1/2charwidth +\def\ctr{{\hskip 6pt\circle\char'010}} +\def\cbl{{\circle\char'012\hskip -6pt}} +\def\cbr{{\hskip 6pt\circle\char'011}} +\def\carttop{\hbox to \cartouter{\hskip\lskip + \ctl\leaders\hrule height\circthick\hfil\ctr + \hskip\rskip}} +\def\cartbot{\hbox to \cartouter{\hskip\lskip + \cbl\leaders\hrule height\circthick\hfil\cbr + \hskip\rskip}} +% +\newskip\lskip\newskip\rskip + +\envdef\cartouche{% + \ifhmode\par\fi % can't be in the midst of a paragraph. + \startsavinginserts + \lskip=\leftskip \rskip=\rightskip + \leftskip=0pt\rightskip=0pt % we want these *outside*. + \cartinner=\hsize \advance\cartinner by-\lskip + \advance\cartinner by-\rskip + \cartouter=\hsize + \advance\cartouter by 18.4pt % allow for 3pt kerns on either + % side, and for 6pt waste from + % each corner char, and rule thickness + \normbskip=\baselineskip \normpskip=\parskip \normlskip=\lineskip + % + % If this cartouche directly follows a sectioning command, we need the + % \parskip glue (backspaced over by default) or the cartouche can + % collide with the section heading. + \ifnum\lastpenalty>10000 \vskip\parskip \penalty\lastpenalty \fi + % + \setbox\groupbox=\vbox\bgroup + \baselineskip=0pt\parskip=0pt\lineskip=0pt + \carttop + \hbox\bgroup + \hskip\lskip + \vrule\kern3pt + \vbox\bgroup + \kern3pt + \hsize=\cartinner + \baselineskip=\normbskip + \lineskip=\normlskip + \parskip=\normpskip + \vskip -\parskip + \comment % For explanation, see the end of def\group. +} +\def\Ecartouche{% + \ifhmode\par\fi + \kern3pt + \egroup + \kern3pt\vrule + \hskip\rskip + \egroup + \cartbot + \egroup + \addgroupbox + \checkinserts +} + + +% This macro is called at the beginning of all the @example variants, +% inside a group. +\newdimen\nonfillparindent +\def\nonfillstart{% + \aboveenvbreak + \ifdim\hfuzz < 12pt \hfuzz = 12pt \fi % Don't be fussy + \sepspaces % Make spaces be word-separators rather than space tokens. + \let\par = \lisppar % don't ignore blank lines + \obeylines % each line of input is a line of output + \parskip = 0pt + % Turn off paragraph indentation but redefine \indent to emulate + % the normal \indent. + \nonfillparindent=\parindent + \parindent = 0pt + \let\indent\nonfillindent + % + \emergencystretch = 0pt % don't try to avoid overfull boxes + \ifx\nonarrowing\relax + \advance \leftskip by \lispnarrowing + \exdentamount=\lispnarrowing + \else + \let\nonarrowing = \relax + \fi + \let\exdent=\nofillexdent +} + +\begingroup +\obeyspaces +% We want to swallow spaces (but not other tokens) after the fake +% @indent in our nonfill-environments, where spaces are normally +% active and set to @tie, resulting in them not being ignored after +% @indent. +\gdef\nonfillindent{\futurelet\temp\nonfillindentcheck}% +\gdef\nonfillindentcheck{% +\ifx\temp % +\expandafter\nonfillindentgobble% +\else% +\leavevmode\nonfillindentbox% +\fi% +}% +\endgroup +\def\nonfillindentgobble#1{\nonfillindent} +\def\nonfillindentbox{\hbox to \nonfillparindent{\hss}} + +% If you want all examples etc. small: @set dispenvsize small. +% If you want even small examples the full size: @set dispenvsize nosmall. +% This affects the following displayed environments: +% @example, @display, @format, @lisp +% +\def\smallword{small} +\def\nosmallword{nosmall} +\let\SETdispenvsize\relax +\def\setnormaldispenv{% + \ifx\SETdispenvsize\smallword + % end paragraph for sake of leading, in case document has no blank + % line. This is redundant with what happens in \aboveenvbreak, but + % we need to do it before changing the fonts, and it's inconvenient + % to change the fonts afterward. + \ifnum \lastpenalty=10000 \else \endgraf \fi + \smallexamplefonts \rm + \fi +} +\def\setsmalldispenv{% + \ifx\SETdispenvsize\nosmallword + \else + \ifnum \lastpenalty=10000 \else \endgraf \fi + \smallexamplefonts \rm + \fi +} + +% We often define two environments, @foo and @smallfoo. +% Let's do it in one command. #1 is the env name, #2 the definition. +\def\makedispenvdef#1#2{% + \expandafter\envdef\csname#1\endcsname {\setnormaldispenv #2}% + \expandafter\envdef\csname small#1\endcsname {\setsmalldispenv #2}% + \expandafter\let\csname E#1\endcsname \afterenvbreak + \expandafter\let\csname Esmall#1\endcsname \afterenvbreak +} + +% Define two environment synonyms (#1 and #2) for an environment. +\def\maketwodispenvdef#1#2#3{% + \makedispenvdef{#1}{#3}% + \makedispenvdef{#2}{#3}% +} +% +% @lisp: indented, narrowed, typewriter font; +% @example: same as @lisp. +% +% @smallexample and @smalllisp: use smaller fonts. +% Originally contributed by Pavel@xerox. +% +\maketwodispenvdef{lisp}{example}{% + \nonfillstart + \tt\setupmarkupstyle{example}% + \let\kbdfont = \kbdexamplefont % Allow @kbd to do something special. + \gobble % eat return +} +% @display/@smalldisplay: same as @lisp except keep current font. +% +\makedispenvdef{display}{% + \nonfillstart + \gobble +} + +% @format/@smallformat: same as @display except don't narrow margins. +% +\makedispenvdef{format}{% + \let\nonarrowing = t% + \nonfillstart + \gobble +} + +% @flushleft: same as @format, but doesn't obey \SETdispenvsize. +\envdef\flushleft{% + \let\nonarrowing = t% + \nonfillstart + \gobble +} +\let\Eflushleft = \afterenvbreak + +% @flushright. +% +\envdef\flushright{% + \let\nonarrowing = t% + \nonfillstart + \advance\leftskip by 0pt plus 1fill\relax + \gobble +} +\let\Eflushright = \afterenvbreak + + +% @raggedright does more-or-less normal line breaking but no right +% justification. From plain.tex. Don't stretch around special +% characters in urls in this environment, since the stretch at the right +% should be enough. +\envdef\raggedright{% + \rightskip0pt plus2.4em \spaceskip.3333em \xspaceskip.5em\relax + \def\urefprestretchamount{0pt}% + \def\urefpoststretchamount{0pt}% +} +\let\Eraggedright\par + +\envdef\raggedleft{% + \parindent=0pt \leftskip0pt plus2em + \spaceskip.3333em \xspaceskip.5em \parfillskip=0pt + \hbadness=10000 % Last line will usually be underfull, so turn off + % badness reporting. +} +\let\Eraggedleft\par + +\envdef\raggedcenter{% + \parindent=0pt \rightskip0pt plus1em \leftskip0pt plus1em + \spaceskip.3333em \xspaceskip.5em \parfillskip=0pt + \hbadness=10000 % Last line will usually be underfull, so turn off + % badness reporting. +} +\let\Eraggedcenter\par + + +% @quotation does normal linebreaking (hence we can't use \nonfillstart) +% and narrows the margins. We keep \parskip nonzero in general, since +% we're doing normal filling. So, when using \aboveenvbreak and +% \afterenvbreak, temporarily make \parskip 0. +% +\makedispenvdef{quotation}{\quotationstart} +% +\def\quotationstart{% + \indentedblockstart % same as \indentedblock, but increase right margin too. + \ifx\nonarrowing\relax + \advance\rightskip by \lispnarrowing + \fi + \parsearg\quotationlabel +} + +% We have retained a nonzero parskip for the environment, since we're +% doing normal filling. +% +\def\Equotation{% + \par + \ifx\quotationauthor\thisisundefined\else + % indent a bit. + \leftline{\kern 2\leftskip \sl ---\quotationauthor}% + \fi + {\parskip=0pt \afterenvbreak}% +} +\def\Esmallquotation{\Equotation} + +% If we're given an argument, typeset it in bold with a colon after. +\def\quotationlabel#1{% + \def\temp{#1}% + \ifx\temp\empty \else + {\bf #1: }% + \fi +} + +% @indentedblock is like @quotation, but indents only on the left and +% has no optional argument. +% +\makedispenvdef{indentedblock}{\indentedblockstart} +% +\def\indentedblockstart{% + {\parskip=0pt \aboveenvbreak}% because \aboveenvbreak inserts \parskip + \parindent=0pt + % + % @cartouche defines \nonarrowing to inhibit narrowing at next level down. + \ifx\nonarrowing\relax + \advance\leftskip by \lispnarrowing + \exdentamount = \lispnarrowing + \else + \let\nonarrowing = \relax + \fi +} + +% Keep a nonzero parskip for the environment, since we're doing normal filling. +% +\def\Eindentedblock{% + \par + {\parskip=0pt \afterenvbreak}% +} +\def\Esmallindentedblock{\Eindentedblock} + + +% LaTeX-like @verbatim...@end verbatim and @verb{...} +% If we want to allow any as delimiter, +% we need the curly braces so that makeinfo sees the @verb command, eg: +% `@verbx...x' would look like the '@verbx' command. --janneke@gnu.org +% +% [Knuth]: Donald Ervin Knuth, 1996. The TeXbook. +% +% [Knuth] p.344; only we need to do the other characters Texinfo sets +% active too. Otherwise, they get lost as the first character on a +% verbatim line. +\def\dospecials{% + \do\ \do\\\do\{\do\}\do\$\do\&% + \do\#\do\^\do\^^K\do\_\do\^^A\do\%\do\~% + \do\<\do\>\do\|\do\@\do+\do\"% + % Don't do the quotes -- if we do, @set txicodequoteundirected and + % @set txicodequotebacktick will not have effect on @verb and + % @verbatim, and ?` and !` ligatures won't get disabled. + %\do\`\do\'% +} +% +% [Knuth] p. 380 +\def\uncatcodespecials{% + \def\do##1{\catcode`##1=\other}\dospecials} +% +% Setup for the @verb command. +% +% Eight spaces for a tab +\begingroup + \catcode`\^^I=\active + \gdef\tabeightspaces{\catcode`\^^I=\active\def^^I{\ \ \ \ \ \ \ \ }} +\endgroup +% +\def\setupverb{% + \tt % easiest (and conventionally used) font for verbatim + \def\par{\leavevmode\endgraf}% + \setupmarkupstyle{verb}% + \tabeightspaces + % Respect line breaks, + % print special symbols as themselves, and + % make each space count + % must do in this order: + \obeylines \uncatcodespecials \sepspaces +} + +% Setup for the @verbatim environment +% +% Real tab expansion. +\newdimen\tabw \setbox0=\hbox{\tt\space} \tabw=8\wd0 % tab amount +% +% We typeset each line of the verbatim in an \hbox, so we can handle +% tabs. The \global is in case the verbatim line starts with an accent, +% or some other command that starts with a begin-group. Otherwise, the +% entire \verbbox would disappear at the corresponding end-group, before +% it is typeset. Meanwhile, we can't have nested verbatim commands +% (can we?), so the \global won't be overwriting itself. +\newbox\verbbox +\def\starttabbox{\global\setbox\verbbox=\hbox\bgroup} +% +\begingroup + \catcode`\^^I=\active + \gdef\tabexpand{% + \catcode`\^^I=\active + \def^^I{\leavevmode\egroup + \dimen\verbbox=\wd\verbbox % the width so far, or since the previous tab + \divide\dimen\verbbox by\tabw + \multiply\dimen\verbbox by\tabw % compute previous multiple of \tabw + \advance\dimen\verbbox by\tabw % advance to next multiple of \tabw + \wd\verbbox=\dimen\verbbox \box\verbbox \starttabbox + }% + } +\endgroup + +% start the verbatim environment. +\def\setupverbatim{% + \let\nonarrowing = t% + \nonfillstart + \tt % easiest (and conventionally used) font for verbatim + % The \leavevmode here is for blank lines. Otherwise, we would + % never \starttabox and the \egroup would end verbatim mode. + \def\par{\leavevmode\egroup\box\verbbox\endgraf}% + \tabexpand + \setupmarkupstyle{verbatim}% + % Respect line breaks, + % print special symbols as themselves, and + % make each space count. + % Must do in this order: + \obeylines \uncatcodespecials \sepspaces + \everypar{\starttabbox}% +} + +% Do the @verb magic: verbatim text is quoted by unique +% delimiter characters. Before first delimiter expect a +% right brace, after last delimiter expect closing brace: +% +% \def\doverb'{'#1'}'{#1} +% +% [Knuth] p. 382; only eat outer {} +\begingroup + \catcode`[=1\catcode`]=2\catcode`\{=\other\catcode`\}=\other + \gdef\doverb{#1[\def\next##1#1}[##1\endgroup]\next] +\endgroup +% +\def\verb{\begingroup\setupverb\doverb} +% +% +% Do the @verbatim magic: define the macro \doverbatim so that +% the (first) argument ends when '@end verbatim' is reached, ie: +% +% \def\doverbatim#1@end verbatim{#1} +% +% For Texinfo it's a lot easier than for LaTeX, +% because texinfo's \verbatim doesn't stop at '\end{verbatim}': +% we need not redefine '\', '{' and '}'. +% +% Inspired by LaTeX's verbatim command set [latex.ltx] +% +\begingroup + \catcode`\ =\active + \obeylines % + % ignore everything up to the first ^^M, that's the newline at the end + % of the @verbatim input line itself. Otherwise we get an extra blank + % line in the output. + \xdef\doverbatim#1^^M#2@end verbatim{#2\noexpand\end\gobble verbatim}% + % We really want {...\end verbatim} in the body of the macro, but + % without the active space; thus we have to use \xdef and \gobble. +\endgroup +% +\envdef\verbatim{% + \setupverbatim\doverbatim +} +\let\Everbatim = \afterenvbreak + + +% @verbatiminclude FILE - insert text of file in verbatim environment. +% +\def\verbatiminclude{\parseargusing\filenamecatcodes\doverbatiminclude} +% +\def\doverbatiminclude#1{% + {% + \makevalueexpandable + \setupverbatim + \indexnofonts % Allow `@@' and other weird things in file names. + \wlog{texinfo.tex: doing @verbatiminclude of #1^^J}% + \input #1 + \afterenvbreak + }% +} + +% @copying ... @end copying. +% Save the text away for @insertcopying later. +% +% We save the uninterpreted tokens, rather than creating a box. +% Saving the text in a box would be much easier, but then all the +% typesetting commands (@smallbook, font changes, etc.) have to be done +% beforehand -- and a) we want @copying to be done first in the source +% file; b) letting users define the frontmatter in as flexible order as +% possible is desirable. +% +\def\copying{\checkenv{}\begingroup\scanargctxt\docopying} +\def\docopying#1@end copying{\endgroup\def\copyingtext{#1}} +% +\def\insertcopying{% + \begingroup + \parindent = 0pt % paragraph indentation looks wrong on title page + \scanexp\copyingtext + \endgroup +} + + +\message{defuns,} +% @defun etc. + +\newskip\defbodyindent \defbodyindent=.4in +\newskip\defargsindent \defargsindent=50pt +\newskip\deflastargmargin \deflastargmargin=18pt +\newcount\defunpenalty + +% Start the processing of @deffn: +\def\startdefun{% + \ifnum\lastpenalty<10000 + \medbreak + \defunpenalty=10003 % Will keep this @deffn together with the + % following @def command, see below. + \else + % If there are two @def commands in a row, we'll have a \nobreak, + % which is there to keep the function description together with its + % header. But if there's nothing but headers, we need to allow a + % break somewhere. Check specifically for penalty 10002, inserted + % by \printdefunline, instead of 10000, since the sectioning + % commands also insert a nobreak penalty, and we don't want to allow + % a break between a section heading and a defun. + % + % As a further refinement, we avoid "club" headers by signalling + % with penalty of 10003 after the very first @deffn in the + % sequence (see above), and penalty of 10002 after any following + % @def command. + \ifnum\lastpenalty=10002 \penalty2000 \else \defunpenalty=10002 \fi + % + % Similarly, after a section heading, do not allow a break. + % But do insert the glue. + \medskip % preceded by discardable penalty, so not a breakpoint + \fi + % + \parindent=0in + \advance\leftskip by \defbodyindent + \exdentamount=\defbodyindent +} + +\def\dodefunx#1{% + % First, check whether we are in the right environment: + \checkenv#1% + % + % As above, allow line break if we have multiple x headers in a row. + % It's not a great place, though. + \ifnum\lastpenalty=10002 \penalty3000 \else \defunpenalty=10002 \fi + % + % And now, it's time to reuse the body of the original defun: + \expandafter\gobbledefun#1% +} +\def\gobbledefun#1\startdefun{} + +% \printdefunline \deffnheader{text} +% +\def\printdefunline#1#2{% + \begingroup + % call \deffnheader: + #1#2 \endheader + % common ending: + \interlinepenalty = 10000 + \advance\rightskip by 0pt plus 1fil\relax + \endgraf + \nobreak\vskip -\parskip + \penalty\defunpenalty % signal to \startdefun and \dodefunx + % Some of the @defun-type tags do not enable magic parentheses, + % rendering the following check redundant. But we don't optimize. + \checkparencounts + \endgroup +} + +\def\Edefun{\endgraf\medbreak} + +% \makedefun{deffn} creates \deffn, \deffnx and \Edeffn; +% the only thing remaining is to define \deffnheader. +% +\def\makedefun#1{% + \expandafter\let\csname E#1\endcsname = \Edefun + \edef\temp{\noexpand\domakedefun + \makecsname{#1}\makecsname{#1x}\makecsname{#1header}}% + \temp +} + +% \domakedefun \deffn \deffnx \deffnheader { (defn. of \deffnheader) } +% +% Define \deffn and \deffnx, without parameters. +% \deffnheader has to be defined explicitly. +% +\def\domakedefun#1#2#3{% + \envdef#1{% + \startdefun + \doingtypefnfalse % distinguish typed functions from all else + \parseargusing\activeparens{\printdefunline#3}% + }% + \def#2{\dodefunx#1}% + \def#3% +} + +\newif\ifdoingtypefn % doing typed function? +\newif\ifrettypeownline % typeset return type on its own line? + +% @deftypefnnewline on|off says whether the return type of typed functions +% are printed on their own line. This affects @deftypefn, @deftypefun, +% @deftypeop, and @deftypemethod. +% +\parseargdef\deftypefnnewline{% + \def\temp{#1}% + \ifx\temp\onword + \expandafter\let\csname SETtxideftypefnnl\endcsname + = \empty + \else\ifx\temp\offword + \expandafter\let\csname SETtxideftypefnnl\endcsname + = \relax + \else + \errhelp = \EMsimple + \errmessage{Unknown @txideftypefnnl value `\temp', + must be on|off}% + \fi\fi +} + +% Untyped functions: + +% @deffn category name args +\makedefun{deffn}{\deffngeneral{}} + +% @deffn category class name args +\makedefun{defop}#1 {\defopon{#1\ \putwordon}} + +% \defopon {category on}class name args +\def\defopon#1#2 {\deffngeneral{\putwordon\ \code{#2}}{#1\ \code{#2}} } + +% \deffngeneral {subind}category name args +% +\def\deffngeneral#1#2 #3 #4\endheader{% + % Remember that \dosubind{fn}{foo}{} is equivalent to \doind{fn}{foo}. + \dosubind{fn}{\code{#3}}{#1}% + \defname{#2}{}{#3}\magicamp\defunargs{#4\unskip}% +} + +% Typed functions: + +% @deftypefn category type name args +\makedefun{deftypefn}{\deftypefngeneral{}} + +% @deftypeop category class type name args +\makedefun{deftypeop}#1 {\deftypeopon{#1\ \putwordon}} + +% \deftypeopon {category on}class type name args +\def\deftypeopon#1#2 {\deftypefngeneral{\putwordon\ \code{#2}}{#1\ \code{#2}} } + +% \deftypefngeneral {subind}category type name args +% +\def\deftypefngeneral#1#2 #3 #4 #5\endheader{% + \dosubind{fn}{\code{#4}}{#1}% + \doingtypefntrue + \defname{#2}{#3}{#4}\defunargs{#5\unskip}% +} + +% Typed variables: + +% @deftypevr category type var args +\makedefun{deftypevr}{\deftypecvgeneral{}} + +% @deftypecv category class type var args +\makedefun{deftypecv}#1 {\deftypecvof{#1\ \putwordof}} + +% \deftypecvof {category of}class type var args +\def\deftypecvof#1#2 {\deftypecvgeneral{\putwordof\ \code{#2}}{#1\ \code{#2}} } + +% \deftypecvgeneral {subind}category type var args +% +\def\deftypecvgeneral#1#2 #3 #4 #5\endheader{% + \dosubind{vr}{\code{#4}}{#1}% + \defname{#2}{#3}{#4}\defunargs{#5\unskip}% +} + +% Untyped variables: + +% @defvr category var args +\makedefun{defvr}#1 {\deftypevrheader{#1} {} } + +% @defcv category class var args +\makedefun{defcv}#1 {\defcvof{#1\ \putwordof}} + +% \defcvof {category of}class var args +\def\defcvof#1#2 {\deftypecvof{#1}#2 {} } + +% Types: + +% @deftp category name args +\makedefun{deftp}#1 #2 #3\endheader{% + \doind{tp}{\code{#2}}% + \defname{#1}{}{#2}\defunargs{#3\unskip}% +} + +% Remaining @defun-like shortcuts: +\makedefun{defun}{\deffnheader{\putwordDeffunc} } +\makedefun{defmac}{\deffnheader{\putwordDefmac} } +\makedefun{defspec}{\deffnheader{\putwordDefspec} } +\makedefun{deftypefun}{\deftypefnheader{\putwordDeffunc} } +\makedefun{defvar}{\defvrheader{\putwordDefvar} } +\makedefun{defopt}{\defvrheader{\putwordDefopt} } +\makedefun{deftypevar}{\deftypevrheader{\putwordDefvar} } +\makedefun{defmethod}{\defopon\putwordMethodon} +\makedefun{deftypemethod}{\deftypeopon\putwordMethodon} +\makedefun{defivar}{\defcvof\putwordInstanceVariableof} +\makedefun{deftypeivar}{\deftypecvof\putwordInstanceVariableof} + +% \defname, which formats the name of the @def (not the args). +% #1 is the category, such as "Function". +% #2 is the return type, if any. +% #3 is the function name. +% +% We are followed by (but not passed) the arguments, if any. +% +\def\defname#1#2#3{% + \par + % Get the values of \leftskip and \rightskip as they were outside the @def... + \advance\leftskip by -\defbodyindent + % + % Determine if we are typesetting the return type of a typed function + % on a line by itself. + \rettypeownlinefalse + \ifdoingtypefn % doing a typed function specifically? + % then check user option for putting return type on its own line: + \expandafter\ifx\csname SETtxideftypefnnl\endcsname\relax \else + \rettypeownlinetrue + \fi + \fi + % + % How we'll format the category name. Putting it in brackets helps + % distinguish it from the body text that may end up on the next line + % just below it. + \def\temp{#1}% + \setbox0=\hbox{\kern\deflastargmargin \ifx\temp\empty\else [\rm\temp]\fi} + % + % Figure out line sizes for the paragraph shape. We'll always have at + % least two. + \tempnum = 2 + % + % The first line needs space for \box0; but if \rightskip is nonzero, + % we need only space for the part of \box0 which exceeds it: + \dimen0=\hsize \advance\dimen0 by -\wd0 \advance\dimen0 by \rightskip + % + % If doing a return type on its own line, we'll have another line. + \ifrettypeownline + \advance\tempnum by 1 + \def\maybeshapeline{0in \hsize}% + \else + \def\maybeshapeline{}% + \fi + % + % The continuations: + \dimen2=\hsize \advance\dimen2 by -\defargsindent + % + % The final paragraph shape: + \parshape \tempnum 0in \dimen0 \maybeshapeline \defargsindent \dimen2 + % + % Put the category name at the right margin. + \noindent + \hbox to 0pt{% + \hfil\box0 \kern-\hsize + % \hsize has to be shortened this way: + \kern\leftskip + % Intentionally do not respect \rightskip, since we need the space. + }% + % + % Allow all lines to be underfull without complaint: + \tolerance=10000 \hbadness=10000 + \exdentamount=\defbodyindent + {% + % defun fonts. We use typewriter by default (used to be bold) because: + % . we're printing identifiers, they should be in tt in principle. + % . in languages with many accents, such as Czech or French, it's + % common to leave accents off identifiers. The result looks ok in + % tt, but exceedingly strange in rm. + % . we don't want -- and --- to be treated as ligatures. + % . this still does not fix the ?` and !` ligatures, but so far no + % one has made identifiers using them :). + \df \tt + \def\temp{#2}% text of the return type + \ifx\temp\empty\else + \tclose{\temp}% typeset the return type + \ifrettypeownline + % put return type on its own line; prohibit line break following: + \hfil\vadjust{\nobreak}\break + \else + \space % type on same line, so just followed by a space + \fi + \fi % no return type + #3% output function name + }% + {\rm\enskip}% hskip 0.5 em of \tenrm + % + \boldbrax + % arguments will be output next, if any. +} + +% Print arguments in slanted roman (not ttsl), inconsistently with using +% tt for the name. This is because literal text is sometimes needed in +% the argument list (groff manual), and ttsl and tt are not very +% distinguishable. Prevent hyphenation at `-' chars. +% +\def\defunargs#1{% + % use sl by default (not ttsl), + % tt for the names. + \df \sl \hyphenchar\font=0 + % + % On the other hand, if an argument has two dashes (for instance), we + % want a way to get ttsl. We used to recommend @var for that, so + % leave the code in, but it's strange for @var to lead to typewriter. + % Nowadays we recommend @code, since the difference between a ttsl hyphen + % and a tt hyphen is pretty tiny. @code also disables ?` !`. + \def\var##1{{\setupmarkupstyle{var}\ttslanted{##1}}}% + #1% + \sl\hyphenchar\font=45 +} + +% We want ()&[] to print specially on the defun line. +% +\def\activeparens{% + \catcode`\(=\active \catcode`\)=\active + \catcode`\[=\active \catcode`\]=\active + \catcode`\&=\active +} + +% Make control sequences which act like normal parenthesis chars. +\let\lparen = ( \let\rparen = ) + +% Be sure that we always have a definition for `(', etc. For example, +% if the fn name has parens in it, \boldbrax will not be in effect yet, +% so TeX would otherwise complain about undefined control sequence. +{ + \activeparens + \global\let(=\lparen \global\let)=\rparen + \global\let[=\lbrack \global\let]=\rbrack + \global\let& = \& + + \gdef\boldbrax{\let(=\opnr\let)=\clnr\let[=\lbrb\let]=\rbrb} + \gdef\magicamp{\let&=\amprm} +} + +\newcount\parencount + +% If we encounter &foo, then turn on ()-hacking afterwards +\newif\ifampseen +\def\amprm#1 {\ampseentrue{\bf\ }} + +\def\parenfont{% + \ifampseen + % At the first level, print parens in roman, + % otherwise use the default font. + \ifnum \parencount=1 \rm \fi + \else + % The \sf parens (in \boldbrax) actually are a little bolder than + % the contained text. This is especially needed for [ and ] . + \sf + \fi +} +\def\infirstlevel#1{% + \ifampseen + \ifnum\parencount=1 + #1% + \fi + \fi +} +\def\bfafterword#1 {#1 \bf} + +\def\opnr{% + \global\advance\parencount by 1 + {\parenfont(}% + \infirstlevel \bfafterword +} +\def\clnr{% + {\parenfont)}% + \infirstlevel \sl + \global\advance\parencount by -1 +} + +\newcount\brackcount +\def\lbrb{% + \global\advance\brackcount by 1 + {\bf[}% +} +\def\rbrb{% + {\bf]}% + \global\advance\brackcount by -1 +} + +\def\checkparencounts{% + \ifnum\parencount=0 \else \badparencount \fi + \ifnum\brackcount=0 \else \badbrackcount \fi +} +% these should not use \errmessage; the glibc manual, at least, actually +% has such constructs (when documenting function pointers). +\def\badparencount{% + \message{Warning: unbalanced parentheses in @def...}% + \global\parencount=0 +} +\def\badbrackcount{% + \message{Warning: unbalanced square brackets in @def...}% + \global\brackcount=0 +} + + +\message{macros,} +% @macro. + +% To do this right we need a feature of e-TeX, \scantokens, +% which we arrange to emulate with a temporary file in ordinary TeX. +\ifx\eTeXversion\thisisundefined + \newwrite\macscribble + \def\scantokens#1{% + \toks0={#1}% + \immediate\openout\macscribble=\jobname.tmp + \immediate\write\macscribble{\the\toks0}% + \immediate\closeout\macscribble + \input \jobname.tmp + } +\fi + +\let\aftermacroxxx\relax +\def\aftermacro{\aftermacroxxx} + +% alias because \c means cedilla in @tex or @math +\let\texinfoc=\c + +% Used at the time of macro expansion. +% Argument is macro body with arguments substituted +\def\scanmacro#1{% + \newlinechar`\^^M + \def\xprocessmacroarg{\eatspaces}% + % + % Process the macro body under the current catcode regime. + \scantokens{#1\texinfoc}\aftermacro% + % + % The \c is to remove the \newlinechar added by \scantokens, and + % can be noticed by \parsearg. + % The \aftermacro allows a \comment at the end of the macro definition + % to duplicate itself past the final \newlinechar added by \scantokens: + % this is used in the definition of \group to comment out a newline. We + % don't do the same for \c to support Texinfo files with macros that ended + % with a @c, which should no longer be necessary. + % We avoid surrounding the call to \scantokens with \bgroup and \egroup + % to allow macros to open or close groups themselves. +} + +% Used for copying and captions +\def\scanexp#1{% + \bgroup + % Undo catcode changes of \startcontents and \printindex + % When called from @insertcopying or (short)caption, we need active + % backslash to get it printed correctly. + % FIXME: This may not be needed. + %\catcode`\@=0 \catcode`\\=\active \escapechar=`\@ + \edef\temp{\noexpand\scanmacro{#1}}% + \temp + \egroup +} + +\newcount\paramno % Count of parameters +\newtoks\macname % Macro name +\newif\ifrecursive % Is it recursive? + +% List of all defined macros in the form +% \definedummyword\macro1\definedummyword\macro2... +% Currently is also contains all @aliases; the list can be split +% if there is a need. +\def\macrolist{} + +% Add the macro to \macrolist +\def\addtomacrolist#1{\expandafter \addtomacrolistxxx \csname#1\endcsname} +\def\addtomacrolistxxx#1{% + \toks0 = \expandafter{\macrolist\definedummyword#1}% + \xdef\macrolist{\the\toks0}% +} + +% Utility routines. +% This does \let #1 = #2, with \csnames; that is, +% \let \csname#1\endcsname = \csname#2\endcsname +% (except of course we have to play expansion games). +% +\def\cslet#1#2{% + \expandafter\let + \csname#1\expandafter\endcsname + \csname#2\endcsname +} + +% Trim leading and trailing spaces off a string. +% Concepts from aro-bend problem 15 (see CTAN). +{\catcode`\@=11 +\gdef\eatspaces #1{\expandafter\trim@\expandafter{#1 }} +\gdef\trim@ #1{\trim@@ @#1 @ #1 @ @@} +\gdef\trim@@ #1@ #2@ #3@@{\trim@@@\empty #2 @} +\def\unbrace#1{#1} +\unbrace{\gdef\trim@@@ #1 } #2@{#1} +} + +% Trim a single trailing ^^M off a string. +{\catcode`\^^M=\other \catcode`\Q=3% +\gdef\eatcr #1{\eatcra #1Q^^MQ}% +\gdef\eatcra#1^^MQ{\eatcrb#1Q}% +\gdef\eatcrb#1Q#2Q{#1}% +} + +% Macro bodies are absorbed as an argument in a context where +% all characters are catcode 10, 11 or 12, except \ which is active +% (as in normal texinfo). It is necessary to change the definition of \ +% to recognize macro arguments; this is the job of \mbodybackslash. +% +% Non-ASCII encodings make 8-bit characters active, so un-activate +% them to avoid their expansion. Must do this non-globally, to +% confine the change to the current group. +% +% It's necessary to have hard CRs when the macro is executed. This is +% done by making ^^M (\endlinechar) catcode 12 when reading the macro +% body, and then making it the \newlinechar in \scanmacro. +% +\def\scanctxt{% used as subroutine + \catcode`\"=\other + \catcode`\+=\other + \catcode`\<=\other + \catcode`\>=\other + \catcode`\^=\other + \catcode`\_=\other + \catcode`\|=\other + \catcode`\~=\other + \ifx\declaredencoding\ascii \else \setnonasciicharscatcodenonglobal\other \fi +} + +\def\scanargctxt{% used for copying and captions, not macros. + \scanctxt + \catcode`\@=\other + \catcode`\\=\other + \catcode`\^^M=\other +} + +\def\macrobodyctxt{% used for @macro definitions + \scanctxt + \catcode`\ =\other + \catcode`\@=\other + \catcode`\{=\other + \catcode`\}=\other + \catcode`\^^M=\other + \usembodybackslash +} + +% Used when scanning braced macro arguments. Note, however, that catcode +% changes here are ineffectual if the macro invocation was nested inside +% an argument to another Texinfo command. +\def\macroargctxt{% + \scanctxt + \catcode`\ =\active + \catcode`\^^M=\other + \catcode`\\=\active +} + +\def\macrolineargctxt{% used for whole-line arguments without braces + \scanctxt + \catcode`\{=\other + \catcode`\}=\other +} + +% \mbodybackslash is the definition of \ in @macro bodies. +% It maps \foo\ => \csname macarg.foo\endcsname => #N +% where N is the macro parameter number. +% We define \csname macarg.\endcsname to be \realbackslash, so +% \\ in macro replacement text gets you a backslash. +% +{\catcode`@=0 @catcode`@\=@active + @gdef@usembodybackslash{@let\=@mbodybackslash} + @gdef@mbodybackslash#1\{@csname macarg.#1@endcsname} +} +\expandafter\def\csname macarg.\endcsname{\realbackslash} + +\def\margbackslash#1{\char`\#1 } + +\def\macro{\recursivefalse\parsearg\macroxxx} +\def\rmacro{\recursivetrue\parsearg\macroxxx} + +\def\macroxxx#1{% + \getargs{#1}% now \macname is the macname and \argl the arglist + \ifx\argl\empty % no arguments + \paramno=0\relax + \else + \expandafter\parsemargdef \argl;% + \if\paramno>256\relax + \ifx\eTeXversion\thisisundefined + \errhelp = \EMsimple + \errmessage{You need eTeX to compile a file with macros with more than 256 arguments} + \fi + \fi + \fi + \if1\csname ismacro.\the\macname\endcsname + \message{Warning: redefining \the\macname}% + \else + \expandafter\ifx\csname \the\macname\endcsname \relax + \else \errmessage{Macro name \the\macname\space already defined}\fi + \global\cslet{macsave.\the\macname}{\the\macname}% + \global\expandafter\let\csname ismacro.\the\macname\endcsname=1% + \addtomacrolist{\the\macname}% + \fi + \begingroup \macrobodyctxt + \ifrecursive \expandafter\parsermacbody + \else \expandafter\parsemacbody + \fi} + +\parseargdef\unmacro{% + \if1\csname ismacro.#1\endcsname + \global\cslet{#1}{macsave.#1}% + \global\expandafter\let \csname ismacro.#1\endcsname=0% + % Remove the macro name from \macrolist: + \begingroup + \expandafter\let\csname#1\endcsname \relax + \let\definedummyword\unmacrodo + \xdef\macrolist{\macrolist}% + \endgroup + \else + \errmessage{Macro #1 not defined}% + \fi +} + +% Called by \do from \dounmacro on each macro. The idea is to omit any +% macro definitions that have been changed to \relax. +% +\def\unmacrodo#1{% + \ifx #1\relax + % remove this + \else + \noexpand\definedummyword \noexpand#1% + \fi +} + +% \getargs -- Parse the arguments to a @macro line. Set \macname to +% the name of the macro, and \argl to the braced argument list. +\def\getargs#1{\getargsxxx#1{}} +\def\getargsxxx#1#{\getmacname #1 \relax\getmacargs} +\def\getmacname#1 #2\relax{\macname={#1}} +\def\getmacargs#1{\def\argl{#1}} +% This made use of the feature that if the last token of a +% is #, then the preceding argument is delimited by +% an opening brace, and that opening brace is not consumed. + +% Parse the optional {params} list to @macro or @rmacro. +% Set \paramno to the number of arguments, +% and \paramlist to a parameter text for the macro (e.g. #1,#2,#3 for a +% three-param macro.) Define \macarg.BLAH for each BLAH in the params +% list to some hook where the argument is to be expanded. If there are +% less than 10 arguments that hook is to be replaced by ##N where N +% is the position in that list, that is to say the macro arguments are to be +% defined `a la TeX in the macro body. +% +% That gets used by \mbodybackslash (above). +% +% If there are 10 or more arguments, a different technique is used: see +% \parsemmanyargdef. +% +\def\parsemargdef#1;{% + \paramno=0\def\paramlist{}% + \let\hash\relax + % \hash is redefined to `#' later to get it into definitions + \let\processmacroarg\relax + \parsemargdefxxx#1,;,% + \ifnum\paramno<10\relax\else + \paramno0\relax + \parsemmanyargdef@@#1,;,% 10 or more arguments + \fi +} +\def\parsemargdefxxx#1,{% + \if#1;\let\next=\relax + \else \let\next=\parsemargdefxxx + \advance\paramno by 1 + \expandafter\edef\csname macarg.\eatspaces{#1}\endcsname + {\processmacroarg{\hash\the\paramno}}% + \edef\paramlist{\paramlist\hash\the\paramno,}% + \fi\next} + +% \parsemacbody, \parsermacbody +% +% Read recursive and nonrecursive macro bodies. (They're different since +% rec and nonrec macros end differently.) +% +% We are in \macrobodyctxt, and the \xdef causes backslashshes in the macro +% body to be transformed. +% Set \macrobody to the body of the macro, and call \defmacro. +% +{\catcode`\ =\other\long\gdef\parsemacbody#1@end macro{% +\xdef\macrobody{\eatcr{#1}}\endgroup\defmacro}}% +{\catcode`\ =\other\long\gdef\parsermacbody#1@end rmacro{% +\xdef\macrobody{\eatcr{#1}}\endgroup\defmacro}}% + +% Make @ a letter, so that we can make private-to-Texinfo macro names. +\edef\texiatcatcode{\the\catcode`\@} +\catcode `@=11\relax + +%%%%%%%%%%%%%% Code for > 10 arguments only %%%%%%%%%%%%%%%%%% + +% If there are 10 or more arguments, a different technique is used, where the +% hook remains in the body, and when macro is to be expanded the body is +% processed again to replace the arguments. +% +% In that case, the hook is \the\toks N-1, and we simply set \toks N-1 to the +% argument N value and then \edef the body (nothing else will expand because of +% the catcode regime under which the body was input). +% +% If you compile with TeX (not eTeX), and you have macros with 10 or more +% arguments, no macro can have more than 256 arguments (else error). +% +% In case that there are 10 or more arguments we parse again the arguments +% list to set new definitions for the \macarg.BLAH macros corresponding to +% each BLAH argument. It was anyhow needed to parse already once this list +% in order to count the arguments, and as macros with at most 9 arguments +% are by far more frequent than macro with 10 or more arguments, defining +% twice the \macarg.BLAH macros does not cost too much processing power. +\def\parsemmanyargdef@@#1,{% + \if#1;\let\next=\relax + \else + \let\next=\parsemmanyargdef@@ + \edef\tempb{\eatspaces{#1}}% + \expandafter\def\expandafter\tempa + \expandafter{\csname macarg.\tempb\endcsname}% + % Note that we need some extra \noexpand\noexpand, this is because we + % don't want \the to be expanded in the \parsermacbody as it uses an + % \xdef . + \expandafter\edef\tempa + {\noexpand\noexpand\noexpand\the\toks\the\paramno}% + \advance\paramno by 1\relax + \fi\next} + + +\let\endargs@\relax +\let\nil@\relax +\def\nilm@{\nil@}% +\long\def\nillm@{\nil@}% + +% This macro is expanded during the Texinfo macro expansion, not during its +% definition. It gets all the arguments' values and assigns them to macros +% macarg.ARGNAME +% +% #1 is the macro name +% #2 is the list of argument names +% #3 is the list of argument values +\def\getargvals@#1#2#3{% + \def\macargdeflist@{}% + \def\saveparamlist@{#2}% Need to keep a copy for parameter expansion. + \def\paramlist{#2,\nil@}% + \def\macroname{#1}% + \begingroup + \macroargctxt + \def\argvaluelist{#3,\nil@}% + \def\@tempa{#3}% + \ifx\@tempa\empty + \setemptyargvalues@ + \else + \getargvals@@ + \fi +} +\def\getargvals@@{% + \ifx\paramlist\nilm@ + % Some sanity check needed here that \argvaluelist is also empty. + \ifx\argvaluelist\nillm@ + \else + \errhelp = \EMsimple + \errmessage{Too many arguments in macro `\macroname'!}% + \fi + \let\next\macargexpandinbody@ + \else + \ifx\argvaluelist\nillm@ + % No more arguments values passed to macro. Set remaining named-arg + % macros to empty. + \let\next\setemptyargvalues@ + \else + % pop current arg name into \@tempb + \def\@tempa##1{\pop@{\@tempb}{\paramlist}##1\endargs@}% + \expandafter\@tempa\expandafter{\paramlist}% + % pop current argument value into \@tempc + \def\@tempa##1{\longpop@{\@tempc}{\argvaluelist}##1\endargs@}% + \expandafter\@tempa\expandafter{\argvaluelist}% + % Here \@tempb is the current arg name and \@tempc is the current arg value. + % First place the new argument macro definition into \@tempd + \expandafter\macname\expandafter{\@tempc}% + \expandafter\let\csname macarg.\@tempb\endcsname\relax + \expandafter\def\expandafter\@tempe\expandafter{% + \csname macarg.\@tempb\endcsname}% + \edef\@tempd{\long\def\@tempe{\the\macname}}% + \push@\@tempd\macargdeflist@ + \let\next\getargvals@@ + \fi + \fi + \next +} + +\def\push@#1#2{% + \expandafter\expandafter\expandafter\def + \expandafter\expandafter\expandafter#2% + \expandafter\expandafter\expandafter{% + \expandafter#1#2}% +} + +% Replace arguments by their values in the macro body, and place the result +% in macro \@tempa. +% +\def\macvalstoargs@{% + % To do this we use the property that token registers that are \the'ed + % within an \edef expand only once. So we are going to place all argument + % values into respective token registers. + % + % First we save the token context, and initialize argument numbering. + \begingroup + \paramno0\relax + % Then, for each argument number #N, we place the corresponding argument + % value into a new token list register \toks#N + \expandafter\putargsintokens@\saveparamlist@,;,% + % Then, we expand the body so that argument are replaced by their + % values. The trick for values not to be expanded themselves is that they + % are within tokens and that tokens expand only once in an \edef . + \edef\@tempc{\csname mac.\macroname .body\endcsname}% + % Now we restore the token stack pointer to free the token list registers + % which we have used, but we make sure that expanded body is saved after + % group. + \expandafter + \endgroup + \expandafter\def\expandafter\@tempa\expandafter{\@tempc}% + } + +% Define the named-macro outside of this group and then close this group. +% +\def\macargexpandinbody@{% + \expandafter + \endgroup + \macargdeflist@ + % First the replace in body the macro arguments by their values, the result + % is in \@tempa . + \macvalstoargs@ + % Then we point at the \norecurse or \gobble (for recursive) macro value + % with \@tempb . + \expandafter\let\expandafter\@tempb\csname mac.\macroname .recurse\endcsname + % Depending on whether it is recursive or not, we need some tailing + % \egroup . + \ifx\@tempb\gobble + \let\@tempc\relax + \else + \let\@tempc\egroup + \fi + % And now we do the real job: + \edef\@tempd{\noexpand\@tempb{\macroname}\noexpand\scanmacro{\@tempa}\@tempc}% + \@tempd +} + +\def\putargsintokens@#1,{% + \if#1;\let\next\relax + \else + \let\next\putargsintokens@ + % First we allocate the new token list register, and give it a temporary + % alias \@tempb . + \toksdef\@tempb\the\paramno + % Then we place the argument value into that token list register. + \expandafter\let\expandafter\@tempa\csname macarg.#1\endcsname + \expandafter\@tempb\expandafter{\@tempa}% + \advance\paramno by 1\relax + \fi + \next +} + +% Trailing missing arguments are set to empty. +% +\def\setemptyargvalues@{% + \ifx\paramlist\nilm@ + \let\next\macargexpandinbody@ + \else + \expandafter\setemptyargvaluesparser@\paramlist\endargs@ + \let\next\setemptyargvalues@ + \fi + \next +} + +\def\setemptyargvaluesparser@#1,#2\endargs@{% + \expandafter\def\expandafter\@tempa\expandafter{% + \expandafter\def\csname macarg.#1\endcsname{}}% + \push@\@tempa\macargdeflist@ + \def\paramlist{#2}% +} + +% #1 is the element target macro +% #2 is the list macro +% #3,#4\endargs@ is the list value +\def\pop@#1#2#3,#4\endargs@{% + \def#1{#3}% + \def#2{#4}% +} +\long\def\longpop@#1#2#3,#4\endargs@{% + \long\def#1{#3}% + \long\def#2{#4}% +} + + +%%%%%%%%%%%%%% End of code for > 10 arguments %%%%%%%%%%%%%%%%%% + + + +% Remove following spaces at the expansion stage. +% This works because spaces are discarded before each argument when TeX is +% getting the arguments for a macro. +% This must not be immediately followed by a }. +\long\def\gobblespaces#1{#1} + +% This defines a Texinfo @macro or @rmacro, called by \parsemacbody. +% \macrobody has the body of the macro in it, with placeholders for +% its parameters, looking like "\processmacroarg{\hash 1}". +% \paramno is the number of parameters +% \paramlist is a TeX parameter text, e.g. "#1,#2,#3," +% There are eight cases: recursive and nonrecursive macros of zero, one, +% up to nine, and many arguments. +% \xdef is used so that macro definitions will survive the file +% they're defined in: @include reads the file inside a group. +% +\def\defmacro{% + \let\hash=##% convert placeholders to macro parameter chars + \ifnum\paramno=1 + \def\processmacroarg{\gobblespaces}% + % This removes the pair of braces around the argument. We don't + % use \eatspaces, because this can cause ends of lines to be lost + % when the argument to \eatspaces is read, leading to line-based + % commands like "@itemize" not being read correctly. + \else + \def\processmacroarg{\xprocessmacroarg}% + \let\xprocessmacroarg\relax + \fi + \ifrecursive %%%%%%%%%%%%%% Recursive %%%%%%%%%%%%%%%%%%%%%%%%%%%%% + \ifcase\paramno + % 0 + \expandafter\xdef\csname\the\macname\endcsname{% + \noexpand\scanmacro{\macrobody}}% + \or % 1 + \expandafter\xdef\csname\the\macname\endcsname{% + \bgroup + \noexpand\braceorline + \expandafter\noexpand\csname\the\macname @@@\endcsname}% + \expandafter\xdef\csname\the\macname @@@\endcsname##1{% + \expandafter\noexpand\csname\the\macname @@@@\endcsname{% + \noexpand\gobblespaces##1\empty}% + % The \empty is for \gobblespaces in case #1 is empty + }% + \expandafter\xdef\csname\the\macname @@@@\endcsname##1{% + \egroup\noexpand\scanmacro{\macrobody}}% + \else + \ifnum\paramno<10\relax % at most 9 + % See non-recursive section below for comments + \expandafter\xdef\csname\the\macname\endcsname{% + \bgroup + \noexpand\expandafter + \noexpand\macroargctxt + \noexpand\expandafter + \expandafter\noexpand\csname\the\macname @@\endcsname}% + \expandafter\xdef\csname\the\macname @@\endcsname##1{% + \noexpand\passargtomacro + \expandafter\noexpand\csname\the\macname @@@\endcsname{##1,}}% + \expandafter\xdef\csname\the\macname @@@\endcsname##1{% + \expandafter\noexpand\csname\the\macname @@@@\endcsname ##1}% + \expandafter\expandafter + \expandafter\xdef + \expandafter\expandafter + \csname\the\macname @@@@\endcsname\paramlist{% + \egroup\noexpand\scanmacro{\macrobody}}% + \else % 10 or more + \expandafter\xdef\csname\the\macname\endcsname{% + \noexpand\getargvals@{\the\macname}{\argl}% + }% + \global\expandafter\let\csname mac.\the\macname .body\endcsname\macrobody + \global\expandafter\let\csname mac.\the\macname .recurse\endcsname\gobble + \fi + \fi + \else %%%%%%%%%%%%%%%%%%%%%% Non-recursive %%%%%%%%%%%%%%%%%%%%%%%%%% + \ifcase\paramno + % 0 + \expandafter\xdef\csname\the\macname\endcsname{% + \noexpand\scanmacro{\macrobody}}% + \or % 1 + \expandafter\xdef\csname\the\macname\endcsname{% + \bgroup + \noexpand\braceorline + \expandafter\noexpand\csname\the\macname @@@\endcsname}% + \expandafter\xdef\csname\the\macname @@@\endcsname##1{% + \expandafter\noexpand\csname\the\macname @@@@\endcsname{% + \noexpand\gobblespaces##1\empty}% + % The \empty is for \gobblespaces in case #1 is empty + }% + \expandafter\xdef\csname\the\macname @@@@\endcsname##1{% + \egroup + \noexpand\scanmacro{\macrobody}% + }% + \else % at most 9 + \ifnum\paramno<10\relax + % @MACNAME sets the context for reading the macro argument + % @MACNAME@@ gets the argument, processes backslashes and appends a + % comma. + % @MACNAME@@@ removes braces surrounding the argument list. + % @MACNAME@@@@ scans the macro body with arguments substituted. + \expandafter\xdef\csname\the\macname\endcsname{% + \bgroup + \noexpand\expandafter % This \expandafter skip any spaces after the + \noexpand\macroargctxt % macro before we change the catcode of space. + \noexpand\expandafter + \expandafter\noexpand\csname\the\macname @@\endcsname}% + \expandafter\xdef\csname\the\macname @@\endcsname##1{% + \noexpand\passargtomacro + \expandafter\noexpand\csname\the\macname @@@\endcsname{##1,}}% + \expandafter\xdef\csname\the\macname @@@\endcsname##1{% + \expandafter\noexpand\csname\the\macname @@@@\endcsname ##1}% + \expandafter\expandafter + \expandafter\xdef + \expandafter\expandafter + \csname\the\macname @@@@\endcsname\paramlist{% + \egroup\noexpand\scanmacro{\macrobody}}% + \else % 10 or more: + \expandafter\xdef\csname\the\macname\endcsname{% + \noexpand\getargvals@{\the\macname}{\argl}% + }% + \global\expandafter\let\csname mac.\the\macname .body\endcsname\macrobody + \global\expandafter\let\csname mac.\the\macname .recurse\endcsname\norecurse + \fi + \fi + \fi} + +\catcode `\@\texiatcatcode\relax % end private-to-Texinfo catcodes + +\def\norecurse#1{\bgroup\cslet{#1}{macsave.#1}} + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +{\catcode`\@=0 \catcode`\\=13 % We need to manipulate \ so use @ as escape +@catcode`@_=11 % private names +@catcode`@!=11 % used as argument separator + +% \passargtomacro#1#2 - +% Call #1 with a list of tokens #2, with any doubled backslashes in #2 +% compressed to one. +% +% This implementation works by expansion, and not execution (so we cannot use +% \def or similar). This reduces the risk of this failing in contexts where +% complete expansion is done with no execution (for example, in writing out to +% an auxiliary file for an index entry). +% +% State is kept in the input stream: the argument passed to +% @look_ahead, @gobble_and_check_finish and @add_segment is +% +% THE_MACRO ARG_RESULT ! {PENDING_BS} NEXT_TOKEN (... rest of input) +% +% where: +% THE_MACRO - name of the macro we want to call +% ARG_RESULT - argument list we build to pass to that macro +% PENDING_BS - either a backslash or nothing +% NEXT_TOKEN - used to look ahead in the input stream to see what's coming next + +@gdef@passargtomacro#1#2{% + @add_segment #1!{}@relax#2\@_finish\% +} +@gdef@_finish{@_finishx} @global@let@_finishx@relax + +% #1 - THE_MACRO ARG_RESULT +% #2 - PENDING_BS +% #3 - NEXT_TOKEN +% #4 used to look ahead +% +% If the next token is not a backslash, process the rest of the argument; +% otherwise, remove the next token. +@gdef@look_ahead#1!#2#3#4{% + @ifx#4\% + @expandafter@gobble_and_check_finish + @else + @expandafter@add_segment + @fi#1!{#2}#4#4% +} + +% #1 - THE_MACRO ARG_RESULT +% #2 - PENDING_BS +% #3 - NEXT_TOKEN +% #4 should be a backslash, which is gobbled. +% #5 looks ahead +% +% Double backslash found. Add a single backslash, and look ahead. +@gdef@gobble_and_check_finish#1!#2#3#4#5{% + @add_segment#1\!{}#5#5% +} + +@gdef@is_fi{@fi} + +% #1 - THE_MACRO ARG_RESULT +% #2 - PENDING_BS +% #3 - NEXT_TOKEN +% #4 is input stream until next backslash +% +% Input stream is either at the start of the argument, or just after a +% backslash sequence, either a lone backslash, or a doubled backslash. +% NEXT_TOKEN contains the first token in the input stream: if it is \finish, +% finish; otherwise, append to ARG_RESULT the segment of the argument up until +% the next backslash. PENDING_BACKSLASH contains a backslash to represent +% a backslash just before the start of the input stream that has not been +% added to ARG_RESULT. +@gdef@add_segment#1!#2#3#4\{% +@ifx#3@_finish + @call_the_macro#1!% +@else + % append the pending backslash to the result, followed by the next segment + @expandafter@is_fi@look_ahead#1#2#4!{\}@fi + % this @fi is discarded by @look_ahead. + % we can't get rid of it with \expandafter because we don't know how + % long #4 is. +} + +% #1 - THE_MACRO +% #2 - ARG_RESULT +% #3 discards the res of the conditional in @add_segment, and @is_fi ends the +% conditional. +@gdef@call_the_macro#1#2!#3@fi{@is_fi #1{#2}} + +} +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +% \braceorline MAC is used for a one-argument macro MAC. It checks +% whether the next non-whitespace character is a {. It sets the context +% for reading the argument (slightly different in the two cases). Then, +% to read the argument, in the whole-line case, it then calls the regular +% \parsearg MAC; in the lbrace case, it calls \passargtomacro MAC. +% +\def\braceorline#1{\let\macnamexxx=#1\futurelet\nchar\braceorlinexxx} +\def\braceorlinexxx{% + \ifx\nchar\bgroup + \macroargctxt + \expandafter\passargtomacro + \else + \macrolineargctxt\expandafter\parsearg + \fi \macnamexxx} + + +% @alias. +% We need some trickery to remove the optional spaces around the equal +% sign. Make them active and then expand them all to nothing. +% +\def\alias{\parseargusing\obeyspaces\aliasxxx} +\def\aliasxxx #1{\aliasyyy#1\relax} +\def\aliasyyy #1=#2\relax{% + {% + \expandafter\let\obeyedspace=\empty + \addtomacrolist{#1}% + \xdef\next{\global\let\makecsname{#1}=\makecsname{#2}}% + }% + \next +} + + +\message{cross references,} + +\newwrite\auxfile +\newif\ifhavexrefs % True if xref values are known. +\newif\ifwarnedxrefs % True if we warned once that they aren't known. + +% @inforef is relatively simple. +\def\inforef #1{\inforefzzz #1,,,,**} +\def\inforefzzz #1,#2,#3,#4**{% + \putwordSee{} \putwordInfo{} \putwordfile{} \file{\ignorespaces #3{}}, + node \samp{\ignorespaces#1{}}} + +% @node's only job in TeX is to define \lastnode, which is used in +% cross-references. The @node line might or might not have commas, and +% might or might not have spaces before the first comma, like: +% @node foo , bar , ... +% We don't want such trailing spaces in the node name. +% +\parseargdef\node{\checkenv{}\donode #1 ,\finishnodeparse} +% +% also remove a trailing comma, in case of something like this: +% @node Help-Cross, , , Cross-refs +\def\donode#1 ,#2\finishnodeparse{\dodonode #1,\finishnodeparse} +\def\dodonode#1,#2\finishnodeparse{\gdef\lastnode{#1}} + +\let\nwnode=\node +\let\lastnode=\empty + +% Write a cross-reference definition for the current node. #1 is the +% type (Ynumbered, Yappendix, Ynothing). +% +\def\donoderef#1{% + \ifx\lastnode\empty\else + \setref{\lastnode}{#1}% + \global\let\lastnode=\empty + \fi +} + +% @anchor{NAME} -- define xref target at arbitrary point. +% +\newcount\savesfregister +% +\def\savesf{\relax \ifhmode \savesfregister=\spacefactor \fi} +\def\restoresf{\relax \ifhmode \spacefactor=\savesfregister \fi} +\def\anchor#1{\savesf \setref{#1}{Ynothing}\restoresf \ignorespaces} + +% \setref{NAME}{SNT} defines a cross-reference point NAME (a node or an +% anchor), which consists of three parts: +% 1) NAME-title - the current sectioning name taken from \lastsection, +% or the anchor name. +% 2) NAME-snt - section number and type, passed as the SNT arg, or +% empty for anchors. +% 3) NAME-pg - the page number. +% +% This is called from \donoderef, \anchor, and \dofloat. In the case of +% floats, there is an additional part, which is not written here: +% 4) NAME-lof - the text as it should appear in a @listoffloats. +% +\def\setref#1#2{% + \pdfmkdest{#1}% + \iflinks + {% + \requireauxfile + \atdummies % preserve commands, but don't expand them + \edef\writexrdef##1##2{% + \write\auxfile{@xrdef{#1-% #1 of \setref, expanded by the \edef + ##1}{##2}}% these are parameters of \writexrdef + }% + \toks0 = \expandafter{\lastsection}% + \immediate \writexrdef{title}{\the\toks0 }% + \immediate \writexrdef{snt}{\csname #2\endcsname}% \Ynumbered etc. + \safewhatsit{\writexrdef{pg}{\folio}}% will be written later, at \shipout + }% + \fi +} + +% @xrefautosectiontitle on|off says whether @section(ing) names are used +% automatically in xrefs, if the third arg is not explicitly specified. +% This was provided as a "secret" @set xref-automatic-section-title +% variable, now it's official. +% +\parseargdef\xrefautomaticsectiontitle{% + \def\temp{#1}% + \ifx\temp\onword + \expandafter\let\csname SETxref-automatic-section-title\endcsname + = \empty + \else\ifx\temp\offword + \expandafter\let\csname SETxref-automatic-section-title\endcsname + = \relax + \else + \errhelp = \EMsimple + \errmessage{Unknown @xrefautomaticsectiontitle value `\temp', + must be on|off}% + \fi\fi +} + +% +% @xref, @pxref, and @ref generate cross-references. For \xrefX, #1 is +% the node name, #2 the name of the Info cross-reference, #3 the printed +% node name, #4 the name of the Info file, #5 the name of the printed +% manual. All but the node name can be omitted. +% +\def\pxref{\putwordsee{} \xrefXX} +\def\xref{\putwordSee{} \xrefXX} +\def\ref{\xrefXX} + +\def\xrefXX#1{\def\xrefXXarg{#1}\futurelet\tokenafterxref\xrefXXX} +\def\xrefXXX{\expandafter\xrefX\expandafter[\xrefXXarg,,,,,,,]} +% +\newbox\toprefbox +\newbox\printedrefnamebox +\newbox\infofilenamebox +\newbox\printedmanualbox +% +\def\xrefX[#1,#2,#3,#4,#5,#6]{\begingroup + \unsepspaces + % + % Get args without leading/trailing spaces. + \def\printedrefname{\ignorespaces #3}% + \setbox\printedrefnamebox = \hbox{\printedrefname\unskip}% + % + \def\infofilename{\ignorespaces #4}% + \setbox\infofilenamebox = \hbox{\infofilename\unskip}% + % + \def\printedmanual{\ignorespaces #5}% + \setbox\printedmanualbox = \hbox{\printedmanual\unskip}% + % + % If the printed reference name (arg #3) was not explicitly given in + % the @xref, figure out what we want to use. + \ifdim \wd\printedrefnamebox = 0pt + % No printed node name was explicitly given. + \expandafter\ifx\csname SETxref-automatic-section-title\endcsname \relax + % Not auto section-title: use node name inside the square brackets. + \def\printedrefname{\ignorespaces #1}% + \else + % Auto section-title: use chapter/section title inside + % the square brackets if we have it. + \ifdim \wd\printedmanualbox > 0pt + % It is in another manual, so we don't have it; use node name. + \def\printedrefname{\ignorespaces #1}% + \else + \ifhavexrefs + % We (should) know the real title if we have the xref values. + \def\printedrefname{\refx{#1-title}{}}% + \else + % Otherwise just copy the Info node name. + \def\printedrefname{\ignorespaces #1}% + \fi% + \fi + \fi + \fi + % + % Make link in pdf output. + \ifpdf + {\indexnofonts + \turnoffactive + \makevalueexpandable + % This expands tokens, so do it after making catcode changes, so _ + % etc. don't get their TeX definitions. This ignores all spaces in + % #4, including (wrongly) those in the middle of the filename. + \getfilename{#4}% + % + % This (wrongly) does not take account of leading or trailing + % spaces in #1, which should be ignored. + \edef\pdfxrefdest{#1}% + \ifx\pdfxrefdest\empty + \def\pdfxrefdest{Top}% no empty targets + \else + \txiescapepdf\pdfxrefdest % escape PDF special chars + \fi + % + \leavevmode + \startlink attr{/Border [0 0 0]}% + \ifnum\filenamelength>0 + goto file{\the\filename.pdf} name{\pdfxrefdest}% + \else + goto name{\pdfmkpgn{\pdfxrefdest}}% + \fi + }% + \setcolor{\linkcolor}% + \fi + {% + % Have to otherify everything special to allow the \csname to + % include an _ in the xref name, etc. + \indexnofonts + \turnoffactive + \expandafter\global\expandafter\let\expandafter\Xthisreftitle + \csname XR#1-title\endcsname + }% + % + % Float references are printed completely differently: "Figure 1.2" + % instead of "[somenode], p.3". \iffloat distinguishes them by + % \Xthisreftitle being set to a magic string. + \iffloat\Xthisreftitle + % If the user specified the print name (third arg) to the ref, + % print it instead of our usual "Figure 1.2". + \ifdim\wd\printedrefnamebox = 0pt + \refx{#1-snt}{}% + \else + \printedrefname + \fi + % + % If the user also gave the printed manual name (fifth arg), append + % "in MANUALNAME". + \ifdim \wd\printedmanualbox > 0pt + \space \putwordin{} \cite{\printedmanual}% + \fi + \else + % node/anchor (non-float) references. + % + % If we use \unhbox to print the node names, TeX does not insert + % empty discretionaries after hyphens, which means that it will not + % find a line break at a hyphen in a node names. Since some manuals + % are best written with fairly long node names, containing hyphens, + % this is a loss. Therefore, we give the text of the node name + % again, so it is as if TeX is seeing it for the first time. + % + \ifdim \wd\printedmanualbox > 0pt + % Cross-manual reference with a printed manual name. + % + \crossmanualxref{\cite{\printedmanual\unskip}}% + % + \else\ifdim \wd\infofilenamebox > 0pt + % Cross-manual reference with only an info filename (arg 4), no + % printed manual name (arg 5). This is essentially the same as + % the case above; we output the filename, since we have nothing else. + % + \crossmanualxref{\code{\infofilename\unskip}}% + % + \else + % Reference within this manual. + % + % _ (for example) has to be the character _ for the purposes of the + % control sequence corresponding to the node, but it has to expand + % into the usual \leavevmode...\vrule stuff for purposes of + % printing. So we \turnoffactive for the \refx-snt, back on for the + % printing, back off for the \refx-pg. + {\turnoffactive + % Only output a following space if the -snt ref is nonempty; for + % @unnumbered and @anchor, it won't be. + \setbox2 = \hbox{\ignorespaces \refx{#1-snt}{}}% + \ifdim \wd2 > 0pt \refx{#1-snt}\space\fi + }% + % output the `[mynode]' via the macro below so it can be overridden. + \xrefprintnodename\printedrefname + % + % But we always want a comma and a space: + ,\space + % + % output the `page 3'. + \turnoffactive \putwordpage\tie\refx{#1-pg}{}% + % Add a , if xref followed by a space + \if\space\noexpand\tokenafterxref ,% + \else\ifx\ \tokenafterxref ,% @TAB + \else\ifx\*\tokenafterxref ,% @* + \else\ifx\ \tokenafterxref ,% @SPACE + \else\ifx\ + \tokenafterxref ,% @NL + \else\ifx\tie\tokenafterxref ,% @tie + \fi\fi\fi\fi\fi\fi + \fi\fi + \fi + \endlink +\endgroup} + +% Output a cross-manual xref to #1. Used just above (twice). +% +% Only include the text "Section ``foo'' in" if the foo is neither +% missing or Top. Thus, @xref{,,,foo,The Foo Manual} outputs simply +% "see The Foo Manual", the idea being to refer to the whole manual. +% +% But, this being TeX, we can't easily compare our node name against the +% string "Top" while ignoring the possible spaces before and after in +% the input. By adding the arbitrary 7sp below, we make it much less +% likely that a real node name would have the same width as "Top" (e.g., +% in a monospaced font). Hopefully it will never happen in practice. +% +% For the same basic reason, we retypeset the "Top" at every +% reference, since the current font is indeterminate. +% +\def\crossmanualxref#1{% + \setbox\toprefbox = \hbox{Top\kern7sp}% + \setbox2 = \hbox{\ignorespaces \printedrefname \unskip \kern7sp}% + \ifdim \wd2 > 7sp % nonempty? + \ifdim \wd2 = \wd\toprefbox \else % same as Top? + \putwordSection{} ``\printedrefname'' \putwordin{}\space + \fi + \fi + #1% +} + +% This macro is called from \xrefX for the `[nodename]' part of xref +% output. It's a separate macro only so it can be changed more easily, +% since square brackets don't work well in some documents. Particularly +% one that Bob is working on :). +% +\def\xrefprintnodename#1{[#1]} + +% Things referred to by \setref. +% +\def\Ynothing{} +\def\Yomitfromtoc{} +\def\Ynumbered{% + \ifnum\secno=0 + \putwordChapter@tie \the\chapno + \else \ifnum\subsecno=0 + \putwordSection@tie \the\chapno.\the\secno + \else \ifnum\subsubsecno=0 + \putwordSection@tie \the\chapno.\the\secno.\the\subsecno + \else + \putwordSection@tie \the\chapno.\the\secno.\the\subsecno.\the\subsubsecno + \fi\fi\fi +} +\def\Yappendix{% + \ifnum\secno=0 + \putwordAppendix@tie @char\the\appendixno{}% + \else \ifnum\subsecno=0 + \putwordSection@tie @char\the\appendixno.\the\secno + \else \ifnum\subsubsecno=0 + \putwordSection@tie @char\the\appendixno.\the\secno.\the\subsecno + \else + \putwordSection@tie + @char\the\appendixno.\the\secno.\the\subsecno.\the\subsubsecno + \fi\fi\fi +} + +% Define \refx{NAME}{SUFFIX} to reference a cross-reference string named NAME. +% If its value is nonempty, SUFFIX is output afterward. +% +\def\refx#1#2{% + \requireauxfile + {% + \indexnofonts + \otherbackslash + \expandafter\global\expandafter\let\expandafter\thisrefX + \csname XR#1\endcsname + }% + \ifx\thisrefX\relax + % If not defined, say something at least. + \angleleft un\-de\-fined\angleright + \iflinks + \ifhavexrefs + {\toks0 = {#1}% avoid expansion of possibly-complex value + \message{\linenumber Undefined cross reference `\the\toks0'.}}% + \else + \ifwarnedxrefs\else + \global\warnedxrefstrue + \message{Cross reference values unknown; you must run TeX again.}% + \fi + \fi + \fi + \else + % It's defined, so just use it. + \thisrefX + \fi + #2% Output the suffix in any case. +} + +% This is the macro invoked by entries in the aux file. Usually it's +% just a \def (we prepend XR to the control sequence name to avoid +% collisions). But if this is a float type, we have more work to do. +% +\def\xrdef#1#2{% + {% The node name might contain 8-bit characters, which in our current + % implementation are changed to commands like @'e. Don't let these + % mess up the control sequence name. + \indexnofonts + \turnoffactive + \xdef\safexrefname{#1}% + }% + % + \expandafter\gdef\csname XR\safexrefname\endcsname{#2}% remember this xref + % + % Was that xref control sequence that we just defined for a float? + \expandafter\iffloat\csname XR\safexrefname\endcsname + % it was a float, and we have the (safe) float type in \iffloattype. + \expandafter\let\expandafter\floatlist + \csname floatlist\iffloattype\endcsname + % + % Is this the first time we've seen this float type? + \expandafter\ifx\floatlist\relax + \toks0 = {\do}% yes, so just \do + \else + % had it before, so preserve previous elements in list. + \toks0 = \expandafter{\floatlist\do}% + \fi + % + % Remember this xref in the control sequence \floatlistFLOATTYPE, + % for later use in \listoffloats. + \expandafter\xdef\csname floatlist\iffloattype\endcsname{\the\toks0 + {\safexrefname}}% + \fi +} + +% If working on a large document in chapters, it is convenient to +% be able to disable indexing, cross-referencing, and contents, for test runs. +% This is done with @novalidate at the beginning of the file. +% +\newif\iflinks \linkstrue % by default we want the aux files. +\let\novalidate = \linksfalse + +% Used when writing to the aux file, or when using data from it. +\def\requireauxfile{% + \iflinks + \tryauxfile + % Open the new aux file. TeX will close it automatically at exit. + \immediate\openout\auxfile=\jobname.aux + \fi + \global\let\requireauxfile=\relax % Only do this once. +} + +% Read the last existing aux file, if any. No error if none exists. +% +\def\tryauxfile{% + \openin 1 \jobname.aux + \ifeof 1 \else + \readdatafile{aux}% + \global\havexrefstrue + \fi + \closein 1 +} + +\def\setupdatafile{% + \catcode`\^^@=\other + \catcode`\^^A=\other + \catcode`\^^B=\other + \catcode`\^^C=\other + \catcode`\^^D=\other + \catcode`\^^E=\other + \catcode`\^^F=\other + \catcode`\^^G=\other + \catcode`\^^H=\other + \catcode`\^^K=\other + \catcode`\^^L=\other + \catcode`\^^N=\other + \catcode`\^^P=\other + \catcode`\^^Q=\other + \catcode`\^^R=\other + \catcode`\^^S=\other + \catcode`\^^T=\other + \catcode`\^^U=\other + \catcode`\^^V=\other + \catcode`\^^W=\other + \catcode`\^^X=\other + \catcode`\^^Z=\other + \catcode`\^^[=\other + \catcode`\^^\=\other + \catcode`\^^]=\other + \catcode`\^^^=\other + \catcode`\^^_=\other + % It was suggested to set the catcode of ^ to 7, which would allow ^^e4 etc. + % in xref tags, i.e., node names. But since ^^e4 notation isn't + % supported in the main text, it doesn't seem desirable. Furthermore, + % that is not enough: for node names that actually contain a ^ + % character, we would end up writing a line like this: 'xrdef {'hat + % b-title}{'hat b} and \xrdef does a \csname...\endcsname on the first + % argument, and \hat is not an expandable control sequence. It could + % all be worked out, but why? Either we support ^^ or we don't. + % + % The other change necessary for this was to define \auxhat: + % \def\auxhat{\def^{'hat }}% extra space so ok if followed by letter + % and then to call \auxhat in \setq. + % + \catcode`\^=\other + % + % Special characters. Should be turned off anyway, but... + \catcode`\~=\other + \catcode`\[=\other + \catcode`\]=\other + \catcode`\"=\other + \catcode`\_=\other + \catcode`\|=\other + \catcode`\<=\other + \catcode`\>=\other + \catcode`\$=\other + \catcode`\#=\other + \catcode`\&=\other + \catcode`\%=\other + \catcode`+=\other % avoid \+ for paranoia even though we've turned it off + % + % This is to support \ in node names and titles, since the \ + % characters end up in a \csname. It's easier than + % leaving it active and making its active definition an actual \ + % character. What I don't understand is why it works in the *value* + % of the xrdef. Seems like it should be a catcode12 \, and that + % should not typeset properly. But it works, so I'm moving on for + % now. --karl, 15jan04. + \catcode`\\=\other + % + % Make the characters 128-255 be printing characters. + {\setnonasciicharscatcodenonglobal\other}% + % + % @ is our escape character in .aux files, and we need braces. + \catcode`\{=1 + \catcode`\}=2 + \catcode`\@=0 +} + +\def\readdatafile#1{% +\begingroup + \setupdatafile + \input\jobname.#1 +\endgroup} + + +\message{insertions,} +% including footnotes. + +\newcount \footnoteno + +% The trailing space in the following definition for supereject is +% vital for proper filling; pages come out unaligned when you do a +% pagealignmacro call if that space before the closing brace is +% removed. (Generally, numeric constants should always be followed by a +% space to prevent strange expansion errors.) +\def\supereject{\par\penalty -20000\footnoteno =0 } + +% @footnotestyle is meaningful for Info output only. +\let\footnotestyle=\comment + +{\catcode `\@=11 +% +% Auto-number footnotes. Otherwise like plain. +\gdef\footnote{% + \global\advance\footnoteno by \@ne + \edef\thisfootno{$^{\the\footnoteno}$}% + % + % In case the footnote comes at the end of a sentence, preserve the + % extra spacing after we do the footnote number. + \let\@sf\empty + \ifhmode\edef\@sf{\spacefactor\the\spacefactor}\ptexslash\fi + % + % Remove inadvertent blank space before typesetting the footnote number. + \unskip + \thisfootno\@sf + \dofootnote +}% + +% Don't bother with the trickery in plain.tex to not require the +% footnote text as a parameter. Our footnotes don't need to be so general. +% +% Oh yes, they do; otherwise, @ifset (and anything else that uses +% \parseargline) fails inside footnotes because the tokens are fixed when +% the footnote is read. --karl, 16nov96. +% +\gdef\dofootnote{% + \insert\footins\bgroup + % + % Nested footnotes are not supported in TeX, that would take a lot + % more work. (\startsavinginserts does not suffice.) + \let\footnote=\errfootnotenest + % + % We want to typeset this text as a normal paragraph, even if the + % footnote reference occurs in (for example) a display environment. + % So reset some parameters. + \hsize=\pagewidth + \interlinepenalty\interfootnotelinepenalty + \splittopskip\ht\strutbox % top baseline for broken footnotes + \splitmaxdepth\dp\strutbox + \floatingpenalty\@MM + \leftskip\z@skip + \rightskip\z@skip + \spaceskip\z@skip + \xspaceskip\z@skip + \parindent\defaultparindent + % + \smallfonts \rm + % + % Because we use hanging indentation in footnotes, a @noindent appears + % to exdent this text, so make it be a no-op. makeinfo does not use + % hanging indentation so @noindent can still be needed within footnote + % text after an @example or the like (not that this is good style). + \let\noindent = \relax + % + % Hang the footnote text off the number. Use \everypar in case the + % footnote extends for more than one paragraph. + \everypar = {\hang}% + \textindent{\thisfootno}% + % + % Don't crash into the line above the footnote text. Since this + % expands into a box, it must come within the paragraph, lest it + % provide a place where TeX can split the footnote. + \footstrut + % + % Invoke rest of plain TeX footnote routine. + \futurelet\next\fo@t +} +}%end \catcode `\@=11 + +\def\errfootnotenest{% + \errhelp=\EMsimple + \errmessage{Nested footnotes not supported in texinfo.tex, + even though they work in makeinfo; sorry} +} + +\def\errfootnoteheading{% + \errhelp=\EMsimple + \errmessage{Footnotes in chapters, sections, etc., are not supported} +} + +% In case a @footnote appears in a vbox, save the footnote text and create +% the real \insert just after the vbox finished. Otherwise, the insertion +% would be lost. +% Similarly, if a @footnote appears inside an alignment, save the footnote +% text to a box and make the \insert when a row of the table is finished. +% And the same can be done for other insert classes. --kasal, 16nov03. +% +% Replace the \insert primitive by a cheating macro. +% Deeper inside, just make sure that the saved insertions are not spilled +% out prematurely. +% +\def\startsavinginserts{% + \ifx \insert\ptexinsert + \let\insert\saveinsert + \else + \let\checkinserts\relax + \fi +} + +% This \insert replacement works for both \insert\footins{foo} and +% \insert\footins\bgroup foo\egroup, but it doesn't work for \insert27{foo}. +% +\def\saveinsert#1{% + \edef\next{\noexpand\savetobox \makeSAVEname#1}% + \afterassignment\next + % swallow the left brace + \let\temp = +} +\def\makeSAVEname#1{\makecsname{SAVE\expandafter\gobble\string#1}} +\def\savetobox#1{\global\setbox#1 = \vbox\bgroup \unvbox#1} + +\def\checksaveins#1{\ifvoid#1\else \placesaveins#1\fi} + +\def\placesaveins#1{% + \ptexinsert \csname\expandafter\gobblesave\string#1\endcsname + {\box#1}% +} + +% eat @SAVE -- beware, all of them have catcode \other: +{ + \def\dospecials{\do S\do A\do V\do E} \uncatcodespecials % ;-) + \gdef\gobblesave @SAVE{} +} + +% initialization: +\def\newsaveins #1{% + \edef\next{\noexpand\newsaveinsX \makeSAVEname#1}% + \next +} +\def\newsaveinsX #1{% + \csname newbox\endcsname #1% + \expandafter\def\expandafter\checkinserts\expandafter{\checkinserts + \checksaveins #1}% +} + +% initialize: +\let\checkinserts\empty +\newsaveins\footins +\newsaveins\margin + + +% @image. We use the macros from epsf.tex to support this. +% If epsf.tex is not installed and @image is used, we complain. +% +% Check for and read epsf.tex up front. If we read it only at @image +% time, we might be inside a group, and then its definitions would get +% undone and the next image would fail. +\openin 1 = epsf.tex +\ifeof 1 \else + % Do not bother showing banner with epsf.tex v2.7k (available in + % doc/epsf.tex and on ctan). + \def\epsfannounce{\toks0 = }% + \input epsf.tex +\fi +\closein 1 +% +% We will only complain once about lack of epsf.tex. +\newif\ifwarnednoepsf +\newhelp\noepsfhelp{epsf.tex must be installed for images to + work. It is also included in the Texinfo distribution, or you can get + it from ftp://tug.org/tex/epsf.tex.} +% +\def\image#1{% + \ifx\epsfbox\thisisundefined + \ifwarnednoepsf \else + \errhelp = \noepsfhelp + \errmessage{epsf.tex not found, images will be ignored}% + \global\warnednoepsftrue + \fi + \else + \imagexxx #1,,,,,\finish + \fi +} +% +% Arguments to @image: +% #1 is (mandatory) image filename; we tack on .eps extension. +% #2 is (optional) width, #3 is (optional) height. +% #4 is (ignored optional) html alt text. +% #5 is (ignored optional) extension. +% #6 is just the usual extra ignored arg for parsing stuff. +\newif\ifimagevmode +\def\imagexxx#1,#2,#3,#4,#5,#6\finish{\begingroup + \catcode`\^^M = 5 % in case we're inside an example + \normalturnoffactive % allow _ et al. in names + \def\xprocessmacroarg{\eatspaces}% in case we are being used via a macro + % If the image is by itself, center it. + \ifvmode + \imagevmodetrue + \else \ifx\centersub\centerV + % for @center @image, we need a vbox so we can have our vertical space + \imagevmodetrue + \vbox\bgroup % vbox has better behavior than vtop herev + \fi\fi + % + \ifimagevmode + \nobreak\medskip + % Usually we'll have text after the image which will insert + % \parskip glue, so insert it here too to equalize the space + % above and below. + \nobreak\vskip\parskip + \nobreak + \fi + % + % Leave vertical mode so that indentation from an enclosing + % environment such as @quotation is respected. + % However, if we're at the top level, we don't want the + % normal paragraph indentation. + % On the other hand, if we are in the case of @center @image, we don't + % want to start a paragraph, which will create a hsize-width box and + % eradicate the centering. + \ifx\centersub\centerV\else \noindent \fi + % + % Output the image. + \ifpdf + % For pdfTeX and LuaTeX <= 0.80 + \dopdfimage{#1}{#2}{#3}% + \else + \ifx\XeTeXrevision\thisisundefined + % For epsf.tex + % \epsfbox itself resets \epsf?size at each figure. + \setbox0 = \hbox{\ignorespaces #2}% + \ifdim\wd0 > 0pt \epsfxsize=#2\relax \fi + \setbox0 = \hbox{\ignorespaces #3}% + \ifdim\wd0 > 0pt \epsfysize=#3\relax \fi + \epsfbox{#1.eps}% + \else + % For XeTeX + \doxeteximage{#1}{#2}{#3}% + \fi + \fi + % + \ifimagevmode + \medskip % space after a standalone image + \fi + \ifx\centersub\centerV \egroup \fi +\endgroup} + + +% @float FLOATTYPE,LABEL,LOC ... @end float for displayed figures, tables, +% etc. We don't actually implement floating yet, we always include the +% float "here". But it seemed the best name for the future. +% +\envparseargdef\float{\eatcommaspace\eatcommaspace\dofloat#1, , ,\finish} + +% There may be a space before second and/or third parameter; delete it. +\def\eatcommaspace#1, {#1,} + +% #1 is the optional FLOATTYPE, the text label for this float, typically +% "Figure", "Table", "Example", etc. Can't contain commas. If omitted, +% this float will not be numbered and cannot be referred to. +% +% #2 is the optional xref label. Also must be present for the float to +% be referable. +% +% #3 is the optional positioning argument; for now, it is ignored. It +% will somehow specify the positions allowed to float to (here, top, bottom). +% +% We keep a separate counter for each FLOATTYPE, which we reset at each +% chapter-level command. +\let\resetallfloatnos=\empty +% +\def\dofloat#1,#2,#3,#4\finish{% + \let\thiscaption=\empty + \let\thisshortcaption=\empty + % + % don't lose footnotes inside @float. + % + % BEWARE: when the floats start float, we have to issue warning whenever an + % insert appears inside a float which could possibly float. --kasal, 26may04 + % + \startsavinginserts + % + % We can't be used inside a paragraph. + \par + % + \vtop\bgroup + \def\floattype{#1}% + \def\floatlabel{#2}% + \def\floatloc{#3}% we do nothing with this yet. + % + \ifx\floattype\empty + \let\safefloattype=\empty + \else + {% + % the floattype might have accents or other special characters, + % but we need to use it in a control sequence name. + \indexnofonts + \turnoffactive + \xdef\safefloattype{\floattype}% + }% + \fi + % + % If label is given but no type, we handle that as the empty type. + \ifx\floatlabel\empty \else + % We want each FLOATTYPE to be numbered separately (Figure 1, + % Table 1, Figure 2, ...). (And if no label, no number.) + % + \expandafter\getfloatno\csname\safefloattype floatno\endcsname + \global\advance\floatno by 1 + % + {% + % This magic value for \lastsection is output by \setref as the + % XREFLABEL-title value. \xrefX uses it to distinguish float + % labels (which have a completely different output format) from + % node and anchor labels. And \xrdef uses it to construct the + % lists of floats. + % + \edef\lastsection{\floatmagic=\safefloattype}% + \setref{\floatlabel}{Yfloat}% + }% + \fi + % + % start with \parskip glue, I guess. + \vskip\parskip + % + % Don't suppress indentation if a float happens to start a section. + \restorefirstparagraphindent +} + +% we have these possibilities: +% @float Foo,lbl & @caption{Cap}: Foo 1.1: Cap +% @float Foo,lbl & no caption: Foo 1.1 +% @float Foo & @caption{Cap}: Foo: Cap +% @float Foo & no caption: Foo +% @float ,lbl & Caption{Cap}: 1.1: Cap +% @float ,lbl & no caption: 1.1 +% @float & @caption{Cap}: Cap +% @float & no caption: +% +\def\Efloat{% + \let\floatident = \empty + % + % In all cases, if we have a float type, it comes first. + \ifx\floattype\empty \else \def\floatident{\floattype}\fi + % + % If we have an xref label, the number comes next. + \ifx\floatlabel\empty \else + \ifx\floattype\empty \else % if also had float type, need tie first. + \appendtomacro\floatident{\tie}% + \fi + % the number. + \appendtomacro\floatident{\chaplevelprefix\the\floatno}% + \fi + % + % Start the printed caption with what we've constructed in + % \floatident, but keep it separate; we need \floatident again. + \let\captionline = \floatident + % + \ifx\thiscaption\empty \else + \ifx\floatident\empty \else + \appendtomacro\captionline{: }% had ident, so need a colon between + \fi + % + % caption text. + \appendtomacro\captionline{\scanexp\thiscaption}% + \fi + % + % If we have anything to print, print it, with space before. + % Eventually this needs to become an \insert. + \ifx\captionline\empty \else + \vskip.5\parskip + \captionline + % + % Space below caption. + \vskip\parskip + \fi + % + % If have an xref label, write the list of floats info. Do this + % after the caption, to avoid chance of it being a breakpoint. + \ifx\floatlabel\empty \else + % Write the text that goes in the lof to the aux file as + % \floatlabel-lof. Besides \floatident, we include the short + % caption if specified, else the full caption if specified, else nothing. + {% + \requireauxfile + \atdummies + % + % since we read the caption text in the macro world, where ^^M + % is turned into a normal character, we have to scan it back, so + % we don't write the literal three characters "^^M" into the aux file. + \scanexp{% + \xdef\noexpand\gtemp{% + \ifx\thisshortcaption\empty + \thiscaption + \else + \thisshortcaption + \fi + }% + }% + \immediate\write\auxfile{@xrdef{\floatlabel-lof}{\floatident + \ifx\gtemp\empty \else : \gtemp \fi}}% + }% + \fi + \egroup % end of \vtop + % + % place the captured inserts + % + % BEWARE: when the floats start floating, we have to issue warning + % whenever an insert appears inside a float which could possibly + % float. --kasal, 26may04 + % + \checkinserts +} + +% Append the tokens #2 to the definition of macro #1, not expanding either. +% +\def\appendtomacro#1#2{% + \expandafter\def\expandafter#1\expandafter{#1#2}% +} + +% @caption, @shortcaption +% +\def\caption{\docaption\thiscaption} +\def\shortcaption{\docaption\thisshortcaption} +\def\docaption{\checkenv\float \bgroup\scanargctxt\defcaption} +\def\defcaption#1#2{\egroup \def#1{#2}} + +% The parameter is the control sequence identifying the counter we are +% going to use. Create it if it doesn't exist and assign it to \floatno. +\def\getfloatno#1{% + \ifx#1\relax + % Haven't seen this figure type before. + \csname newcount\endcsname #1% + % + % Remember to reset this floatno at the next chap. + \expandafter\gdef\expandafter\resetallfloatnos + \expandafter{\resetallfloatnos #1=0 }% + \fi + \let\floatno#1% +} + +% \setref calls this to get the XREFLABEL-snt value. We want an @xref +% to the FLOATLABEL to expand to "Figure 3.1". We call \setref when we +% first read the @float command. +% +\def\Yfloat{\floattype@tie \chaplevelprefix\the\floatno}% + +% Magic string used for the XREFLABEL-title value, so \xrefX can +% distinguish floats from other xref types. +\def\floatmagic{!!float!!} + +% #1 is the control sequence we are passed; we expand into a conditional +% which is true if #1 represents a float ref. That is, the magic +% \lastsection value which we \setref above. +% +\def\iffloat#1{\expandafter\doiffloat#1==\finish} +% +% #1 is (maybe) the \floatmagic string. If so, #2 will be the +% (safe) float type for this float. We set \iffloattype to #2. +% +\def\doiffloat#1=#2=#3\finish{% + \def\temp{#1}% + \def\iffloattype{#2}% + \ifx\temp\floatmagic +} + +% @listoffloats FLOATTYPE - print a list of floats like a table of contents. +% +\parseargdef\listoffloats{% + \def\floattype{#1}% floattype + {% + % the floattype might have accents or other special characters, + % but we need to use it in a control sequence name. + \indexnofonts + \turnoffactive + \xdef\safefloattype{\floattype}% + }% + % + % \xrdef saves the floats as a \do-list in \floatlistSAFEFLOATTYPE. + \expandafter\ifx\csname floatlist\safefloattype\endcsname \relax + \ifhavexrefs + % if the user said @listoffloats foo but never @float foo. + \message{\linenumber No `\safefloattype' floats to list.}% + \fi + \else + \begingroup + \leftskip=\tocindent % indent these entries like a toc + \let\do=\listoffloatsdo + \csname floatlist\safefloattype\endcsname + \endgroup + \fi +} + +% This is called on each entry in a list of floats. We're passed the +% xref label, in the form LABEL-title, which is how we save it in the +% aux file. We strip off the -title and look up \XRLABEL-lof, which +% has the text we're supposed to typeset here. +% +% Figures without xref labels will not be included in the list (since +% they won't appear in the aux file). +% +\def\listoffloatsdo#1{\listoffloatsdoentry#1\finish} +\def\listoffloatsdoentry#1-title\finish{{% + % Can't fully expand XR#1-lof because it can contain anything. Just + % pass the control sequence. On the other hand, XR#1-pg is just the + % page number, and we want to fully expand that so we can get a link + % in pdf output. + \toksA = \expandafter{\csname XR#1-lof\endcsname}% + % + % use the same \entry macro we use to generate the TOC and index. + \edef\writeentry{\noexpand\entry{\the\toksA}{\csname XR#1-pg\endcsname}}% + \writeentry +}} + + +\message{localization,} + +% For single-language documents, @documentlanguage is usually given very +% early, just after @documentencoding. Single argument is the language +% (de) or locale (de_DE) abbreviation. +% +{ + \catcode`\_ = \active + \globaldefs=1 +\parseargdef\documentlanguage{% + \tex % read txi-??.tex file in plain TeX. + % Read the file by the name they passed if it exists. + \let_ = \normalunderscore % normal _ character for filename test + \openin 1 txi-#1.tex + \ifeof 1 + \documentlanguagetrywithoutunderscore #1_\finish + \else + \globaldefs = 1 % everything in the txi-LL files needs to persist + \input txi-#1.tex + \fi + \closein 1 + \endgroup % end raw TeX +} +% +% If they passed de_DE, and txi-de_DE.tex doesn't exist, +% try txi-de.tex. +% +\gdef\documentlanguagetrywithoutunderscore#1_#2\finish{% + \openin 1 txi-#1.tex + \ifeof 1 + \errhelp = \nolanghelp + \errmessage{Cannot read language file txi-#1.tex}% + \else + \globaldefs = 1 % everything in the txi-LL files needs to persist + \input txi-#1.tex + \fi + \closein 1 +} +}% end of special _ catcode +% +\newhelp\nolanghelp{The given language definition file cannot be found or +is empty. Maybe you need to install it? Putting it in the current +directory should work if nowhere else does.} + +% This macro is called from txi-??.tex files; the first argument is the +% \language name to set (without the "\lang@" prefix), the second and +% third args are \{left,right}hyphenmin. +% +% The language names to pass are determined when the format is built. +% See the etex.log file created at that time, e.g., +% /usr/local/texlive/2008/texmf-var/web2c/pdftex/etex.log. +% +% With TeX Live 2008, etex now includes hyphenation patterns for all +% available languages. This means we can support hyphenation in +% Texinfo, at least to some extent. (This still doesn't solve the +% accented characters problem.) +% +\catcode`@=11 +\def\txisetlanguage#1#2#3{% + % do not set the language if the name is undefined in the current TeX. + \expandafter\ifx\csname lang@#1\endcsname \relax + \message{no patterns for #1}% + \else + \global\language = \csname lang@#1\endcsname + \fi + % but there is no harm in adjusting the hyphenmin values regardless. + \global\lefthyphenmin = #2\relax + \global\righthyphenmin = #3\relax +} + +% Get input by bytes instead of by UTF-8 codepoints for XeTeX and LuaTeX, +% otherwise the encoding support is completely broken. +\ifx\XeTeXrevision\thisisundefined +\else +\XeTeXdefaultencoding "bytes" % For subsequent files to be read +\XeTeXinputencoding "bytes" % Effective in texinfo.tex only +% Unfortunately, there seems to be no corresponding XeTeX command for +% output encoding. This is a problem for auxiliary index and TOC files. +% The only solution would be perhaps to write out @U{...} sequences in +% place of UTF-8 characters. +\fi + +\ifx\luatexversion\thisisundefined +\else +\directlua{ +local utf8_char, byte, gsub = unicode.utf8.char, string.byte, string.gsub +local function convert_char (char) + return utf8_char(byte(char)) +end + +local function convert_line (line) + return gsub(line, ".", convert_char) +end + +callback.register("process_input_buffer", convert_line) + +local function convert_line_out (line) + local line_out = "" + for c in string.utfvalues(line) do + line_out = line_out .. string.char(c) + end + return line_out +end + +callback.register("process_output_buffer", convert_line_out) +} +\fi + + +% Helpers for encodings. +% Set the catcode of characters 128 through 255 to the specified number. +% +\def\setnonasciicharscatcode#1{% + \count255=128 + \loop\ifnum\count255<256 + \global\catcode\count255=#1\relax + \advance\count255 by 1 + \repeat +} + +\def\setnonasciicharscatcodenonglobal#1{% + \count255=128 + \loop\ifnum\count255<256 + \catcode\count255=#1\relax + \advance\count255 by 1 + \repeat +} + +% @documentencoding sets the definition of non-ASCII characters +% according to the specified encoding. +% +\def\documentencoding{\parseargusing\filenamecatcodes\documentencodingzzz} +\def\documentencodingzzz#1{% + % Get input by bytes instead of by UTF-8 codepoints for XeTeX, + % otherwise the encoding support is completely broken. + % This settings is for the document root file. + \ifx\XeTeXrevision\thisisundefined + \else + \XeTeXinputencoding "bytes" + \fi + % + % Encoding being declared for the document. + \def\declaredencoding{\csname #1.enc\endcsname}% + % + % Supported encodings: names converted to tokens in order to be able + % to compare them with \ifx. + \def\ascii{\csname US-ASCII.enc\endcsname}% + \def\latnine{\csname ISO-8859-15.enc\endcsname}% + \def\latone{\csname ISO-8859-1.enc\endcsname}% + \def\lattwo{\csname ISO-8859-2.enc\endcsname}% + \def\utfeight{\csname UTF-8.enc\endcsname}% + % + \ifx \declaredencoding \ascii + \asciichardefs + % + \else \ifx \declaredencoding \lattwo + \setnonasciicharscatcode\active + \lattwochardefs + % + \else \ifx \declaredencoding \latone + \setnonasciicharscatcode\active + \latonechardefs + % + \else \ifx \declaredencoding \latnine + \setnonasciicharscatcode\active + \latninechardefs + % + \else \ifx \declaredencoding \utfeight + \setnonasciicharscatcode\active + % since we already invoked \utfeightchardefs at the top level + % (below), do not re-invoke it, then our check for duplicated + % definitions triggers. Making non-ascii chars active is enough. + % + \else + \message{Ignoring unknown document encoding: #1.}% + % + \fi % utfeight + \fi % latnine + \fi % latone + \fi % lattwo + \fi % ascii +} + +% emacs-page +% A message to be logged when using a character that isn't available +% the default font encoding (OT1). +% +\def\missingcharmsg#1{\message{Character missing, sorry: #1.}} + +% Take account of \c (plain) vs. \, (Texinfo) difference. +\def\cedilla#1{\ifx\c\ptexc\c{#1}\else\,{#1}\fi} + +% First, make active non-ASCII characters in order for them to be +% correctly categorized when TeX reads the replacement text of +% macros containing the character definitions. +\setnonasciicharscatcode\active +% +% Latin1 (ISO-8859-1) character definitions. +\def\latonechardefs{% + \gdef^^a0{\tie} + \gdef^^a1{\exclamdown} + \gdef^^a2{{\tcfont \char162}} % cent + \gdef^^a3{\pounds} + \gdef^^a4{{\tcfont \char164}} % currency + \gdef^^a5{{\tcfont \char165}} % yen + \gdef^^a6{{\tcfont \char166}} % broken bar + \gdef^^a7{\S} + \gdef^^a8{\"{}} + \gdef^^a9{\copyright} + \gdef^^aa{\ordf} + \gdef^^ab{\guillemetleft} + \gdef^^ac{\ensuremath\lnot} + \gdef^^ad{\-} + \gdef^^ae{\registeredsymbol} + \gdef^^af{\={}} + % + \gdef^^b0{\textdegree} + \gdef^^b1{$\pm$} + \gdef^^b2{$^2$} + \gdef^^b3{$^3$} + \gdef^^b4{\'{}} + \gdef^^b5{$\mu$} + \gdef^^b6{\P} + \gdef^^b7{\ensuremath\cdot} + \gdef^^b8{\cedilla\ } + \gdef^^b9{$^1$} + \gdef^^ba{\ordm} + \gdef^^bb{\guillemetright} + \gdef^^bc{$1\over4$} + \gdef^^bd{$1\over2$} + \gdef^^be{$3\over4$} + \gdef^^bf{\questiondown} + % + \gdef^^c0{\`A} + \gdef^^c1{\'A} + \gdef^^c2{\^A} + \gdef^^c3{\~A} + \gdef^^c4{\"A} + \gdef^^c5{\ringaccent A} + \gdef^^c6{\AE} + \gdef^^c7{\cedilla C} + \gdef^^c8{\`E} + \gdef^^c9{\'E} + \gdef^^ca{\^E} + \gdef^^cb{\"E} + \gdef^^cc{\`I} + \gdef^^cd{\'I} + \gdef^^ce{\^I} + \gdef^^cf{\"I} + % + \gdef^^d0{\DH} + \gdef^^d1{\~N} + \gdef^^d2{\`O} + \gdef^^d3{\'O} + \gdef^^d4{\^O} + \gdef^^d5{\~O} + \gdef^^d6{\"O} + \gdef^^d7{$\times$} + \gdef^^d8{\O} + \gdef^^d9{\`U} + \gdef^^da{\'U} + \gdef^^db{\^U} + \gdef^^dc{\"U} + \gdef^^dd{\'Y} + \gdef^^de{\TH} + \gdef^^df{\ss} + % + \gdef^^e0{\`a} + \gdef^^e1{\'a} + \gdef^^e2{\^a} + \gdef^^e3{\~a} + \gdef^^e4{\"a} + \gdef^^e5{\ringaccent a} + \gdef^^e6{\ae} + \gdef^^e7{\cedilla c} + \gdef^^e8{\`e} + \gdef^^e9{\'e} + \gdef^^ea{\^e} + \gdef^^eb{\"e} + \gdef^^ec{\`{\dotless i}} + \gdef^^ed{\'{\dotless i}} + \gdef^^ee{\^{\dotless i}} + \gdef^^ef{\"{\dotless i}} + % + \gdef^^f0{\dh} + \gdef^^f1{\~n} + \gdef^^f2{\`o} + \gdef^^f3{\'o} + \gdef^^f4{\^o} + \gdef^^f5{\~o} + \gdef^^f6{\"o} + \gdef^^f7{$\div$} + \gdef^^f8{\o} + \gdef^^f9{\`u} + \gdef^^fa{\'u} + \gdef^^fb{\^u} + \gdef^^fc{\"u} + \gdef^^fd{\'y} + \gdef^^fe{\th} + \gdef^^ff{\"y} +} + +% Latin9 (ISO-8859-15) encoding character definitions. +\def\latninechardefs{% + % Encoding is almost identical to Latin1. + \latonechardefs + % + \gdef^^a4{\euro} + \gdef^^a6{\v S} + \gdef^^a8{\v s} + \gdef^^b4{\v Z} + \gdef^^b8{\v z} + \gdef^^bc{\OE} + \gdef^^bd{\oe} + \gdef^^be{\"Y} +} + +% Latin2 (ISO-8859-2) character definitions. +\def\lattwochardefs{% + \gdef^^a0{\tie} + \gdef^^a1{\ogonek{A}} + \gdef^^a2{\u{}} + \gdef^^a3{\L} + \gdef^^a4{\missingcharmsg{CURRENCY SIGN}} + \gdef^^a5{\v L} + \gdef^^a6{\'S} + \gdef^^a7{\S} + \gdef^^a8{\"{}} + \gdef^^a9{\v S} + \gdef^^aa{\cedilla S} + \gdef^^ab{\v T} + \gdef^^ac{\'Z} + \gdef^^ad{\-} + \gdef^^ae{\v Z} + \gdef^^af{\dotaccent Z} + % + \gdef^^b0{\textdegree} + \gdef^^b1{\ogonek{a}} + \gdef^^b2{\ogonek{ }} + \gdef^^b3{\l} + \gdef^^b4{\'{}} + \gdef^^b5{\v l} + \gdef^^b6{\'s} + \gdef^^b7{\v{}} + \gdef^^b8{\cedilla\ } + \gdef^^b9{\v s} + \gdef^^ba{\cedilla s} + \gdef^^bb{\v t} + \gdef^^bc{\'z} + \gdef^^bd{\H{}} + \gdef^^be{\v z} + \gdef^^bf{\dotaccent z} + % + \gdef^^c0{\'R} + \gdef^^c1{\'A} + \gdef^^c2{\^A} + \gdef^^c3{\u A} + \gdef^^c4{\"A} + \gdef^^c5{\'L} + \gdef^^c6{\'C} + \gdef^^c7{\cedilla C} + \gdef^^c8{\v C} + \gdef^^c9{\'E} + \gdef^^ca{\ogonek{E}} + \gdef^^cb{\"E} + \gdef^^cc{\v E} + \gdef^^cd{\'I} + \gdef^^ce{\^I} + \gdef^^cf{\v D} + % + \gdef^^d0{\DH} + \gdef^^d1{\'N} + \gdef^^d2{\v N} + \gdef^^d3{\'O} + \gdef^^d4{\^O} + \gdef^^d5{\H O} + \gdef^^d6{\"O} + \gdef^^d7{$\times$} + \gdef^^d8{\v R} + \gdef^^d9{\ringaccent U} + \gdef^^da{\'U} + \gdef^^db{\H U} + \gdef^^dc{\"U} + \gdef^^dd{\'Y} + \gdef^^de{\cedilla T} + \gdef^^df{\ss} + % + \gdef^^e0{\'r} + \gdef^^e1{\'a} + \gdef^^e2{\^a} + \gdef^^e3{\u a} + \gdef^^e4{\"a} + \gdef^^e5{\'l} + \gdef^^e6{\'c} + \gdef^^e7{\cedilla c} + \gdef^^e8{\v c} + \gdef^^e9{\'e} + \gdef^^ea{\ogonek{e}} + \gdef^^eb{\"e} + \gdef^^ec{\v e} + \gdef^^ed{\'{\dotless{i}}} + \gdef^^ee{\^{\dotless{i}}} + \gdef^^ef{\v d} + % + \gdef^^f0{\dh} + \gdef^^f1{\'n} + \gdef^^f2{\v n} + \gdef^^f3{\'o} + \gdef^^f4{\^o} + \gdef^^f5{\H o} + \gdef^^f6{\"o} + \gdef^^f7{$\div$} + \gdef^^f8{\v r} + \gdef^^f9{\ringaccent u} + \gdef^^fa{\'u} + \gdef^^fb{\H u} + \gdef^^fc{\"u} + \gdef^^fd{\'y} + \gdef^^fe{\cedilla t} + \gdef^^ff{\dotaccent{}} +} + +% UTF-8 character definitions. +% +% This code to support UTF-8 is based on LaTeX's utf8.def, with some +% changes for Texinfo conventions. It is included here under the GPL by +% permission from Frank Mittelbach and the LaTeX team. +% +\newcount\countUTFx +\newcount\countUTFy +\newcount\countUTFz + +\gdef\UTFviiiTwoOctets#1#2{\expandafter + \UTFviiiDefined\csname u8:#1\string #2\endcsname} +% +\gdef\UTFviiiThreeOctets#1#2#3{\expandafter + \UTFviiiDefined\csname u8:#1\string #2\string #3\endcsname} +% +\gdef\UTFviiiFourOctets#1#2#3#4{\expandafter + \UTFviiiDefined\csname u8:#1\string #2\string #3\string #4\endcsname} + +\gdef\UTFviiiDefined#1{% + \ifx #1\relax + \message{\linenumber Unicode char \string #1 not defined for Texinfo}% + \else + \expandafter #1% + \fi +} + +\begingroup + \catcode`\~13 + \catcode`\"12 + + \def\UTFviiiLoop{% + \global\catcode\countUTFx\active + \uccode`\~\countUTFx + \uppercase\expandafter{\UTFviiiTmp}% + \advance\countUTFx by 1 + \ifnum\countUTFx < \countUTFy + \expandafter\UTFviiiLoop + \fi} + + \countUTFx = "C2 + \countUTFy = "E0 + \def\UTFviiiTmp{% + \xdef~{\noexpand\UTFviiiTwoOctets\string~}} + \UTFviiiLoop + + \countUTFx = "E0 + \countUTFy = "F0 + \def\UTFviiiTmp{% + \xdef~{\noexpand\UTFviiiThreeOctets\string~}} + \UTFviiiLoop + + \countUTFx = "F0 + \countUTFy = "F4 + \def\UTFviiiTmp{% + \xdef~{\noexpand\UTFviiiFourOctets\string~}} + \UTFviiiLoop +\endgroup + +\def\globallet{\global\let} % save some \expandafter's below + +% @U{xxxx} to produce U+xxxx, if we support it. +\def\U#1{% + \expandafter\ifx\csname uni:#1\endcsname \relax + \errhelp = \EMsimple + \errmessage{Unicode character U+#1 not supported, sorry}% + \else + \csname uni:#1\endcsname + \fi +} + +\begingroup + \catcode`\"=12 + \catcode`\<=12 + \catcode`\.=12 + \catcode`\,=12 + \catcode`\;=12 + \catcode`\!=12 + \catcode`\~=13 + \gdef\DeclareUnicodeCharacter#1#2{% + \countUTFz = "#1\relax + %\wlog{\space\space defining Unicode char U+#1 (decimal \the\countUTFz)}% + \begingroup + \parseXMLCharref + \def\UTFviiiTwoOctets##1##2{% + \csname u8:##1\string ##2\endcsname}% + \def\UTFviiiThreeOctets##1##2##3{% + \csname u8:##1\string ##2\string ##3\endcsname}% + \def\UTFviiiFourOctets##1##2##3##4{% + \csname u8:##1\string ##2\string ##3\string ##4\endcsname}% + \expandafter\expandafter\expandafter\expandafter + \expandafter\expandafter\expandafter + \gdef\UTFviiiTmp{#2}% + % + \expandafter\ifx\csname uni:#1\endcsname \relax \else + \message{Internal error, already defined: #1}% + \fi + % + % define an additional control sequence for this code point. + \expandafter\globallet\csname uni:#1\endcsname \UTFviiiTmp + \endgroup} + + \gdef\parseXMLCharref{% + \ifnum\countUTFz < "A0\relax + \errhelp = \EMsimple + \errmessage{Cannot define Unicode char value < 00A0}% + \else\ifnum\countUTFz < "800\relax + \parseUTFviiiA,% + \parseUTFviiiB C\UTFviiiTwoOctets.,% + \else\ifnum\countUTFz < "10000\relax + \parseUTFviiiA;% + \parseUTFviiiA,% + \parseUTFviiiB E\UTFviiiThreeOctets.{,;}% + \else + \parseUTFviiiA;% + \parseUTFviiiA,% + \parseUTFviiiA!% + \parseUTFviiiB F\UTFviiiFourOctets.{!,;}% + \fi\fi\fi + } + + \gdef\parseUTFviiiA#1{% + \countUTFx = \countUTFz + \divide\countUTFz by 64 + \countUTFy = \countUTFz + \multiply\countUTFz by 64 + \advance\countUTFx by -\countUTFz + \advance\countUTFx by 128 + \uccode `#1\countUTFx + \countUTFz = \countUTFy} + + \gdef\parseUTFviiiB#1#2#3#4{% + \advance\countUTFz by "#10\relax + \uccode `#3\countUTFz + \uppercase{\gdef\UTFviiiTmp{#2#3#4}}} +\endgroup + +% https://en.wikipedia.org/wiki/Plane_(Unicode)#Basic_M +% U+0000..U+007F = https://en.wikipedia.org/wiki/Basic_Latin_(Unicode_block) +% U+0080..U+00FF = https://en.wikipedia.org/wiki/Latin-1_Supplement_(Unicode_block) +% U+0100..U+017F = https://en.wikipedia.org/wiki/Latin_Extended-A +% U+0180..U+024F = https://en.wikipedia.org/wiki/Latin_Extended-B +% +% Many of our renditions are less than wonderful, and all the missing +% characters are available somewhere. Loading the necessary fonts +% awaits user request. We can't truly support Unicode without +% reimplementing everything that's been done in LaTeX for many years, +% plus probably using luatex or xetex, and who knows what else. +% We won't be doing that here in this simple file. But we can try to at +% least make most of the characters not bomb out. +% +\def\utfeightchardefs{% + \DeclareUnicodeCharacter{00A0}{\tie} + \DeclareUnicodeCharacter{00A1}{\exclamdown} + \DeclareUnicodeCharacter{00A2}{{\tcfont \char162}}% 0242=cent + \DeclareUnicodeCharacter{00A3}{\pounds} + \DeclareUnicodeCharacter{00A4}{{\tcfont \char164}}% 0244=currency + \DeclareUnicodeCharacter{00A5}{{\tcfont \char165}}% 0245=yen + \DeclareUnicodeCharacter{00A6}{{\tcfont \char166}}% 0246=brokenbar + \DeclareUnicodeCharacter{00A7}{\S} + \DeclareUnicodeCharacter{00A8}{\"{ }} + \DeclareUnicodeCharacter{00A9}{\copyright} + \DeclareUnicodeCharacter{00AA}{\ordf} + \DeclareUnicodeCharacter{00AB}{\guillemetleft} + \DeclareUnicodeCharacter{00AC}{\ensuremath\lnot} + \DeclareUnicodeCharacter{00AD}{\-} + \DeclareUnicodeCharacter{00AE}{\registeredsymbol} + \DeclareUnicodeCharacter{00AF}{\={ }} + % + \DeclareUnicodeCharacter{00B0}{\ringaccent{ }} + \DeclareUnicodeCharacter{00B1}{\ensuremath\pm} + \DeclareUnicodeCharacter{00B2}{$^2$} + \DeclareUnicodeCharacter{00B3}{$^3$} + \DeclareUnicodeCharacter{00B4}{\'{ }} + \DeclareUnicodeCharacter{00B5}{$\mu$} + \DeclareUnicodeCharacter{00B6}{\P} + \DeclareUnicodeCharacter{00B7}{\ensuremath\cdot} + \DeclareUnicodeCharacter{00B8}{\cedilla{ }} + \DeclareUnicodeCharacter{00B9}{$^1$} + \DeclareUnicodeCharacter{00BA}{\ordm} + \DeclareUnicodeCharacter{00BB}{\guillemetright} + \DeclareUnicodeCharacter{00BC}{$1\over4$} + \DeclareUnicodeCharacter{00BD}{$1\over2$} + \DeclareUnicodeCharacter{00BE}{$3\over4$} + \DeclareUnicodeCharacter{00BF}{\questiondown} + % + \DeclareUnicodeCharacter{00C0}{\`A} + \DeclareUnicodeCharacter{00C1}{\'A} + \DeclareUnicodeCharacter{00C2}{\^A} + \DeclareUnicodeCharacter{00C3}{\~A} + \DeclareUnicodeCharacter{00C4}{\"A} + \DeclareUnicodeCharacter{00C5}{\AA} + \DeclareUnicodeCharacter{00C6}{\AE} + \DeclareUnicodeCharacter{00C7}{\cedilla{C}} + \DeclareUnicodeCharacter{00C8}{\`E} + \DeclareUnicodeCharacter{00C9}{\'E} + \DeclareUnicodeCharacter{00CA}{\^E} + \DeclareUnicodeCharacter{00CB}{\"E} + \DeclareUnicodeCharacter{00CC}{\`I} + \DeclareUnicodeCharacter{00CD}{\'I} + \DeclareUnicodeCharacter{00CE}{\^I} + \DeclareUnicodeCharacter{00CF}{\"I} + % + \DeclareUnicodeCharacter{00D0}{\DH} + \DeclareUnicodeCharacter{00D1}{\~N} + \DeclareUnicodeCharacter{00D2}{\`O} + \DeclareUnicodeCharacter{00D3}{\'O} + \DeclareUnicodeCharacter{00D4}{\^O} + \DeclareUnicodeCharacter{00D5}{\~O} + \DeclareUnicodeCharacter{00D6}{\"O} + \DeclareUnicodeCharacter{00D7}{\ensuremath\times} + \DeclareUnicodeCharacter{00D8}{\O} + \DeclareUnicodeCharacter{00D9}{\`U} + \DeclareUnicodeCharacter{00DA}{\'U} + \DeclareUnicodeCharacter{00DB}{\^U} + \DeclareUnicodeCharacter{00DC}{\"U} + \DeclareUnicodeCharacter{00DD}{\'Y} + \DeclareUnicodeCharacter{00DE}{\TH} + \DeclareUnicodeCharacter{00DF}{\ss} + % + \DeclareUnicodeCharacter{00E0}{\`a} + \DeclareUnicodeCharacter{00E1}{\'a} + \DeclareUnicodeCharacter{00E2}{\^a} + \DeclareUnicodeCharacter{00E3}{\~a} + \DeclareUnicodeCharacter{00E4}{\"a} + \DeclareUnicodeCharacter{00E5}{\aa} + \DeclareUnicodeCharacter{00E6}{\ae} + \DeclareUnicodeCharacter{00E7}{\cedilla{c}} + \DeclareUnicodeCharacter{00E8}{\`e} + \DeclareUnicodeCharacter{00E9}{\'e} + \DeclareUnicodeCharacter{00EA}{\^e} + \DeclareUnicodeCharacter{00EB}{\"e} + \DeclareUnicodeCharacter{00EC}{\`{\dotless{i}}} + \DeclareUnicodeCharacter{00ED}{\'{\dotless{i}}} + \DeclareUnicodeCharacter{00EE}{\^{\dotless{i}}} + \DeclareUnicodeCharacter{00EF}{\"{\dotless{i}}} + % + \DeclareUnicodeCharacter{00F0}{\dh} + \DeclareUnicodeCharacter{00F1}{\~n} + \DeclareUnicodeCharacter{00F2}{\`o} + \DeclareUnicodeCharacter{00F3}{\'o} + \DeclareUnicodeCharacter{00F4}{\^o} + \DeclareUnicodeCharacter{00F5}{\~o} + \DeclareUnicodeCharacter{00F6}{\"o} + \DeclareUnicodeCharacter{00F7}{\ensuremath\div} + \DeclareUnicodeCharacter{00F8}{\o} + \DeclareUnicodeCharacter{00F9}{\`u} + \DeclareUnicodeCharacter{00FA}{\'u} + \DeclareUnicodeCharacter{00FB}{\^u} + \DeclareUnicodeCharacter{00FC}{\"u} + \DeclareUnicodeCharacter{00FD}{\'y} + \DeclareUnicodeCharacter{00FE}{\th} + \DeclareUnicodeCharacter{00FF}{\"y} + % + \DeclareUnicodeCharacter{0100}{\=A} + \DeclareUnicodeCharacter{0101}{\=a} + \DeclareUnicodeCharacter{0102}{\u{A}} + \DeclareUnicodeCharacter{0103}{\u{a}} + \DeclareUnicodeCharacter{0104}{\ogonek{A}} + \DeclareUnicodeCharacter{0105}{\ogonek{a}} + \DeclareUnicodeCharacter{0106}{\'C} + \DeclareUnicodeCharacter{0107}{\'c} + \DeclareUnicodeCharacter{0108}{\^C} + \DeclareUnicodeCharacter{0109}{\^c} + \DeclareUnicodeCharacter{010A}{\dotaccent{C}} + \DeclareUnicodeCharacter{010B}{\dotaccent{c}} + \DeclareUnicodeCharacter{010C}{\v{C}} + \DeclareUnicodeCharacter{010D}{\v{c}} + \DeclareUnicodeCharacter{010E}{\v{D}} + \DeclareUnicodeCharacter{010F}{d'} + % + \DeclareUnicodeCharacter{0110}{\DH} + \DeclareUnicodeCharacter{0111}{\dh} + \DeclareUnicodeCharacter{0112}{\=E} + \DeclareUnicodeCharacter{0113}{\=e} + \DeclareUnicodeCharacter{0114}{\u{E}} + \DeclareUnicodeCharacter{0115}{\u{e}} + \DeclareUnicodeCharacter{0116}{\dotaccent{E}} + \DeclareUnicodeCharacter{0117}{\dotaccent{e}} + \DeclareUnicodeCharacter{0118}{\ogonek{E}} + \DeclareUnicodeCharacter{0119}{\ogonek{e}} + \DeclareUnicodeCharacter{011A}{\v{E}} + \DeclareUnicodeCharacter{011B}{\v{e}} + \DeclareUnicodeCharacter{011C}{\^G} + \DeclareUnicodeCharacter{011D}{\^g} + \DeclareUnicodeCharacter{011E}{\u{G}} + \DeclareUnicodeCharacter{011F}{\u{g}} + % + \DeclareUnicodeCharacter{0120}{\dotaccent{G}} + \DeclareUnicodeCharacter{0121}{\dotaccent{g}} + \DeclareUnicodeCharacter{0122}{\cedilla{G}} + \DeclareUnicodeCharacter{0123}{\cedilla{g}} + \DeclareUnicodeCharacter{0124}{\^H} + \DeclareUnicodeCharacter{0125}{\^h} + \DeclareUnicodeCharacter{0126}{\missingcharmsg{H WITH STROKE}} + \DeclareUnicodeCharacter{0127}{\missingcharmsg{h WITH STROKE}} + \DeclareUnicodeCharacter{0128}{\~I} + \DeclareUnicodeCharacter{0129}{\~{\dotless{i}}} + \DeclareUnicodeCharacter{012A}{\=I} + \DeclareUnicodeCharacter{012B}{\={\dotless{i}}} + \DeclareUnicodeCharacter{012C}{\u{I}} + \DeclareUnicodeCharacter{012D}{\u{\dotless{i}}} + \DeclareUnicodeCharacter{012E}{\ogonek{I}} + \DeclareUnicodeCharacter{012F}{\ogonek{i}} + % + \DeclareUnicodeCharacter{0130}{\dotaccent{I}} + \DeclareUnicodeCharacter{0131}{\dotless{i}} + \DeclareUnicodeCharacter{0132}{IJ} + \DeclareUnicodeCharacter{0133}{ij} + \DeclareUnicodeCharacter{0134}{\^J} + \DeclareUnicodeCharacter{0135}{\^{\dotless{j}}} + \DeclareUnicodeCharacter{0136}{\cedilla{K}} + \DeclareUnicodeCharacter{0137}{\cedilla{k}} + \DeclareUnicodeCharacter{0138}{\ensuremath\kappa} + \DeclareUnicodeCharacter{0139}{\'L} + \DeclareUnicodeCharacter{013A}{\'l} + \DeclareUnicodeCharacter{013B}{\cedilla{L}} + \DeclareUnicodeCharacter{013C}{\cedilla{l}} + \DeclareUnicodeCharacter{013D}{L'}% should kern + \DeclareUnicodeCharacter{013E}{l'}% should kern + \DeclareUnicodeCharacter{013F}{L\U{00B7}} + % + \DeclareUnicodeCharacter{0140}{l\U{00B7}} + \DeclareUnicodeCharacter{0141}{\L} + \DeclareUnicodeCharacter{0142}{\l} + \DeclareUnicodeCharacter{0143}{\'N} + \DeclareUnicodeCharacter{0144}{\'n} + \DeclareUnicodeCharacter{0145}{\cedilla{N}} + \DeclareUnicodeCharacter{0146}{\cedilla{n}} + \DeclareUnicodeCharacter{0147}{\v{N}} + \DeclareUnicodeCharacter{0148}{\v{n}} + \DeclareUnicodeCharacter{0149}{'n} + \DeclareUnicodeCharacter{014A}{\missingcharmsg{ENG}} + \DeclareUnicodeCharacter{014B}{\missingcharmsg{eng}} + \DeclareUnicodeCharacter{014C}{\=O} + \DeclareUnicodeCharacter{014D}{\=o} + \DeclareUnicodeCharacter{014E}{\u{O}} + \DeclareUnicodeCharacter{014F}{\u{o}} + % + \DeclareUnicodeCharacter{0150}{\H{O}} + \DeclareUnicodeCharacter{0151}{\H{o}} + \DeclareUnicodeCharacter{0152}{\OE} + \DeclareUnicodeCharacter{0153}{\oe} + \DeclareUnicodeCharacter{0154}{\'R} + \DeclareUnicodeCharacter{0155}{\'r} + \DeclareUnicodeCharacter{0156}{\cedilla{R}} + \DeclareUnicodeCharacter{0157}{\cedilla{r}} + \DeclareUnicodeCharacter{0158}{\v{R}} + \DeclareUnicodeCharacter{0159}{\v{r}} + \DeclareUnicodeCharacter{015A}{\'S} + \DeclareUnicodeCharacter{015B}{\'s} + \DeclareUnicodeCharacter{015C}{\^S} + \DeclareUnicodeCharacter{015D}{\^s} + \DeclareUnicodeCharacter{015E}{\cedilla{S}} + \DeclareUnicodeCharacter{015F}{\cedilla{s}} + % + \DeclareUnicodeCharacter{0160}{\v{S}} + \DeclareUnicodeCharacter{0161}{\v{s}} + \DeclareUnicodeCharacter{0162}{\cedilla{T}} + \DeclareUnicodeCharacter{0163}{\cedilla{t}} + \DeclareUnicodeCharacter{0164}{\v{T}} + \DeclareUnicodeCharacter{0165}{\v{t}} + \DeclareUnicodeCharacter{0166}{\missingcharmsg{H WITH STROKE}} + \DeclareUnicodeCharacter{0167}{\missingcharmsg{h WITH STROKE}} + \DeclareUnicodeCharacter{0168}{\~U} + \DeclareUnicodeCharacter{0169}{\~u} + \DeclareUnicodeCharacter{016A}{\=U} + \DeclareUnicodeCharacter{016B}{\=u} + \DeclareUnicodeCharacter{016C}{\u{U}} + \DeclareUnicodeCharacter{016D}{\u{u}} + \DeclareUnicodeCharacter{016E}{\ringaccent{U}} + \DeclareUnicodeCharacter{016F}{\ringaccent{u}} + % + \DeclareUnicodeCharacter{0170}{\H{U}} + \DeclareUnicodeCharacter{0171}{\H{u}} + \DeclareUnicodeCharacter{0172}{\ogonek{U}} + \DeclareUnicodeCharacter{0173}{\ogonek{u}} + \DeclareUnicodeCharacter{0174}{\^W} + \DeclareUnicodeCharacter{0175}{\^w} + \DeclareUnicodeCharacter{0176}{\^Y} + \DeclareUnicodeCharacter{0177}{\^y} + \DeclareUnicodeCharacter{0178}{\"Y} + \DeclareUnicodeCharacter{0179}{\'Z} + \DeclareUnicodeCharacter{017A}{\'z} + \DeclareUnicodeCharacter{017B}{\dotaccent{Z}} + \DeclareUnicodeCharacter{017C}{\dotaccent{z}} + \DeclareUnicodeCharacter{017D}{\v{Z}} + \DeclareUnicodeCharacter{017E}{\v{z}} + \DeclareUnicodeCharacter{017F}{\missingcharmsg{LONG S}} + % + \DeclareUnicodeCharacter{01C4}{D\v{Z}} + \DeclareUnicodeCharacter{01C5}{D\v{z}} + \DeclareUnicodeCharacter{01C6}{d\v{z}} + \DeclareUnicodeCharacter{01C7}{LJ} + \DeclareUnicodeCharacter{01C8}{Lj} + \DeclareUnicodeCharacter{01C9}{lj} + \DeclareUnicodeCharacter{01CA}{NJ} + \DeclareUnicodeCharacter{01CB}{Nj} + \DeclareUnicodeCharacter{01CC}{nj} + \DeclareUnicodeCharacter{01CD}{\v{A}} + \DeclareUnicodeCharacter{01CE}{\v{a}} + \DeclareUnicodeCharacter{01CF}{\v{I}} + % + \DeclareUnicodeCharacter{01D0}{\v{\dotless{i}}} + \DeclareUnicodeCharacter{01D1}{\v{O}} + \DeclareUnicodeCharacter{01D2}{\v{o}} + \DeclareUnicodeCharacter{01D3}{\v{U}} + \DeclareUnicodeCharacter{01D4}{\v{u}} + % + \DeclareUnicodeCharacter{01E2}{\={\AE}} + \DeclareUnicodeCharacter{01E3}{\={\ae}} + \DeclareUnicodeCharacter{01E6}{\v{G}} + \DeclareUnicodeCharacter{01E7}{\v{g}} + \DeclareUnicodeCharacter{01E8}{\v{K}} + \DeclareUnicodeCharacter{01E9}{\v{k}} + % + \DeclareUnicodeCharacter{01F0}{\v{\dotless{j}}} + \DeclareUnicodeCharacter{01F1}{DZ} + \DeclareUnicodeCharacter{01F2}{Dz} + \DeclareUnicodeCharacter{01F3}{dz} + \DeclareUnicodeCharacter{01F4}{\'G} + \DeclareUnicodeCharacter{01F5}{\'g} + \DeclareUnicodeCharacter{01F8}{\`N} + \DeclareUnicodeCharacter{01F9}{\`n} + \DeclareUnicodeCharacter{01FC}{\'{\AE}} + \DeclareUnicodeCharacter{01FD}{\'{\ae}} + \DeclareUnicodeCharacter{01FE}{\'{\O}} + \DeclareUnicodeCharacter{01FF}{\'{\o}} + % + \DeclareUnicodeCharacter{021E}{\v{H}} + \DeclareUnicodeCharacter{021F}{\v{h}} + % + \DeclareUnicodeCharacter{0226}{\dotaccent{A}} + \DeclareUnicodeCharacter{0227}{\dotaccent{a}} + \DeclareUnicodeCharacter{0228}{\cedilla{E}} + \DeclareUnicodeCharacter{0229}{\cedilla{e}} + \DeclareUnicodeCharacter{022E}{\dotaccent{O}} + \DeclareUnicodeCharacter{022F}{\dotaccent{o}} + % + \DeclareUnicodeCharacter{0232}{\=Y} + \DeclareUnicodeCharacter{0233}{\=y} + \DeclareUnicodeCharacter{0237}{\dotless{j}} + % + \DeclareUnicodeCharacter{02DB}{\ogonek{ }} + % + % Greek letters upper case + \DeclareUnicodeCharacter{0391}{{\it A}} + \DeclareUnicodeCharacter{0392}{{\it B}} + \DeclareUnicodeCharacter{0393}{\ensuremath{\mit\Gamma}} + \DeclareUnicodeCharacter{0394}{\ensuremath{\mit\Delta}} + \DeclareUnicodeCharacter{0395}{{\it E}} + \DeclareUnicodeCharacter{0396}{{\it Z}} + \DeclareUnicodeCharacter{0397}{{\it H}} + \DeclareUnicodeCharacter{0398}{\ensuremath{\mit\Theta}} + \DeclareUnicodeCharacter{0399}{{\it I}} + \DeclareUnicodeCharacter{039A}{{\it K}} + \DeclareUnicodeCharacter{039B}{\ensuremath{\mit\Lambda}} + \DeclareUnicodeCharacter{039C}{{\it M}} + \DeclareUnicodeCharacter{039D}{{\it N}} + \DeclareUnicodeCharacter{039E}{\ensuremath{\mit\Xi}} + \DeclareUnicodeCharacter{039F}{{\it O}} + \DeclareUnicodeCharacter{03A0}{\ensuremath{\mit\Pi}} + \DeclareUnicodeCharacter{03A1}{{\it P}} + %\DeclareUnicodeCharacter{03A2}{} % none - corresponds to final sigma + \DeclareUnicodeCharacter{03A3}{\ensuremath{\mit\Sigma}} + \DeclareUnicodeCharacter{03A4}{{\it T}} + \DeclareUnicodeCharacter{03A5}{\ensuremath{\mit\Upsilon}} + \DeclareUnicodeCharacter{03A6}{\ensuremath{\mit\Phi}} + \DeclareUnicodeCharacter{03A7}{{\it X}} + \DeclareUnicodeCharacter{03A8}{\ensuremath{\mit\Psi}} + \DeclareUnicodeCharacter{03A9}{\ensuremath{\mit\Omega}} + % + % Vowels with accents + \DeclareUnicodeCharacter{0390}{\ensuremath{\ddot{\acute\iota}}} + \DeclareUnicodeCharacter{03AC}{\ensuremath{\acute\alpha}} + \DeclareUnicodeCharacter{03AD}{\ensuremath{\acute\epsilon}} + \DeclareUnicodeCharacter{03AE}{\ensuremath{\acute\eta}} + \DeclareUnicodeCharacter{03AF}{\ensuremath{\acute\iota}} + \DeclareUnicodeCharacter{03B0}{\ensuremath{\acute{\ddot\upsilon}}} + % + % Standalone accent + \DeclareUnicodeCharacter{0384}{\ensuremath{\acute{\ }}} + % + % Greek letters lower case + \DeclareUnicodeCharacter{03B1}{\ensuremath\alpha} + \DeclareUnicodeCharacter{03B2}{\ensuremath\beta} + \DeclareUnicodeCharacter{03B3}{\ensuremath\gamma} + \DeclareUnicodeCharacter{03B4}{\ensuremath\delta} + \DeclareUnicodeCharacter{03B5}{\ensuremath\epsilon} + \DeclareUnicodeCharacter{03B6}{\ensuremath\zeta} + \DeclareUnicodeCharacter{03B7}{\ensuremath\eta} + \DeclareUnicodeCharacter{03B8}{\ensuremath\theta} + \DeclareUnicodeCharacter{03B9}{\ensuremath\iota} + \DeclareUnicodeCharacter{03BA}{\ensuremath\kappa} + \DeclareUnicodeCharacter{03BB}{\ensuremath\lambda} + \DeclareUnicodeCharacter{03BC}{\ensuremath\mu} + \DeclareUnicodeCharacter{03BD}{\ensuremath\nu} + \DeclareUnicodeCharacter{03BE}{\ensuremath\xi} + \DeclareUnicodeCharacter{03BF}{{\it o}} % omicron + \DeclareUnicodeCharacter{03C0}{\ensuremath\pi} + \DeclareUnicodeCharacter{03C1}{\ensuremath\rho} + \DeclareUnicodeCharacter{03C2}{\ensuremath\varsigma} + \DeclareUnicodeCharacter{03C3}{\ensuremath\sigma} + \DeclareUnicodeCharacter{03C4}{\ensuremath\tau} + \DeclareUnicodeCharacter{03C5}{\ensuremath\upsilon} + \DeclareUnicodeCharacter{03C6}{\ensuremath\phi} + \DeclareUnicodeCharacter{03C7}{\ensuremath\chi} + \DeclareUnicodeCharacter{03C8}{\ensuremath\psi} + \DeclareUnicodeCharacter{03C9}{\ensuremath\omega} + % + % More Greek vowels with accents + \DeclareUnicodeCharacter{03CA}{\ensuremath{\ddot\iota}} + \DeclareUnicodeCharacter{03CB}{\ensuremath{\ddot\upsilon}} + \DeclareUnicodeCharacter{03CC}{\ensuremath{\acute o}} + \DeclareUnicodeCharacter{03CD}{\ensuremath{\acute\upsilon}} + \DeclareUnicodeCharacter{03CE}{\ensuremath{\acute\omega}} + % + % Variant Greek letters + \DeclareUnicodeCharacter{03D1}{\ensuremath\vartheta} + \DeclareUnicodeCharacter{03D6}{\ensuremath\varpi} + \DeclareUnicodeCharacter{03F1}{\ensuremath\varrho} + % + \DeclareUnicodeCharacter{1E02}{\dotaccent{B}} + \DeclareUnicodeCharacter{1E03}{\dotaccent{b}} + \DeclareUnicodeCharacter{1E04}{\udotaccent{B}} + \DeclareUnicodeCharacter{1E05}{\udotaccent{b}} + \DeclareUnicodeCharacter{1E06}{\ubaraccent{B}} + \DeclareUnicodeCharacter{1E07}{\ubaraccent{b}} + \DeclareUnicodeCharacter{1E0A}{\dotaccent{D}} + \DeclareUnicodeCharacter{1E0B}{\dotaccent{d}} + \DeclareUnicodeCharacter{1E0C}{\udotaccent{D}} + \DeclareUnicodeCharacter{1E0D}{\udotaccent{d}} + \DeclareUnicodeCharacter{1E0E}{\ubaraccent{D}} + \DeclareUnicodeCharacter{1E0F}{\ubaraccent{d}} + % + \DeclareUnicodeCharacter{1E1E}{\dotaccent{F}} + \DeclareUnicodeCharacter{1E1F}{\dotaccent{f}} + % + \DeclareUnicodeCharacter{1E20}{\=G} + \DeclareUnicodeCharacter{1E21}{\=g} + \DeclareUnicodeCharacter{1E22}{\dotaccent{H}} + \DeclareUnicodeCharacter{1E23}{\dotaccent{h}} + \DeclareUnicodeCharacter{1E24}{\udotaccent{H}} + \DeclareUnicodeCharacter{1E25}{\udotaccent{h}} + \DeclareUnicodeCharacter{1E26}{\"H} + \DeclareUnicodeCharacter{1E27}{\"h} + % + \DeclareUnicodeCharacter{1E30}{\'K} + \DeclareUnicodeCharacter{1E31}{\'k} + \DeclareUnicodeCharacter{1E32}{\udotaccent{K}} + \DeclareUnicodeCharacter{1E33}{\udotaccent{k}} + \DeclareUnicodeCharacter{1E34}{\ubaraccent{K}} + \DeclareUnicodeCharacter{1E35}{\ubaraccent{k}} + \DeclareUnicodeCharacter{1E36}{\udotaccent{L}} + \DeclareUnicodeCharacter{1E37}{\udotaccent{l}} + \DeclareUnicodeCharacter{1E3A}{\ubaraccent{L}} + \DeclareUnicodeCharacter{1E3B}{\ubaraccent{l}} + \DeclareUnicodeCharacter{1E3E}{\'M} + \DeclareUnicodeCharacter{1E3F}{\'m} + % + \DeclareUnicodeCharacter{1E40}{\dotaccent{M}} + \DeclareUnicodeCharacter{1E41}{\dotaccent{m}} + \DeclareUnicodeCharacter{1E42}{\udotaccent{M}} + \DeclareUnicodeCharacter{1E43}{\udotaccent{m}} + \DeclareUnicodeCharacter{1E44}{\dotaccent{N}} + \DeclareUnicodeCharacter{1E45}{\dotaccent{n}} + \DeclareUnicodeCharacter{1E46}{\udotaccent{N}} + \DeclareUnicodeCharacter{1E47}{\udotaccent{n}} + \DeclareUnicodeCharacter{1E48}{\ubaraccent{N}} + \DeclareUnicodeCharacter{1E49}{\ubaraccent{n}} + % + \DeclareUnicodeCharacter{1E54}{\'P} + \DeclareUnicodeCharacter{1E55}{\'p} + \DeclareUnicodeCharacter{1E56}{\dotaccent{P}} + \DeclareUnicodeCharacter{1E57}{\dotaccent{p}} + \DeclareUnicodeCharacter{1E58}{\dotaccent{R}} + \DeclareUnicodeCharacter{1E59}{\dotaccent{r}} + \DeclareUnicodeCharacter{1E5A}{\udotaccent{R}} + \DeclareUnicodeCharacter{1E5B}{\udotaccent{r}} + \DeclareUnicodeCharacter{1E5E}{\ubaraccent{R}} + \DeclareUnicodeCharacter{1E5F}{\ubaraccent{r}} + % + \DeclareUnicodeCharacter{1E60}{\dotaccent{S}} + \DeclareUnicodeCharacter{1E61}{\dotaccent{s}} + \DeclareUnicodeCharacter{1E62}{\udotaccent{S}} + \DeclareUnicodeCharacter{1E63}{\udotaccent{s}} + \DeclareUnicodeCharacter{1E6A}{\dotaccent{T}} + \DeclareUnicodeCharacter{1E6B}{\dotaccent{t}} + \DeclareUnicodeCharacter{1E6C}{\udotaccent{T}} + \DeclareUnicodeCharacter{1E6D}{\udotaccent{t}} + \DeclareUnicodeCharacter{1E6E}{\ubaraccent{T}} + \DeclareUnicodeCharacter{1E6F}{\ubaraccent{t}} + % + \DeclareUnicodeCharacter{1E7C}{\~V} + \DeclareUnicodeCharacter{1E7D}{\~v} + \DeclareUnicodeCharacter{1E7E}{\udotaccent{V}} + \DeclareUnicodeCharacter{1E7F}{\udotaccent{v}} + % + \DeclareUnicodeCharacter{1E80}{\`W} + \DeclareUnicodeCharacter{1E81}{\`w} + \DeclareUnicodeCharacter{1E82}{\'W} + \DeclareUnicodeCharacter{1E83}{\'w} + \DeclareUnicodeCharacter{1E84}{\"W} + \DeclareUnicodeCharacter{1E85}{\"w} + \DeclareUnicodeCharacter{1E86}{\dotaccent{W}} + \DeclareUnicodeCharacter{1E87}{\dotaccent{w}} + \DeclareUnicodeCharacter{1E88}{\udotaccent{W}} + \DeclareUnicodeCharacter{1E89}{\udotaccent{w}} + \DeclareUnicodeCharacter{1E8A}{\dotaccent{X}} + \DeclareUnicodeCharacter{1E8B}{\dotaccent{x}} + \DeclareUnicodeCharacter{1E8C}{\"X} + \DeclareUnicodeCharacter{1E8D}{\"x} + \DeclareUnicodeCharacter{1E8E}{\dotaccent{Y}} + \DeclareUnicodeCharacter{1E8F}{\dotaccent{y}} + % + \DeclareUnicodeCharacter{1E90}{\^Z} + \DeclareUnicodeCharacter{1E91}{\^z} + \DeclareUnicodeCharacter{1E92}{\udotaccent{Z}} + \DeclareUnicodeCharacter{1E93}{\udotaccent{z}} + \DeclareUnicodeCharacter{1E94}{\ubaraccent{Z}} + \DeclareUnicodeCharacter{1E95}{\ubaraccent{z}} + \DeclareUnicodeCharacter{1E96}{\ubaraccent{h}} + \DeclareUnicodeCharacter{1E97}{\"t} + \DeclareUnicodeCharacter{1E98}{\ringaccent{w}} + \DeclareUnicodeCharacter{1E99}{\ringaccent{y}} + % + \DeclareUnicodeCharacter{1EA0}{\udotaccent{A}} + \DeclareUnicodeCharacter{1EA1}{\udotaccent{a}} + % + \DeclareUnicodeCharacter{1EB8}{\udotaccent{E}} + \DeclareUnicodeCharacter{1EB9}{\udotaccent{e}} + \DeclareUnicodeCharacter{1EBC}{\~E} + \DeclareUnicodeCharacter{1EBD}{\~e} + % + \DeclareUnicodeCharacter{1ECA}{\udotaccent{I}} + \DeclareUnicodeCharacter{1ECB}{\udotaccent{i}} + \DeclareUnicodeCharacter{1ECC}{\udotaccent{O}} + \DeclareUnicodeCharacter{1ECD}{\udotaccent{o}} + % + \DeclareUnicodeCharacter{1EE4}{\udotaccent{U}} + \DeclareUnicodeCharacter{1EE5}{\udotaccent{u}} + % + \DeclareUnicodeCharacter{1EF2}{\`Y} + \DeclareUnicodeCharacter{1EF3}{\`y} + \DeclareUnicodeCharacter{1EF4}{\udotaccent{Y}} + % + \DeclareUnicodeCharacter{1EF8}{\~Y} + \DeclareUnicodeCharacter{1EF9}{\~y} + % + % Punctuation + \DeclareUnicodeCharacter{2013}{--} + \DeclareUnicodeCharacter{2014}{---} + \DeclareUnicodeCharacter{2018}{\quoteleft} + \DeclareUnicodeCharacter{2019}{\quoteright} + \DeclareUnicodeCharacter{201A}{\quotesinglbase} + \DeclareUnicodeCharacter{201C}{\quotedblleft} + \DeclareUnicodeCharacter{201D}{\quotedblright} + \DeclareUnicodeCharacter{201E}{\quotedblbase} + \DeclareUnicodeCharacter{2020}{\ensuremath\dagger} + \DeclareUnicodeCharacter{2021}{\ensuremath\ddagger} + \DeclareUnicodeCharacter{2022}{\bullet} + \DeclareUnicodeCharacter{202F}{\thinspace} + \DeclareUnicodeCharacter{2026}{\dots} + \DeclareUnicodeCharacter{2039}{\guilsinglleft} + \DeclareUnicodeCharacter{203A}{\guilsinglright} + % + \DeclareUnicodeCharacter{20AC}{\euro} + % + \DeclareUnicodeCharacter{2192}{\expansion} + \DeclareUnicodeCharacter{21D2}{\result} + % + % Mathematical symbols + \DeclareUnicodeCharacter{2200}{\ensuremath\forall} + \DeclareUnicodeCharacter{2203}{\ensuremath\exists} + \DeclareUnicodeCharacter{2208}{\ensuremath\in} + \DeclareUnicodeCharacter{2212}{\minus} + \DeclareUnicodeCharacter{2217}{\ast} + \DeclareUnicodeCharacter{221E}{\ensuremath\infty} + \DeclareUnicodeCharacter{2225}{\ensuremath\parallel} + \DeclareUnicodeCharacter{2227}{\ensuremath\wedge} + \DeclareUnicodeCharacter{2229}{\ensuremath\cap} + \DeclareUnicodeCharacter{2261}{\equiv} + \DeclareUnicodeCharacter{2264}{\ensuremath\leq} + \DeclareUnicodeCharacter{2265}{\ensuremath\geq} + \DeclareUnicodeCharacter{2282}{\ensuremath\subset} + \DeclareUnicodeCharacter{2287}{\ensuremath\supseteq} + % + \DeclareUnicodeCharacter{2016}{\ensuremath\Vert} + \DeclareUnicodeCharacter{2032}{\ensuremath\prime} + \DeclareUnicodeCharacter{210F}{\ensuremath\hbar} + \DeclareUnicodeCharacter{2111}{\ensuremath\Im} + \DeclareUnicodeCharacter{2113}{\ensuremath\ell} + \DeclareUnicodeCharacter{2118}{\ensuremath\wp} + \DeclareUnicodeCharacter{211C}{\ensuremath\Re} + \DeclareUnicodeCharacter{2127}{\ensuremath\mho} + \DeclareUnicodeCharacter{2135}{\ensuremath\aleph} + \DeclareUnicodeCharacter{2190}{\ensuremath\leftarrow} + \DeclareUnicodeCharacter{2191}{\ensuremath\uparrow} + \DeclareUnicodeCharacter{2193}{\ensuremath\downarrow} + \DeclareUnicodeCharacter{2194}{\ensuremath\leftrightarrow} + \DeclareUnicodeCharacter{2195}{\ensuremath\updownarrow} + \DeclareUnicodeCharacter{2196}{\ensuremath\nwarrow} + \DeclareUnicodeCharacter{2197}{\ensuremath\nearrow} + \DeclareUnicodeCharacter{2198}{\ensuremath\searrow} + \DeclareUnicodeCharacter{2199}{\ensuremath\swarrow} + \DeclareUnicodeCharacter{21A6}{\ensuremath\mapsto} + \DeclareUnicodeCharacter{21A9}{\ensuremath\hookleftarrow} + \DeclareUnicodeCharacter{21AA}{\ensuremath\hookrightarrow} + \DeclareUnicodeCharacter{21BC}{\ensuremath\leftharpoonup} + \DeclareUnicodeCharacter{21BD}{\ensuremath\leftharpoondown} + \DeclareUnicodeCharacter{21BE}{\ensuremath\upharpoonright} + \DeclareUnicodeCharacter{21C0}{\ensuremath\rightharpoonup} + \DeclareUnicodeCharacter{21C1}{\ensuremath\rightharpoondown} + \DeclareUnicodeCharacter{21CC}{\ensuremath\rightleftharpoons} + \DeclareUnicodeCharacter{21D0}{\ensuremath\Leftarrow} + \DeclareUnicodeCharacter{21D1}{\ensuremath\Uparrow} + \DeclareUnicodeCharacter{21D3}{\ensuremath\Downarrow} + \DeclareUnicodeCharacter{21D4}{\ensuremath\Leftrightarrow} + \DeclareUnicodeCharacter{21D5}{\ensuremath\Updownarrow} + \DeclareUnicodeCharacter{21DD}{\ensuremath\leadsto} + \DeclareUnicodeCharacter{2201}{\ensuremath\complement} + \DeclareUnicodeCharacter{2202}{\ensuremath\partial} + \DeclareUnicodeCharacter{2205}{\ensuremath\emptyset} + \DeclareUnicodeCharacter{2207}{\ensuremath\nabla} + \DeclareUnicodeCharacter{2209}{\ensuremath\notin} + \DeclareUnicodeCharacter{220B}{\ensuremath\owns} + \DeclareUnicodeCharacter{220F}{\ensuremath\prod} + \DeclareUnicodeCharacter{2210}{\ensuremath\coprod} + \DeclareUnicodeCharacter{2211}{\ensuremath\sum} + \DeclareUnicodeCharacter{2213}{\ensuremath\mp} + \DeclareUnicodeCharacter{2218}{\ensuremath\circ} + \DeclareUnicodeCharacter{221A}{\ensuremath\surd} + \DeclareUnicodeCharacter{221D}{\ensuremath\propto} + \DeclareUnicodeCharacter{2220}{\ensuremath\angle} + \DeclareUnicodeCharacter{2223}{\ensuremath\mid} + \DeclareUnicodeCharacter{2228}{\ensuremath\vee} + \DeclareUnicodeCharacter{222A}{\ensuremath\cup} + \DeclareUnicodeCharacter{222B}{\ensuremath\smallint} + \DeclareUnicodeCharacter{222E}{\ensuremath\oint} + \DeclareUnicodeCharacter{223C}{\ensuremath\sim} + \DeclareUnicodeCharacter{2240}{\ensuremath\wr} + \DeclareUnicodeCharacter{2243}{\ensuremath\simeq} + \DeclareUnicodeCharacter{2245}{\ensuremath\cong} + \DeclareUnicodeCharacter{2248}{\ensuremath\approx} + \DeclareUnicodeCharacter{224D}{\ensuremath\asymp} + \DeclareUnicodeCharacter{2250}{\ensuremath\doteq} + \DeclareUnicodeCharacter{2260}{\ensuremath\neq} + \DeclareUnicodeCharacter{226A}{\ensuremath\ll} + \DeclareUnicodeCharacter{226B}{\ensuremath\gg} + \DeclareUnicodeCharacter{227A}{\ensuremath\prec} + \DeclareUnicodeCharacter{227B}{\ensuremath\succ} + \DeclareUnicodeCharacter{2283}{\ensuremath\supset} + \DeclareUnicodeCharacter{2286}{\ensuremath\subseteq} + \DeclareUnicodeCharacter{228E}{\ensuremath\uplus} + \DeclareUnicodeCharacter{228F}{\ensuremath\sqsubset} + \DeclareUnicodeCharacter{2290}{\ensuremath\sqsupset} + \DeclareUnicodeCharacter{2291}{\ensuremath\sqsubseteq} + \DeclareUnicodeCharacter{2292}{\ensuremath\sqsupseteq} + \DeclareUnicodeCharacter{2293}{\ensuremath\sqcap} + \DeclareUnicodeCharacter{2294}{\ensuremath\sqcup} + \DeclareUnicodeCharacter{2295}{\ensuremath\oplus} + \DeclareUnicodeCharacter{2296}{\ensuremath\ominus} + \DeclareUnicodeCharacter{2297}{\ensuremath\otimes} + \DeclareUnicodeCharacter{2298}{\ensuremath\oslash} + \DeclareUnicodeCharacter{2299}{\ensuremath\odot} + \DeclareUnicodeCharacter{22A2}{\ensuremath\vdash} + \DeclareUnicodeCharacter{22A3}{\ensuremath\dashv} + \DeclareUnicodeCharacter{22A4}{\ensuremath\ptextop} + \DeclareUnicodeCharacter{22A5}{\ensuremath\bot} + \DeclareUnicodeCharacter{22A8}{\ensuremath\models} + \DeclareUnicodeCharacter{22B4}{\ensuremath\unlhd} + \DeclareUnicodeCharacter{22B5}{\ensuremath\unrhd} + \DeclareUnicodeCharacter{22C0}{\ensuremath\bigwedge} + \DeclareUnicodeCharacter{22C1}{\ensuremath\bigvee} + \DeclareUnicodeCharacter{22C2}{\ensuremath\bigcap} + \DeclareUnicodeCharacter{22C3}{\ensuremath\bigcup} + \DeclareUnicodeCharacter{22C4}{\ensuremath\diamond} + \DeclareUnicodeCharacter{22C5}{\ensuremath\cdot} + \DeclareUnicodeCharacter{22C6}{\ensuremath\star} + \DeclareUnicodeCharacter{22C8}{\ensuremath\bowtie} + \DeclareUnicodeCharacter{2308}{\ensuremath\lceil} + \DeclareUnicodeCharacter{2309}{\ensuremath\rceil} + \DeclareUnicodeCharacter{230A}{\ensuremath\lfloor} + \DeclareUnicodeCharacter{230B}{\ensuremath\rfloor} + \DeclareUnicodeCharacter{2322}{\ensuremath\frown} + \DeclareUnicodeCharacter{2323}{\ensuremath\smile} + % + \DeclareUnicodeCharacter{25A1}{\ensuremath\Box} + \DeclareUnicodeCharacter{25B3}{\ensuremath\triangle} + \DeclareUnicodeCharacter{25B7}{\ensuremath\triangleright} + \DeclareUnicodeCharacter{25BD}{\ensuremath\bigtriangledown} + \DeclareUnicodeCharacter{25C1}{\ensuremath\triangleleft} + \DeclareUnicodeCharacter{25C7}{\ensuremath\Diamond} + \DeclareUnicodeCharacter{2660}{\ensuremath\spadesuit} + \DeclareUnicodeCharacter{2661}{\ensuremath\heartsuit} + \DeclareUnicodeCharacter{2662}{\ensuremath\diamondsuit} + \DeclareUnicodeCharacter{2663}{\ensuremath\clubsuit} + \DeclareUnicodeCharacter{266D}{\ensuremath\flat} + \DeclareUnicodeCharacter{266E}{\ensuremath\natural} + \DeclareUnicodeCharacter{266F}{\ensuremath\sharp} + \DeclareUnicodeCharacter{26AA}{\ensuremath\bigcirc} + \DeclareUnicodeCharacter{27B9}{\ensuremath\rangle} + \DeclareUnicodeCharacter{27C2}{\ensuremath\perp} + \DeclareUnicodeCharacter{27E8}{\ensuremath\langle} + \DeclareUnicodeCharacter{27F5}{\ensuremath\longleftarrow} + \DeclareUnicodeCharacter{27F6}{\ensuremath\longrightarrow} + \DeclareUnicodeCharacter{27F7}{\ensuremath\longleftrightarrow} + \DeclareUnicodeCharacter{27FC}{\ensuremath\longmapsto} + \DeclareUnicodeCharacter{29F5}{\ensuremath\setminus} + \DeclareUnicodeCharacter{2A00}{\ensuremath\bigodot} + \DeclareUnicodeCharacter{2A01}{\ensuremath\bigoplus} + \DeclareUnicodeCharacter{2A02}{\ensuremath\bigotimes} + \DeclareUnicodeCharacter{2A04}{\ensuremath\biguplus} + \DeclareUnicodeCharacter{2A06}{\ensuremath\bigsqcup} + \DeclareUnicodeCharacter{2A1D}{\ensuremath\Join} + \DeclareUnicodeCharacter{2A3F}{\ensuremath\amalg} + \DeclareUnicodeCharacter{2AAF}{\ensuremath\preceq} + \DeclareUnicodeCharacter{2AB0}{\ensuremath\succeq} + % + \global\mathchardef\checkmark="1370 % actually the square root sign + \DeclareUnicodeCharacter{2713}{\ensuremath\checkmark} +}% end of \utfeightchardefs + +% US-ASCII character definitions. +\def\asciichardefs{% nothing need be done + \relax +} + +% Latin1 (ISO-8859-1) character definitions. +\def\nonasciistringdefs{% + \setnonasciicharscatcode\active + \def\defstringchar##1{\def##1{\string##1}}% + % + \defstringchar^^80\defstringchar^^81\defstringchar^^82\defstringchar^^83% + \defstringchar^^84\defstringchar^^85\defstringchar^^86\defstringchar^^87% + \defstringchar^^88\defstringchar^^89\defstringchar^^8a\defstringchar^^8b% + \defstringchar^^8c\defstringchar^^8d\defstringchar^^8e\defstringchar^^8f% + % + \defstringchar^^90\defstringchar^^91\defstringchar^^92\defstringchar^^93% + \defstringchar^^94\defstringchar^^95\defstringchar^^96\defstringchar^^97% + \defstringchar^^98\defstringchar^^99\defstringchar^^9a\defstringchar^^9b% + \defstringchar^^9c\defstringchar^^9d\defstringchar^^9e\defstringchar^^9f% + % + \defstringchar^^a0\defstringchar^^a1\defstringchar^^a2\defstringchar^^a3% + \defstringchar^^a4\defstringchar^^a5\defstringchar^^a6\defstringchar^^a7% + \defstringchar^^a8\defstringchar^^a9\defstringchar^^aa\defstringchar^^ab% + \defstringchar^^ac\defstringchar^^ad\defstringchar^^ae\defstringchar^^af% + % + \defstringchar^^b0\defstringchar^^b1\defstringchar^^b2\defstringchar^^b3% + \defstringchar^^b4\defstringchar^^b5\defstringchar^^b6\defstringchar^^b7% + \defstringchar^^b8\defstringchar^^b9\defstringchar^^ba\defstringchar^^bb% + \defstringchar^^bc\defstringchar^^bd\defstringchar^^be\defstringchar^^bf% + % + \defstringchar^^c0\defstringchar^^c1\defstringchar^^c2\defstringchar^^c3% + \defstringchar^^c4\defstringchar^^c5\defstringchar^^c6\defstringchar^^c7% + \defstringchar^^c8\defstringchar^^c9\defstringchar^^ca\defstringchar^^cb% + \defstringchar^^cc\defstringchar^^cd\defstringchar^^ce\defstringchar^^cf% + % + \defstringchar^^d0\defstringchar^^d1\defstringchar^^d2\defstringchar^^d3% + \defstringchar^^d4\defstringchar^^d5\defstringchar^^d6\defstringchar^^d7% + \defstringchar^^d8\defstringchar^^d9\defstringchar^^da\defstringchar^^db% + \defstringchar^^dc\defstringchar^^dd\defstringchar^^de\defstringchar^^df% + % + \defstringchar^^e0\defstringchar^^e1\defstringchar^^e2\defstringchar^^e3% + \defstringchar^^e4\defstringchar^^e5\defstringchar^^e6\defstringchar^^e7% + \defstringchar^^e8\defstringchar^^e9\defstringchar^^ea\defstringchar^^eb% + \defstringchar^^ec\defstringchar^^ed\defstringchar^^ee\defstringchar^^ef% + % + \defstringchar^^f0\defstringchar^^f1\defstringchar^^f2\defstringchar^^f3% + \defstringchar^^f4\defstringchar^^f5\defstringchar^^f6\defstringchar^^f7% + \defstringchar^^f8\defstringchar^^f9\defstringchar^^fa\defstringchar^^fb% + \defstringchar^^fc\defstringchar^^fd\defstringchar^^fe\defstringchar^^ff% +} + + +% define all the unicode characters we know about, for the sake of @U. +\utfeightchardefs + + +% Make non-ASCII characters printable again for compatibility with +% existing Texinfo documents that may use them, even without declaring a +% document encoding. +% +\setnonasciicharscatcode \other + + +\message{formatting,} + +\newdimen\defaultparindent \defaultparindent = 15pt + +\chapheadingskip = 15pt plus 4pt minus 2pt +\secheadingskip = 12pt plus 3pt minus 2pt +\subsecheadingskip = 9pt plus 2pt minus 2pt + +% Prevent underfull vbox error messages. +\vbadness = 10000 + +% Don't be very finicky about underfull hboxes, either. +\hbadness = 6666 + +% Following George Bush, get rid of widows and orphans. +\widowpenalty=10000 +\clubpenalty=10000 + +% Use TeX 3.0's \emergencystretch to help line breaking, but if we're +% using an old version of TeX, don't do anything. We want the amount of +% stretch added to depend on the line length, hence the dependence on +% \hsize. We call this whenever the paper size is set. +% +\def\setemergencystretch{% + \ifx\emergencystretch\thisisundefined + % Allow us to assign to \emergencystretch anyway. + \def\emergencystretch{\dimen0}% + \else + \emergencystretch = .15\hsize + \fi +} + +% Parameters in order: 1) textheight; 2) textwidth; +% 3) voffset; 4) hoffset; 5) binding offset; 6) topskip; +% 7) physical page height; 8) physical page width. +% +% We also call \setleading{\textleading}, so the caller should define +% \textleading. The caller should also set \parskip. +% +\def\internalpagesizes#1#2#3#4#5#6#7#8{% + \voffset = #3\relax + \topskip = #6\relax + \splittopskip = \topskip + % + \vsize = #1\relax + \advance\vsize by \topskip + \outervsize = \vsize + \advance\outervsize by 2\topandbottommargin + \pageheight = \vsize + % + \hsize = #2\relax + \outerhsize = \hsize + \advance\outerhsize by 0.5in + \pagewidth = \hsize + % + \normaloffset = #4\relax + \bindingoffset = #5\relax + % + \ifpdf + \pdfpageheight #7\relax + \pdfpagewidth #8\relax + % if we don't reset these, they will remain at "1 true in" of + % whatever layout pdftex was dumped with. + \pdfhorigin = 1 true in + \pdfvorigin = 1 true in + \fi + % + \setleading{\textleading} + % + \parindent = \defaultparindent + \setemergencystretch +} + +% @letterpaper (the default). +\def\letterpaper{{\globaldefs = 1 + \parskip = 3pt plus 2pt minus 1pt + \textleading = 13.2pt + % + % If page is nothing but text, make it come out even. + \internalpagesizes{607.2pt}{6in}% that's 46 lines + {\voffset}{.25in}% + {\bindingoffset}{36pt}% + {11in}{8.5in}% +}} + +% Use @smallbook to reset parameters for 7x9.25 trim size. +\def\smallbook{{\globaldefs = 1 + \parskip = 2pt plus 1pt + \textleading = 12pt + % + \internalpagesizes{7.5in}{5in}% + {-.2in}{0in}% + {\bindingoffset}{16pt}% + {9.25in}{7in}% + % + \lispnarrowing = 0.3in + \tolerance = 700 + \hfuzz = 1pt + \contentsrightmargin = 0pt + \defbodyindent = .5cm +}} + +% Use @smallerbook to reset parameters for 6x9 trim size. +% (Just testing, parameters still in flux.) +\def\smallerbook{{\globaldefs = 1 + \parskip = 1.5pt plus 1pt + \textleading = 12pt + % + \internalpagesizes{7.4in}{4.8in}% + {-.2in}{-.4in}% + {0pt}{14pt}% + {9in}{6in}% + % + \lispnarrowing = 0.25in + \tolerance = 700 + \hfuzz = 1pt + \contentsrightmargin = 0pt + \defbodyindent = .4cm +}} + +% Use @afourpaper to print on European A4 paper. +\def\afourpaper{{\globaldefs = 1 + \parskip = 3pt plus 2pt minus 1pt + \textleading = 13.2pt + % + % Double-side printing via postscript on Laserjet 4050 + % prints double-sided nicely when \bindingoffset=10mm and \hoffset=-6mm. + % To change the settings for a different printer or situation, adjust + % \normaloffset until the front-side and back-side texts align. Then + % do the same for \bindingoffset. You can set these for testing in + % your texinfo source file like this: + % @tex + % \global\normaloffset = -6mm + % \global\bindingoffset = 10mm + % @end tex + \internalpagesizes{673.2pt}{160mm}% that's 51 lines + {\voffset}{\hoffset}% + {\bindingoffset}{44pt}% + {297mm}{210mm}% + % + \tolerance = 700 + \hfuzz = 1pt + \contentsrightmargin = 0pt + \defbodyindent = 5mm +}} + +% Use @afivepaper to print on European A5 paper. +% From romildo@urano.iceb.ufop.br, 2 July 2000. +% He also recommends making @example and @lisp be small. +\def\afivepaper{{\globaldefs = 1 + \parskip = 2pt plus 1pt minus 0.1pt + \textleading = 12.5pt + % + \internalpagesizes{160mm}{120mm}% + {\voffset}{\hoffset}% + {\bindingoffset}{8pt}% + {210mm}{148mm}% + % + \lispnarrowing = 0.2in + \tolerance = 800 + \hfuzz = 1.2pt + \contentsrightmargin = 0pt + \defbodyindent = 2mm + \tableindent = 12mm +}} + +% A specific text layout, 24x15cm overall, intended for A4 paper. +\def\afourlatex{{\globaldefs = 1 + \afourpaper + \internalpagesizes{237mm}{150mm}% + {\voffset}{4.6mm}% + {\bindingoffset}{7mm}% + {297mm}{210mm}% + % + % Must explicitly reset to 0 because we call \afourpaper. + \globaldefs = 0 +}} + +% Use @afourwide to print on A4 paper in landscape format. +\def\afourwide{{\globaldefs = 1 + \afourpaper + \internalpagesizes{241mm}{165mm}% + {\voffset}{-2.95mm}% + {\bindingoffset}{7mm}% + {297mm}{210mm}% + \globaldefs = 0 +}} + +% @pagesizes TEXTHEIGHT[,TEXTWIDTH] +% Perhaps we should allow setting the margins, \topskip, \parskip, +% and/or leading, also. Or perhaps we should compute them somehow. +% +\parseargdef\pagesizes{\pagesizesyyy #1,,\finish} +\def\pagesizesyyy#1,#2,#3\finish{{% + \setbox0 = \hbox{\ignorespaces #2}\ifdim\wd0 > 0pt \hsize=#2\relax \fi + \globaldefs = 1 + % + \parskip = 3pt plus 2pt minus 1pt + \setleading{\textleading}% + % + \dimen0 = #1\relax + \advance\dimen0 by \voffset + % + \dimen2 = \hsize + \advance\dimen2 by \normaloffset + % + \internalpagesizes{#1}{\hsize}% + {\voffset}{\normaloffset}% + {\bindingoffset}{44pt}% + {\dimen0}{\dimen2}% +}} + +% Set default to letter. +% +\letterpaper + + +\message{and turning on texinfo input format.} + +\def^^L{\par} % remove \outer, so ^L can appear in an @comment + +% DEL is a comment character, in case @c does not suffice. +\catcode`\^^? = 14 + +% Define macros to output various characters with catcode for normal text. +\catcode`\"=\other \def\normaldoublequote{"} +\catcode`\$=\other \def\normaldollar{$}%$ font-lock fix +\catcode`\+=\other \def\normalplus{+} +\catcode`\<=\other \def\normalless{<} +\catcode`\>=\other \def\normalgreater{>} +\catcode`\^=\other \def\normalcaret{^} +\catcode`\_=\other \def\normalunderscore{_} +\catcode`\|=\other \def\normalverticalbar{|} +\catcode`\~=\other \def\normaltilde{~} + +% This macro is used to make a character print one way in \tt +% (where it can probably be output as-is), and another way in other fonts, +% where something hairier probably needs to be done. +% +% #1 is what to print if we are indeed using \tt; #2 is what to print +% otherwise. Since all the Computer Modern typewriter fonts have zero +% interword stretch (and shrink), and it is reasonable to expect all +% typewriter fonts to have this, we can check that font parameter. +% +\def\ifusingtt#1#2{\ifdim \fontdimen3\font=0pt #1\else #2\fi} + +% Same as above, but check for italic font. Actually this also catches +% non-italic slanted fonts since it is impossible to distinguish them from +% italic fonts. But since this is only used by $ and it uses \sl anyway +% this is not a problem. +\def\ifusingit#1#2{\ifdim \fontdimen1\font>0pt #1\else #2\fi} + +% Set catcodes for Texinfo file + +% Active characters for printing the wanted glyph. +% Most of these we simply print from the \tt font, but for some, we can +% use math or other variants that look better in normal text. +% +\catcode`\"=\active +\def\activedoublequote{{\tt\char34}} +\let"=\activedoublequote +\catcode`\~=\active \def\activetilde{{\tt\char126}} \let~ = \activetilde +\chardef\hatchar=`\^ +\catcode`\^=\active \def\activehat{{\tt \hatchar}} \let^ = \activehat + +\catcode`\_=\active +\def_{\ifusingtt\normalunderscore\_} +\def\_{\leavevmode \kern.07em \vbox{\hrule width.3em height.1ex}\kern .07em } +\let\realunder=_ + +\catcode`\|=\active \def|{{\tt\char124}} + +\chardef \less=`\< +\catcode`\<=\active \def\activeless{{\tt \less}}\let< = \activeless +\chardef \gtr=`\> +\catcode`\>=\active \def\activegtr{{\tt \gtr}}\let> = \activegtr +\catcode`\+=\active \def+{{\tt \char 43}} +\catcode`\$=\active \def${\ifusingit{{\sl\$}}\normaldollar}%$ font-lock fix +\catcode`\-=\active \let-=\normaldash + + +% used for headline/footline in the output routine, in case the page +% breaks in the middle of an @tex block. +\def\texinfochars{% + \let< = \activeless + \let> = \activegtr + \let~ = \activetilde + \let^ = \activehat + \markupsetuplqdefault \markupsetuprqdefault + \let\b = \strong + \let\i = \smartitalic + % in principle, all other definitions in \tex have to be undone too. +} + +% Used sometimes to turn off (effectively) the active characters even after +% parsing them. +\def\turnoffactive{% + \normalturnoffactive + \otherbackslash +} + +\catcode`\@=0 + +% \backslashcurfont outputs one backslash character in current font, +% as in \char`\\. +\global\chardef\backslashcurfont=`\\ +\global\let\rawbackslashxx=\backslashcurfont % let existing .??s files work + +% \realbackslash is an actual character `\' with catcode other, and +% \doublebackslash is two of them (for the pdf outlines). +{\catcode`\\=\other @gdef@realbackslash{\} @gdef@doublebackslash{\\}} + +% In Texinfo, backslash is an active character; it prints the backslash +% in fixed width font. +\catcode`\\=\active % @ for escape char from now on. + +% Print a typewriter backslash. For math mode, we can't simply use +% \backslashcurfont: the story here is that in math mode, the \char +% of \backslashcurfont ends up printing the roman \ from the math symbol +% font (because \char in math mode uses the \mathcode, and plain.tex +% sets \mathcode`\\="026E). Hence we use an explicit \mathchar, +% which is the decimal equivalent of "715c (class 7, e.g., use \fam; +% ignored family value; char position "5C). We can't use " for the +% usual hex value because it has already been made active. + +@def@ttbackslash{{@tt @ifmmode @mathchar29020 @else @backslashcurfont @fi}} +@let@backslashchar = @ttbackslash % @backslashchar{} is for user documents. + +% \rawbackslash defines an active \ to do \backslashcurfont. +% \otherbackslash defines an active \ to be a literal `\' character with +% catcode other. We switch back and forth between these. +@gdef@rawbackslash{@let\=@backslashcurfont} +@gdef@otherbackslash{@let\=@realbackslash} + +% Same as @turnoffactive except outputs \ as {\tt\char`\\} instead of +% the literal character `\'. +% +{@catcode`- = @active + @gdef@normalturnoffactive{% + @nonasciistringdefs + @let-=@normaldash + @let"=@normaldoublequote + @let$=@normaldollar %$ font-lock fix + @let+=@normalplus + @let<=@normalless + @let>=@normalgreater + @let^=@normalcaret + @let_=@normalunderscore + @let|=@normalverticalbar + @let~=@normaltilde + @let\=@ttbackslash + @markupsetuplqdefault + @markupsetuprqdefault + @unsepspaces + } +} + +% If a .fmt file is being used, characters that might appear in a file +% name cannot be active until we have parsed the command line. +% So turn them off again, and have @fixbackslash turn them back on. +@catcode`+=@other @catcode`@_=@other + +% \enablebackslashhack - allow file to begin `\input texinfo' +% +% If a .fmt file is being used, we don't want the `\input texinfo' to show up. +% That is what \eatinput is for; after that, the `\' should revert to printing +% a backslash. +% If the file did not have a `\input texinfo', then it is turned off after +% the first line; otherwise the first `\' in the file would cause an error. +% This is used on the very last line of this file, texinfo.tex. +% We also use @c to call @fixbackslash, in case ends of lines are hidden. +{ +@catcode`@^=7 +@catcode`@^^M=13@gdef@enablebackslashhack{% + @global@let\ = @eatinput% + @catcode`@^^M=13% + @def@c{@fixbackslash@c}% + @def ^^M{@let^^M@secondlinenl}% + @gdef @secondlinenl{@let^^M@thirdlinenl}% + @gdef @thirdlinenl{@fixbackslash}% +}} + +{@catcode`@^=7 @catcode`@^^M=13% +@gdef@eatinput input texinfo#1^^M{@fixbackslash}} + +% Emergency active definition of newline, in case an active newline token +% appears by mistake. +{@catcode`@^=7 @catcode13=13% +@gdef@enableemergencynewline{% + @gdef^^M{% + @par% + %@par% +}}} + + +@gdef@fixbackslash{% + @ifx\@eatinput @let\ = @ttbackslash @fi + @catcode13=5 % regular end of line + @enableemergencynewline + @let@c=@texinfoc + % Also turn back on active characters that might appear in the input + % file name, in case not using a pre-dumped format. + @catcode`+=@active + @catcode`@_=@active + % + % If texinfo.cnf is present on the system, read it. + % Useful for site-wide @afourpaper, etc. This macro, @fixbackslash, gets + % called at the beginning of every Texinfo file. Not opening texinfo.cnf + % directly in this file, texinfo.tex, makes it possible to make a format + % file for Texinfo. + % + @openin 1 texinfo.cnf + @ifeof 1 @else @input texinfo.cnf @fi + @closein 1 +} + + +% Say @foo, not \foo, in error messages. +@escapechar = `@@ + +% These (along with & and #) are made active for url-breaking, so need +% active definitions as the normal characters. +@def@normaldot{.} +@def@normalquest{?} +@def@normalslash{/} + +% These look ok in all fonts, so just make them not special. +% @hashchar{} gets its own user-level command, because of #line. +@catcode`@& = @other @def@normalamp{&} +@catcode`@# = @other @def@normalhash{#} +@catcode`@% = @other @def@normalpercent{%} + +@let @hashchar = @normalhash + +@c Finally, make ` and ' active, so that txicodequoteundirected and +@c txicodequotebacktick work right in, e.g., @w{@code{`foo'}}. If we +@c don't make ` and ' active, @code will not get them as active chars. +@c Do this last of all since we use ` in the previous @catcode assignments. +@catcode`@'=@active +@catcode`@`=@active +@markupsetuplqdefault +@markupsetuprqdefault + +@c Local variables: +@c eval: (add-hook 'write-file-hooks 'time-stamp) +@c page-delimiter: "^\\\\message\\|emacs-page" +@c time-stamp-start: "def\\\\texinfoversion{" +@c time-stamp-format: "%:y-%02m-%02d.%02H" +@c time-stamp-end: "}" +@c End: + +@c vim:sw=2: + +@ignore + arch-tag: e1b36e32-c96e-4135-a41a-0b2efa2ea115 +@end ignore +@enablebackslashhack diff --git a/doc/vtysh.1 b/doc/vtysh.1 index de4913e51..a2afa9ff1 100644 --- a/doc/vtysh.1 +++ b/doc/vtysh.1 @@ -76,6 +76,9 @@ config file. .BI /usr/local/etc/Quagga.conf The default location of the integrated Quagga routing engine config file if integrated config file is in use (not default). +.TP +.BI ${HOME}/.history_quagga +Location of history of commands entered via cli .SH WARNING This man page is intended to be a quick reference for command line options. The definitive document is the Info file \fBQuagga\fR. diff --git a/doc/watchquagga.8 b/doc/watchquagga.8 new file mode 100644 index 000000000..ca9916461 --- /dev/null +++ b/doc/watchquagga.8 @@ -0,0 +1,231 @@ +.\" This file was originally generated by help2man 1.36. +.TH WATCHQUAGGA 8 "July 2010" +.SH NAME +watchquagga \- a program to monitor the status of quagga daemons +.SH SYNOPSIS +.B watchquagga +.RI [ option ...] +.IR daemon ... +.br +.B watchquagga +.BR \-h " | " \-v +.SH DESCRIPTION +.B watchquagga +is a watchdog program that monitors the status of supplied quagga +.IR daemon s +and tries to restart them in case they become unresponsive or shut down. +.PP +To determine whether a daemon is running, it tries to connect to the +daemon's VTY UNIX stream socket, and send echo commands to ensure the +daemon responds. When the daemon crashes, EOF is received from the socket, +so that watchquagga can react immediately. +.PP +This program can run in one of the following 5 modes: +.TP +.B Mode 0: monitor +In this mode, the program serves as a monitor and reports status changes. +.IP +Example usage: watchquagga \-d zebra ospfd bgpd +.TP +.B Mode 1: global restart +In this mode, whenever a daemon hangs or crashes, the given command is used +to restart all watched daemons. +.IP +Example usage: watchquagga \-dz \e +.br +-R '/sbin/service zebra restart; /sbin/service ospfd restart' \e +.br +zebra ospfd +.TP +.B Mode 2: individual daemon restart +In this mode, whenever a single daemon hangs or crashes, the given command +is used to restart this daemon only. +.IP +Example usage: watchquagga \-dz \-r '/sbin/service %s restart' \e +.br +zebra ospfd bgpd +.TP +.B Mode 3: phased zebra restart +In this mode, whenever a single daemon hangs or crashes, the given command +is used to restart this daemon only. The only exception is the zebra +daemon; in this case, the following steps are taken: (1) all other daemons +are stopped, (2) zebra is restarted, and (3) other daemons are started +again. +.IP +Example usage: watchquagga \-adz \-r '/sbin/service %s restart' \e +.br +\-s '/sbin/service %s start' \e +.br +\-k '/sbin/service %s stop' zebra ospfd bgpd +.TP +.B Mode 4: phased global restart for any failure +In this mode, whenever a single daemon hangs or crashes, the following +steps are taken: (1) all other daemons are stopped, (2) zebra is restarted, +and (3) other daemons are started again. +.IP +Example usage: watchquagga \-Adz \-r '/sbin/service %s restart' \e +.br +\-s '/sbin/service %s start' \e +.br +\-k '/sbin/service %s stop' zebra ospfd bgpd +.PP +Important: It is believed that mode 2 (individual daemon restart) is not +safe, and mode 3 (phased zebra restart) may not be safe with certain +routing daemons. +.PP +In order to avoid restarting the daemons in quick succession, you can +supply the +.B \-m +and +.B \-M +options to set the minimum and maximum delay between the restart commands. +The minimum restart delay is recalculated each time a restart is attempted. +If the time since the last restart attempt exceeds twice the value of +.BR \-M , +the restart delay is set to the value of +.BR \-m , +otherwise the interval is doubled (but capped at the value of +.BR \-M ). +.SH OPTIONS +.TP +.BR \-d ", " \-\-daemon +Run in daemon mode. When supplied, error messages are sent to Syslog +instead of standard output (stdout). +.TP +.BI \-S " directory" "\fR, \fB\-\-statedir " directory +Set the VTY socket +.I directory +(the default value is "/var/run/quagga"). +.TP +.BR \-e ", " \-\-no\-echo +Do not ping the daemons to test whether they respond. This option is +necessary if one or more daemons do not support the echo command. +.TP +.BI \-l " level" "\fR, \fB\-\-loglevel " level +Set the logging +.I level +(the default value is "6"). The value should range from 0 (LOG_EMERG) to 7 +(LOG_DEBUG), but higher number can be supplied if extra debugging messages +are required. +.TP +.BI \-m " number" "\fR, \fB\-\-min\-restart\-interval " number +Set the minimum +.I number +of seconds to wait between invocations of the daemon restart commands (the +default value is "60"). +.TP +.BI \-M " number" "\fR, \fB\-\-max\-restart\-interval " number +Set the maximum +.I number +of seconds to wait between invocations of the daemon restart commands (the +default value is "600"). +.TP +.BI \-i " number" "\fR, \fB\-\-interval " number +Set the status polling interval in seconds (the default value is "5"). +.TP +.BI \-t " number" "\fR, \fB\-\-timeout " number +Set the unresponsiveness timeout in seconds (the default value is "10"). +.TP +.BI \-T " number" "\fR, \fB\-\-restart\-timeout " number +Set the restart (kill) timeout in seconds (the default value is "20"). If +any background jobs are still running after this period has elapsed, they +will be killed. +.TP +.BI \-r " command" "\fR, \fB\-\-restart " command +Supply a Bourne shell +.I command +to restart a single daemon. The command string should contain the '%s' +placeholder to be substituted with the daemon name. +.IP +Note that +.B \-r +and +.B \-R +options are not compatible. +.TP +.BI \-s " command" "\fR, \fB\-\-start\-command " command +Supply a Bourne shell +.I command +to start a single daemon. The command string should contain the '%s' +placeholder to be substituted with the daemon name. +.TP +.BI \-k " command" "\fR, \fB\-\-kill\-command " command +Supply a Bourne shell +.I command +to stop a single daemon. The command string should contain the '%s' +placeholder to be substituted with the daemon name. +.TP +.BR \-R ", " \-\-restart\-all +When one or more daemons are shut down, try to restart them using the +Bourne shell command supplied on the command line. +.IP +Note that +.B \-r +and +.B \-R +options are not compatible. +.TP +.BR \-z ", " \-\-unresponsive\-restart +When a daemon is in an unresponsive state, treat it as being shut down for +the restart purposes. +.TP +.BR \-a ", " \-\-all\-restart +When zebra hangs or crashes, restart all daemons taking the following +steps: (1) stop all other daemons, (2) restart zebra, and (3) start other +daemons again. +.IP +Note that this option also requires +.BR \-r , +.BR \-s , +and +.B \-k +options to be specified. +.TP +.BR \-A ", " \-\-always\-all\-restart +When any daemon (i.e., not just zebra) hangs or crashes, restart all +daemons taking the following steps: (1) stop all other daemons, (2) restart +zebra, and (3) start other daemons again. +.IP +Note that this option also requires +.BR \-r , +.BR \-s , +and +.B \-k +options to be specified. +.TP +.BI \-p " filename" "\fR, \fB\-\-pid\-file " filename +Set the process identifier +.I filename +(the default value is "/var/run/quagga/watchquagga.pid"). +.TP +.BI \-b " string" "\fR, \fB\-\-blank\-string " string +When the supplied +.I string +is found in any of the command line option arguments (i.e., +.BR \-r , +.BR \-s , +.BR \-k , +or +.BR \-R ), +replace it with a space. +.IP +This is an ugly hack to circumvent problems with passing the command line +arguments containing embedded spaces. +.TP +.BR \-v ", " \-\-version +Display the version information and exit. +.TP +.BR \-h ", " \-\-help +Display the usage information and exit. +.SH SEE ALSO +.BR zebra (8), +.BR bgpd (8), +.BR isisd (8), +.BR ospfd (8), +.BR ospf6d (8), +.BR ripd (8), +.BR ripngd (8) +.PP +See the project homepage at . +.SH AUTHORS +Copyright 2004 Andrew J. Schorr diff --git a/doc/zebra.8 b/doc/zebra.8 index 23703e727..6f70389fe 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. @@ -119,6 +119,7 @@ debugging options, see the Info file, or the source for details. .BR ospfd (8), .BR ospf6d (8), .BR isisd (8), +.BR nhrpd (8), .BR vtysh (1) .SH BUGS .B zebra diff --git a/guile/.gitignore b/fpm/.gitignore similarity index 64% rename from guile/.gitignore rename to fpm/.gitignore index 5c2e06b67..b133c52a4 100644 --- a/guile/.gitignore +++ b/fpm/.gitignore @@ -1,10 +1,15 @@ Makefile -*.o -zebra-guile Makefile.in +*.o +tags +TAGS +.deps .nfs* +*.lo +*.la +*.a +*.libs .arch-inventory .arch-ids *~ *.loT - diff --git a/fpm/Makefile.am b/fpm/Makefile.am new file mode 100644 index 000000000..83ab31ce3 --- /dev/null +++ b/fpm/Makefile.am @@ -0,0 +1,29 @@ +include ../common.am + +AM_CPPFLAGS = -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib $(Q_PROTOBUF_C_CLIENT_INCLUDES) + +PROTOBUF_INCLUDES=-I$(top_srcdir) +PROTOBUF_PACKAGE = fpm + +lib_LTLIBRARIES = libfpm_pb.la +libfpm_pb_la_LDFLAGS = -version-info 0:0:0 + +if HAVE_PROTOBUF +protobuf_srcs = + +protobuf_srcs_nodist = \ + fpm.pb-c.c +endif + +libfpm_pb_la_SOURCES = \ + fpm.h \ + fpm_pb.h \ + fpm_pb.c \ + $(protobuf_srcs) + +nodist_libfpm_pb_la_SOURCES = $(protobuf_srcs_nodist) + +CLEANFILES = $(Q_CLEANFILES) + +BUILT_SOURCES = $(Q_PROTOBUF_SRCS) +EXTRA_DIST = fpm.proto diff --git a/fpm/fpm.h b/fpm/fpm.h new file mode 100644 index 000000000..85285996c --- /dev/null +++ b/fpm/fpm.h @@ -0,0 +1,309 @@ +/* + * 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. + */ + +/* + * Local host as a default server for fpm connection + */ +#define FPM_DEFAULT_IP (htonl (INADDR_LOOPBACK)) + +/* + * default port for fpm connections + */ +#define FPM_DEFAULT_PORT 2620 + +/* + * Largest message that can be sent to or received from the FPM. + */ +#define FPM_MAX_MSG_LEN 4096 + +#ifdef __SUNPRO_C +#pragma pack(1) +#endif + +/* + * 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. + */ + uint16_t msg_len; +} __attribute__ ((packed)) fpm_msg_hdr_t; + +#ifdef __SUNPRO_C +#pragma pack() +#endif + +/* + * 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. + * + * XXX Netlink cares about the alignment of messages. When any + * FPM_MSG_TYPE_NETLINK messages are sent over a channel, then all + * messages should be sized such that netlink alignment is + * maintained. + */ + FPM_MSG_TYPE_NETLINK = 1, + FPM_MSG_TYPE_PROTOBUF = 2, +} 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. + * + * **NB**: Alignment is required only when netlink messages are used. + */ +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 (sizeof (fpm_msg_hdr_t)) + +#ifndef COMPILE_ASSERT +#define COMPILE_ASSERT(x) extern int __dummy[2 * !!(x) - 1] +#endif + +COMPILE_ASSERT(FPM_MSG_ALIGNTO == FPM_MSG_HDR_LEN); + +/* + * 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 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; + + /* + * Netlink messages must be aligned properly. + */ + if (hdr->msg_type == FPM_MSG_TYPE_NETLINK && + 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; +} + +// tcp maximum range +#define TCP_MAX_PORT 65535 + +// tcp minimum range +#define TCP_MIN_PORT 1 + +#endif /* _FPM_H */ diff --git a/fpm/fpm.proto b/fpm/fpm.proto new file mode 100644 index 000000000..318e80a92 --- /dev/null +++ b/fpm/fpm.proto @@ -0,0 +1,88 @@ +// +// fpm.proto +// +// @copyright Copyright (C) 2016 Sproute Networks, Inc. +// +// @author Avneesh Sachdev +// +// 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. +// + +// +// Protobuf definitions pertaining to the Forwarding Plane Manager component. +// + +package fpm; + +import "qpb/qpb.proto"; + +// +// A Nexthop for a route. It indicates how packets to a given prefix +// should be forwarded (for instance, send them out of a specified +// interface to a specified address). +// +message Nexthop { + optional qpb.IfIdentifier if_id = 2; + optional qpb.L3Address address = 3; +} + +message RouteKey { + optional qpb.L3Prefix prefix = 1; +} + +message DeleteRoute { + required uint32 vrf_id = 1; + required qpb.AddressFamily address_family = 2; + required qpb.SubAddressFamily sub_address_family = 3; + required RouteKey key = 4; +} + +enum RouteType { + UNKNOWN = 0; + NORMAL = 1; + UNREACHABLE = 2; + BLACKHOLE = 3; +} + +message AddRoute { + required uint32 vrf_id = 1; + required qpb.AddressFamily address_family = 2; + required qpb.SubAddressFamily sub_address_family = 3; + required RouteKey key = 4; + + optional RouteType route_type = 5; + + required qpb.Protocol protocol = 6; + + required int32 metric = 8; + + repeated Nexthop nexthops = 9; +} + +// +// Any message from the FPM. +// +message Message { + enum Type { + UNKNOWN_MSG = 0; + ADD_ROUTE = 1; + DELETE_ROUTE = 2; + }; + + optional Type type = 1; + + optional AddRoute add_route = 2; + optional DeleteRoute delete_route = 3; +} diff --git a/fpm/fpm_pb.c b/fpm/fpm_pb.c new file mode 100644 index 000000000..ba18627ea --- /dev/null +++ b/fpm/fpm_pb.c @@ -0,0 +1,28 @@ +/* + * fpm_pb.c + * + * @copyright Copyright (C) 2016 Sproute Networks, Inc. + * + * @author Avneesh Sachdev + * + * 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. + */ + +/* + * Main file for the fpm_pb library. + */ diff --git a/fpm/fpm_pb.h b/fpm/fpm_pb.h new file mode 100644 index 000000000..8f74ac06e --- /dev/null +++ b/fpm/fpm_pb.h @@ -0,0 +1,63 @@ +/* + * fpm_pb.h + * + * @copyright Copyright (C) 2016 Sproute Networks, Inc. + * + * @author Avneesh Sachdev + * + * 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. + */ + +/* + * Public header file for fpm protobuf definitions. + */ + +#ifndef _FPM_PB_H +#define _FPM_PB_H + +#include "route_types.h" +#include "qpb/qpb.h" + +#include "fpm/fpm.pb-c.h" + +/* + * fpm__route_key__create + */ +#define fpm_route_key_create fpm__route_key__create +static inline Fpm__RouteKey * +fpm__route_key__create (qpb_allocator_t *allocator, struct prefix *prefix) +{ + Fpm__RouteKey *key; + + key = QPB_ALLOC (allocator, typeof (*key)); + if (!key) + { + return NULL; + } + fpm__route_key__init (key); + + key->prefix = qpb__l3_prefix__create (allocator, prefix); + if (!key->prefix) + { + return NULL; + } + + return key; +} + +#endif diff --git a/gdb/lib.txt b/gdb/lib.txt new file mode 100644 index 000000000..b703808b4 --- /dev/null +++ b/gdb/lib.txt @@ -0,0 +1,295 @@ +# GDB macros for use with Quagga. +# +# Macros in this file are not daemon specific. E.g., OS or Quagga library +# APIs. +# +# The macro file can be loaded with 'source '. They can then be +# called by the user. Macros that explore more complicated structs generally +# take pointer arguments. +# +# E.g.: +# +# (gdb) source ~paul/code/quagga/gdb/lib.txt +# (gdb) break bgp_packet.c:613 +# Breakpoint 3 at 0x7fa883033a32: file bgp_packet.c, line 613. +# (gdb) cont +# ... +# (gdb) cont +# Breakpoint 3, bgp_write_packet (peer=0x7fa885199080) at bgp_packet.c:614 +# 614 if (CHECK_FLAG (adv->binfo->peer->cap,PEER_CAP_RESTART_RCV) +# (gdb) dump_prefix4 &adv->rn->p +# IPv4:10.1.1.0/24 +# (gdb) dump_prefix &adv->rn->p +# IPv4:10.1.1.0/24 +# + + +define def_ntohs + set $data = (char *)$arg0 + set $i = 0 + + set $_ = $data[$i++] << 8 + set $_ += $data[$i++] +end +document def_ntohs +Read a 2-byte short at the given pointed to area as big-endian and +return it in $_ + +Argument: Pointer to a 2-byte, big-endian short word. +Returns: Integer value of that word in $_ +end + +define def_ntohl + set $data = (char *)$arg0 + set $i = 0 + + set $_ = $data[$i++] << 24 + set $_ += $data[$i++] << 16 + set $_ += $data[$i++] << 8 + set $_ += $data[$i++] +end +document def_ntohl +Read a 4-byte integer at the given pointed to area as big-endian and +return it in $_ + +Argument: Pointer to a big-endian 4-byte word. +Returns: Integer value of that word in $_ +end + +# NB: This is in more complicated iterative form, rather than more +# conventional and simpler recursive form, because GDB has a recursion limit +# on macro calls (I think). +define walk_route_table_next + # callee saves + set $_top = $top + set $_node = $node + set $_prevl = $prevl + + set $top = (struct route_node *)$arg0 + set $node = (struct route_node *)$arg1 + set $prevl = $node + + # first try left + #echo try left\n + set $node = $prevl->link[0] + + # otherwise try right + if ($node == 0) + #echo left null, try right\n + set $node = $prevl->link[1] + end + + # otherwise go up, till we find the first right that + # we havn't been to yet + if ($node == 0) + set $node = $prevl + while ($node != $top) + #echo right null, try up and right\n + + set $prevl = $node + set $parent = $node->parent + set $node = $parent->link[1] + + if ($node != 0 && $node != $prevl) + #echo found node \n + loop_break + end + + #echo go up\n + set $node = $parent + end + end + + #printf "next node: 0x%x\n", $node + + set $_ = $node + + set $top = $_top + set $node = $_node + set $prevl = $_prevl +end +document walk_route_table_next +Return the next node to visit in the given route_table (or subset of) and +the given current node. + +Arguments: +1st: (struct route_node *) to the top of the route_table to walk +2nd: (struct route_node *) to the current node + +Returns: The (struct route_node *) for the next to visit in $_ +end + +define walk_route_table + set $_visited = $visited + set $_node = $node + set $top = $_top + + set $node = (struct route_node *)$arg0 + set $top = (struct route_node *)$arg0 + set $visited = 0 + + while ($node != 0) + printf "Node: 0x%x", $node + + if ($node->info != 0) + printf "\tinfo: 0x%x", $node->info + set $visited = $visited + 1 + end + + printf "\n" + + walk_route_table_next $top $node + set $node = $_ + + # we've gotten back to the top, finish + if ($node == $top) + set $node = 0 + end + end + printf "Visited: %u\n", $visited + + set $top = $_top + set $visited = $_visited + set $node = $_node +end + +document walk_route_table +Walk through a routing table (or subset thereof) and dump all the non-null +(struct route_node *)->info pointers. + +Argument: A lib/thread.h::(struct route_node *) pointing to the route_node +under which all data should be dumped +end + +define dump_timeval + set $tv = (struct timeval *)$arg0 + set $day = 3600*24 + + if $tv->tv_sec > $day + printf "%d days, ", $tv->tv_sec / $day + end + if $tv->tv_sec > 3600 + printf "%dh", $tv->tv_sec / 3600 + end + if ($tv->tv_sec % 3600) > 60 + printf "%dm", ($tv->tv_sec % 3600) / 60 + end + printf "%d", $tv->tv_sec % 3600 % 60 + if $tv->tv_usec != 0 + printf ".%06d", $tv->tv_usec + end + printf "s" +end +document dump_timeval +Human readable dump of a (struct timeval *) argument +end + +define dump_s_addr + set $addr = (char *)$arg0 + + printf "%d.%d.%d.%d", $addr[0], $addr[1], $addr[2], $addr[3] +end + +define dump_s6_addr + set $a6 = (char *)$arg0 + set $field = 0 + + while ($field < 16) + set $i1 = $field++ + set $i2 = $field++ + + printf "%x%x", $a6[$i1], $a6[$i2] + + if ($field > 2 && ($field % 4 == 0)) + printf ":" + end + end +end +document dump_s6_addr +Interpret the memory starting at given address as an IPv6 s6_addr and +print in human readable form. +end + +define dump_prefix4 + set $p = (struct prefix *) $arg0 + echo IPv4: + dump_s_addr &($p->u.prefix4) + printf "/%d\n", $p->prefixlen +end +document dump_prefix4 +Textual dump of a (struct prefix4 *) argument. +end + +define dump_prefix6 + set $p = (struct prefix *) $arg0 + echo IPv6: + dump_s6_addr &($p->u.prefix6) + printf "/%d\n", $p->prefixlen +end +document dump_prefix6 +Textual dump of a (struct prefix6 *) argument. +end + +define dump_prefix + set $p = $arg0 + + if ($p->family == 2) + dump_prefix4 $p + end + if ($p->family == 10) + dump_prefix6 $p + end +end +document dump_prefix +Human readable dump of a (struct prefix *) argument. +end + +define rn_next_down + set $node = $arg0 + while ($node != 0) + print/x $node + if ($node->link[0] != 0) + set $node = $node->link[0] + else + set $node = $node->link[1] + end + end +end + +document rn_next_down +Walk left-down a given route table, dumping locations of route_nodes + +Argument: A single (struct route_node *). +end + +define rn_next_up + set $top = (struct route_node *)$arg0 + set $node = (struct route_node *)$arg1 + + while ($node != $top) + echo walk up\n + + set $prevl = $node + set $parent = $node->parent + set $node = $parent->link[1] + + if ($node != 0 && $node != $prevl) + echo found a node\n + loop_break + end + + echo going up\n + set $node = $parent + end + output/x $node + echo \n +end + +document rn_next_up +Walk up-and-right from the given route_node to the next valid route_node +which is not the given "top" route_node + +Arguments: +1st: A (struct route_node *) to the top of the route table. +2nd: The (struct route_node *) to walk up from +end diff --git a/gdb/ospf.txt b/gdb/ospf.txt new file mode 100644 index 000000000..984104b85 --- /dev/null +++ b/gdb/ospf.txt @@ -0,0 +1,137 @@ +# GDB macros for use with Quagga. +# +# Macros in this file are specific to ospfd/. Definitions here depend on the +# lib.txt macros file, which must also be loaed. +# +# The macro file can be loaded with 'source '. They can then be +# called by the user. Macros that explore more complicated structs generally +# take pointer arguments. + +define dump_ospf_lsa_flags + set $flags = $arg0 + + printf "%u: ", $flags + + if $flags & 0x1 + echo Self, + end + if $flags & 0x2 + echo Self-checked, + end + if $flags & 0x4 + echo Recvd, + end + if $flags & 0x8 + echo Apprvd, + end + if $flags & 0x10 + echo Discard, + end + if $flags & 0x20 + echo Local-Xlt, + end + if $flags & 0x40 + echo Premature-Aged, + end + if $flags & 0x40 + echo In-Maxage, + end + echo \n +end + +define dump_ospf_lsa_data + set $lsad = (struct lsa_header *)$arg0 + + echo ID / AdvRtr: \t\t + dump_s_addr &$lsad->id.s_addr + echo \ : \ + dump_s_addr &$lsad->adv_router.s_addr + echo \n + + def_ntohs &$lsad->ls_age + printf "Type: %2u Age: %4u,", $lsad->type, $_ + + def_ntohs &$lsad->length + printf " length: %2u", $_ + + def_ntohl &$lsad->ls_seqnum + printf " Seqnum: 0x%08x", $_ + + def_ntohs &$lsad->checksum + printf " csum: 0x%04x\n", $_ + + # return the age + def_ntohs &$lsad->ls_age +end + +define dump_ospf_lsa + set $lsa = (struct ospf_lsa *)$arg0 + + #print/x *$lsa + + dump_ospf_lsa_data $lsa->data + + set $relage = $_ + (relative_time.tv_sec - $lsa->tv_recv.tv_sec) + printf "Relative age: %4u\n", $relage + + dump_ospf_lsa_flags $lsa->flags + + echo tv_recv: \ + dump_timeval &$lsa->tv_recv + echo \ tv_orig: \ + dump_timeval &$lsa->tv_orig + echo \n + + printf "lock %2u", $lsa->lock + printf " stat %2d", $lsa->stat + printf " rtx count: %u", $lsa->retransmit_counter + printf " rfsh list: %d", $lsa->refresh_list + printf "\n\n" +end + +define walk_ospf_lsdb + set $node = (struct route_node *)$arg0 + set $top = (struct route_node *)$arg0 + set $visited = 0 + + while ($node != 0) + set $prevl = $node + + if ($node->info != 0) + dump_ospf_lsa $node->info + set $visited = $visited + 1 + end + + walk_route_table_next $top $node + set $node = $_ + + # we've gotten back to the top, finish + if ($node == $top) + set $node = 0 + end + end + printf "Visited: %u\n", $visited +end + +document walk_ospf_lsdb +Walk through an OSPF LSDB (or subset thereof) and dump all the LSAs +contained there-in. + +Argument: A (struct route_node *) pointing to the top of the +LSDB route-table which should be dumped. +end + +define ospf_backbone_lsdb_top + set $type = $arg0 + + set $ospf = ospf_master->ospf->head->data + + output/x ((struct ospf *)$ospf)->backbone->lsdb->type[$type]->db->top + echo \n +end +document ospf_backbone_lsdb_top +Dump location of the LSDB in the backbone area for the given LSA type + +Argument: Integer LSA type +end + diff --git a/guile/Makefile.am b/guile/Makefile.am deleted file mode 100644 index 5beb71c41..000000000 --- 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 -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 2773029b9..000000000 --- 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 8e18fae56..000000000 --- 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 fbd01ba0e..000000000 --- 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 f618dbc55..000000000 --- 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 f43e287d8..000000000 --- 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 9a6ef814a..000000000 --- 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/infra/buildbot/master/master.cfg b/infra/buildbot/master/master.cfg new file mode 100644 index 000000000..00e56dd90 --- /dev/null +++ b/infra/buildbot/master/master.cfg @@ -0,0 +1,619 @@ +# -*- python -*- +# ex: set syntax=python: + +from buildbot.plugins import * +from buildbot.plugins import buildslave, util + +# This is a sample buildmaster config file. It must be installed as +# 'master.cfg' in your buildmaster's base directory. + +# This is the dictionary that the buildmaster pays attention to. We also use +# a shorter alias to save typing. +c = BuildmasterConfig = {} + +quaggagit = 'git://git.sv.gnu.org/quagga.git' + +# password defs +execfile("pass.cfg") + +# filter a given 'workers' entry into a property list +# suitable for public display +def workers2publicprops (worker): + publicprops = [ "os", "version", "vm", "pkg", "texi", "cc", + "latent", ] + return { k:worker[k] for k in worker if k in publicprops } + +# vm: non-VM are assumed faster and used for initial build +# pkg: rpm, sysv, dpkg - only do test rpm builds at moment +# texi: True or "true" if we can use for doc building +# cc: List of tuples of installed compilers, with: +# (, , ) +# tag: gcc, clang, sunpro +# latent: VM spun up on demand via LatentSlave, uses "session" for +# the libvirt URI. +# session: libvirt URI to use for latent workers. Default will be set on +# latent VMs if not specified. +# hd_image: libvirt image to use +workers = { + "fedora-24": { + "os": "Fedora", + "version": "24", + "vm": False, + "pkg": "rpm", + "texi": True, + "cc": [ ("gcc", "6.3.1"), + ("clang", "3.8.1"), + ("gcc", "3.4.6", "gcc34"), + ], + }, + "fedora-26": { + "os": "Fedora", + "version": "26", + "vm": False, + "pkg": "rpm", + "cc": [ ("gcc", "7.0.1"), + ("clang", "3.9.0"), + ("gcc", "3.4.6", "gcc34"), + ], + }, + "centos-7": { + "os": "CentOS", + "version": "7", + "vm": False, + "pkg": "rpm", + "cc": [ ("gcc", "4.8.5") ], + }, + "debian-8": { + "os": "Debian", + "version": "8", + "vm": True, + "pkg": "dpkg", + "latent": True, + "cc": [ ("gcc", "4.9.2") ], + "hd_image": "/var/lib/libvirt/images/debian8.qcow2", + }, + "debian-9": { + "os": "Debian", + "version": "9", + "vm": True, + "pkg": "dpkg", + "cc": [ ("gcc", "6.3.0") ], + "latent": True, + "hd_image": "/var/lib/libvirt/images/debian9.qcow2", + }, + "freebsd-10": { + "os": "FreeBSD", + "version": "10", + "vm": True, + "pkg": "", + "latent": True, + "cc": [ ("clang", "3.4.1") ], + "hd_image": "/var/lib/libvirt/images/freebsd103.qcow2", + }, + "freebsd-11": { + "os": "FreeBSD", + "version": "11", + "vm": True, + "pkg": "", + "cc": [ ("gcc", "4.9.4"), ("clang", "3.8.0"), ], + "latent": True, + "hd_image": "/var/lib/libvirt/images/freebsd110.qcow2", + }, + "oi-hipster": { + "os": "OpenIndiana", + "version": "hipster", + "vm": True, + "pkg": "sysv", + "latent": True, + "cc": [ ("gcc", "6.3.0"), ("sunpro", "12.0"), + ("gcc", "4.4.4") + ], + "hd_image": "/var/lib/libvirt/images/buildbot-oi-hipster.qcow2", + }, +} + +# ensure "latent" is set to false, where not set. +# add in the passwords +for kw in workers: + w = workers[kw] + w["bot"] = "buildbot-" + kw + if "latent" not in w: + w["latent"] = False + w["pass"] = workers_pass[kw] + +analyses_builders = [ "clang-analyzer" ] + +# default Libvirt session +for w in (w for w in workers.values () if ("latent" in w and w["latent"]) + and ("session" not in w)): + w["session"] = 'qemu+ssh://buildbot@sagan.jakma.org/system' + +osbuilders = ["build-" + kw for kw in workers] +osfastbuilders = ["build-" + kw for kw in workers if workers[kw]["vm"] == False] +osslowbuilders = ["build-" + kw for kw in workers if workers[kw]["vm"] == True] + +rpmbuilders = ["rpm-" + kw for kw in workers if workers[kw]["pkg"] == "rpm"] + +# compilers +# not using yet +# [kw for kw in workers if len([v for (c,v) in workers[kw]["cc"] if c == "gcc"]) > 0 ] + +allbuilders = [] +allbuilders += osbuilders +allbuilders += rpmbuilders +allbuilders += analyses_builders +allbuilders += ["commit-builder"] +allbuilders += ["build-distcheck"] +allbuilders += ["build-docs" ] + +# Force merging of requests. +# c['mergeRequests'] = lambda *args, **kwargs: True + +####### BUILDSLAVES +c['slaves'] = [] + +# The 'slaves' list defines the set of recognized buildslaves. Each element is +# a BuildSlave object, specifying a unique slave name and password. The same +# slave name and password must be configured on the slave. + +for w in (w for w in workers.values() if ("latent" not in w) + or (w["latent"] == False)): + c['slaves'].append(buildslave.BuildSlave(w["bot"], w["pass"], + properties=workers2publicprops (w), + )) + +for w in (w for w in workers.values() + if ("latent" in w) + and w["latent"] + and "hd_image" in w): + c['slaves'].append(buildslave.LibVirtSlave( + w["bot"], + w["pass"], + util.Connection(w["session"]), + w["hd_image"], + properties=workers2publicprops (w), + )) + +# 'protocols' contains information about protocols which master will use for +# communicating with slaves. +# You must define at least 'port' option that slaves could connect to your master +# with this protocol. +# 'port' must match the value configured into the buildslaves (with their +# --master option) +c['protocols'] = {'pb': {'port': 9989}} + +####### CHANGESOURCES + +# the 'change_source' setting tells the buildmaster how it should find out +# about source code changes. Here we point to the buildbot clone of pyflakes. + +c['change_source'] = [] +c['change_source'].append(changes.GitPoller( + quaggagit, + workdir='gitpoller-workdir', + branches=['master','volatile/next'], + pollinterval=300)) + +####### REVISION LINKS +# associate changesouce repositories to URI templates for code view +# +c['revlink'] = util.RevlinkMatch([quaggagit + r"(.*)"], + r"http://git.savannah.gnu.org/cgit/quagga.git/commit/?id=%s") + +####### SCHEDULERS + +# Configure the Schedulers, which decide how to react to incoming changes. + +# We want a first line of 'quick' builds, which then trigger further builds. +# +# A control-flow builder, "commit-builder", used to sequence the 'real' +# sets of builders, via Triggers. + +c['schedulers'] = [] +c['schedulers'].append(schedulers.SingleBranchScheduler( + name="master-change", + change_filter=util.ChangeFilter(branch='master'), + treeStableTimer=10, + builderNames=[ "commit-builder" ])) + +c['schedulers'].append(schedulers.SingleBranchScheduler( + name="next-change", + change_filter=util.ChangeFilter( + branch='volatile/next'), + treeStableTimer=10, + builderNames=[ "commit-builder" ] )) + +# Initial build checks on faster, non-VM +c['schedulers'].append(schedulers.Triggerable( + name="trigger-build-first", + builderNames=osfastbuilders)) + +# Build using remaining builders, after firstbuilders. +c['schedulers'].append(schedulers.Triggerable( + name="trigger-build-rest", + builderNames=osslowbuilders)) + +# Analyses tools, e.g. CLang Analyzer scan-build +c['schedulers'].append(schedulers.Triggerable( + name="trigger-build-analyses", + builderNames=analyses_builders)) +# Dist check +c['schedulers'].append(schedulers.Triggerable( + name="trigger-distcheck", + builderNames=["build-distcheck"])) +# RPM check and build +c['schedulers'].append(schedulers.Triggerable( + name="trigger-rpm", + builderNames=rpmbuilders)) + +# Doc build check (non-nightly, so no upload) +c['schedulers'].append(schedulers.Triggerable( + name="trigger-build-docs", + builderNames=["build-docs"])) + +# Try and force schedulers +c['schedulers'].append(schedulers.ForceScheduler( + name="force", + builderNames=allbuilders)) + +c['schedulers'].append(schedulers.Try_Userpass( + name="try", + builderNames=osbuilders + + rpmbuilders + + ["build-distcheck", + "clang-analyzer", + "build-docs" ], + userpass=users, + port=8031)) + +## nightly docs build +c['schedulers'].append(schedulers.Nightly( + name="nightly-docs", + branch="master", + builderNames=[ "build-docs" ], + hour=3, + minute=0, + onlyIfChanged=True, + properties = { "nightly": True }, +)) + + +####### BUILDERS +c['builders'] = [] + +# The 'builders' list defines the Builders, which tell Buildbot how to perform a build: +# what steps, and which slaves can execute them. Note that any particular build will +# only take place on one slave. + +common_setup = [ + steps.Git(repourl=quaggagit, mode='incremental'), + steps.ShellCommand(command=["./update-autotools"], + description="generating autoconf", + descriptionDone="autoconf"), + steps.Configure(command="../build/configure"), + steps.ShellCommand(command=["make", "clean"], + description="cleaning", + descriptionDone="make clean"), +] + +### Default 'check' build, builder instantiated for each OS + +factory = util.BuildFactory() +# check out the source +factory.addStep(steps.Git(repourl=quaggagit, mode='incremental')) +factory.addStep(steps.ShellCommand(command=["./update-autotools"], + description="generating autoconf", + descriptionDone="autoconf")) +factory.addStep(steps.Configure()) +factory.addStep(steps.ShellCommand(command=["make", "clean"], + description="cleaning", + descriptionDone="clean")) + +#factory.addSteps(common_setup) +factory.addStep(steps.Compile(command=["make", "-j", "2", "all"])) +factory.addStep(steps.ShellCommand(command=["make", "check"], + description="checking", + descriptionDone="make check")) + +# create builder for every OS, for every buildbot +# XXX: at moment this assumes 1:1 OS<->bot +for kw in workers: + c['builders'].append(util.BuilderConfig( + name="build-" + kw, + slavenames=workers[kw]["bot"], + factory=factory)) + +### distcheck Builder, executed on any available bot +factory = util.BuildFactory() +# check out the source +factory.addStep(steps.Git(repourl=quaggagit, mode='incremental')) +factory.addStep(steps.ShellCommand(command=["./update-autotools"], + description="generating autoconf", + descriptionDone="autoconf")) +factory.addStep(steps.Configure()) +factory.addStep(steps.ShellCommand(command=["make", "clean"], + description="cleaning", + descriptionDone="make clean")) +factory.addStep(steps.ShellCommand(command=["make", "distcheck"], + description="run make distcheck", + descriptionDone="make distcheck")) +c['builders'].append( + util.BuilderConfig(name="build-distcheck", + slavenames=list(w["bot"] for w in workers.values()), + factory=factory, +)) + +### LLVM clang-analyzer build, executed on any available non-VM bot + +f = util.BuildFactory() +# check out the source +f.addStep(steps.Git(repourl=quaggagit, mode='incremental', + getDescription=True)) +f.addStep(steps.ShellCommand(command=["./update-autotools"], + description="run autotools", + descriptionDone="autoconf")) +f.addStep(steps.Configure()) +f.addStep(steps.ShellCommand(command=["make", "clean"], + description="cleaning", + descriptionDone="make clean")) + +f.addStep(steps.SetProperty(property="clang-id", + value=util.Interpolate("%(prop:commit-description)s-%(prop:buildnumber)s"))) + +f.addStep(steps.SetProperty(property="clang-output-dir", + value=util.Interpolate("../CLANG-%(prop:clang-id)s"))) +f.addStep(steps.SetProperty(property="clang-uri", + value=util.Interpolate("/clang-analyzer/%(prop:clang-id)s"))) +# relative to buildbot master working directory +f.addStep(steps.SetProperty(property="clang-upload-dir", + value=util.Interpolate("public_html/clang-analyzer/%(prop:clang-id)s"))) + +f.addStep(steps.Compile(command=["scan-build", + "-analyze-headers", + "-o", + util.Interpolate("%(prop:clang-output-dir)s"), + "make", "-j", "all"])) +f.addStep(steps.DirectoryUpload( + slavesrc=util.Interpolate("%(prop:clang-output-dir)s"), + masterdest = util.Interpolate("%(prop:clang-upload-dir)s"), + compress = 'bz2', + name = "clang report", + url = util.Interpolate("%(prop:clang-uri)s"), +)) +f.addStep(steps.RemoveDirectory( + dir=util.Interpolate("%(prop:clang-output-dir)s") +)) + + +c['builders'].append( + util.BuilderConfig(name="clang-analyzer", + slavenames=list(w["bot"] for w in workers.values() if not w["vm"]), + factory=f)) + + +### RPM: check and build +f = util.BuildFactory () + +# check out the source +f.addStep(steps.Git(repourl=quaggagit, mode='full')) +f.addStep(steps.ShellCommand(command=["./update-autotools"], + description="run autotools", + descriptionDone="autotools")) +f.addStep(steps.Configure()) +f.addStep(steps.ShellCommand(command=["make", "dist"], + description="run make dist", + descriptionDone="make dist")) +# not imported somehow +#f.addStep(steps.RpmLint(fileloc="redhat/quagga.spec")) +f.addStep(steps.ShellCommand(command=["rpmlint", "-i", "redhat/quagga.spec"], + description="run rpmlint", + descriptionDone="rpmlint")) +f.addStep(steps.RpmBuild(specfile="redhat/quagga.spec")) +# rpmdir=util.Interpolate("%(prop:builddir)s/rpm"))) + +# XXX: assuming 1:1 OS:buildbot mapping +for kw in (kw for kw in workers if workers[kw]["pkg"] == "rpm"): + c['builders'].append( + util.BuilderConfig(name="rpm-" + kw, + slavenames="buildbot-" + kw, + factory=f + ) + ) + +### Build documentation + +def build_is_nightly (step): + n = step.getProperty("nightly") + if n == True or n == "True" or n == "true": + return True + return False + +f = util.BuildFactory () +f.addStep(steps.Git(repourl=quaggagit, mode='full')) +f.addStep(steps.ShellCommand(command=["./update-autotools"], + description="run autotools", + descriptionDone="autotools")) +f.addStep(steps.Configure(command=["../build/configure"], + workdir="docs")) +f.addStep(steps.ShellCommand(command=["make", "V=99", "quagga.html"], + description="making split HTML doc", + descriptionDone="docs: split HTML", + workdir="docs/doc", + haltOnFailure=True, +)) +#f.addStep(steps.FileUpload( +# slavesrc="build/doc/fig-normal-processing.png", +# masterdest = "public_html/docs/nightly/quagga/", +# name = "Upload Fig 1", +# doStepIf=build_is_nightly, +#)) +#f.addStep(steps.FileUpload( +# slavesrc="build/doc/fig-rs-processing.png", +# masterdest = "public_html/docs/nightly/quagga/", +# name = "Upload Fig 2", +# doStepIf=build_is_nightly, +#)) +f.addStep(steps.MultipleFileUpload( + slavesrcs=[ "doc/fig-rs-processing.png", + "doc/fig-normal-processing.png" ], + masterdest = "public_html/docs/nightly/quagga/", + name = "Upload Figures", + doStepIf=build_is_nightly, +)) +f.addStep(steps.DirectoryUpload( + slavesrc="quagga.html", + masterdest = "public_html/docs/nightly/quagga", + compress = 'bz2', + name = "Upload split HTML", + url = "/docs/nightly/quagga/index.html", + workdir="docs/doc", + doStepIf=build_is_nightly, +)) +f.addStep(steps.RemoveDirectory( + dir="docs/doc/quagga.html", +)) +f.addStep(steps.ShellCommand(command=["make", "V=99", + "MAKEINFOFLAGS=--no-split", + "quagga.html"], + description="making one-page HTML doc", + descriptionDone="docs: one-page HTML", + workdir="docs/doc", + haltOnFailure=True +)) +f.addStep(steps.FileUpload( + slavesrc="quagga.html", + masterdest = "public_html/docs/nightly/quagga/quagga.html", + name = "Upload single HTML", + url = "/docs/nightly/quagga/quagga.html", + workdir="docs/doc", + doStepIf=build_is_nightly, +)) +f.addStep(steps.ShellCommand(command=["make", "V=99", "quagga.pdf"], + description="making PDF docs", + descriptionDone="docs: PDF", + workdir="docs/doc" +)) +f.addStep(steps.FileUpload( + slavesrc="quagga.pdf", + masterdest = "public_html/docs/nightly/quagga/quagga.pdf", + name = "Upload PDF", + url = "/docs/nightly/quagga/quagga.pdf", + workdir="docs/doc", + doStepIf=build_is_nightly, +)) + +c['builders'].append( + util.BuilderConfig(name="build-docs", + slavenames=[w["bot"] for w in workers.values() + if "texi" in w and w["texi"] == True ], + factory=f +)) + +### Co-ordination builds used to sequence parallel builds via Triggerable + +# to understand this you have to read this list and the Triggered schedulers +# to see what sets of builds are being sequenced. Bit clunky, but Buildbot +# doesn't have a way to just specify a pipeline of groups of builders more +# cleanly. + +f = util.BuildFactory() +f.addStep(steps.Trigger ( + schedulerNames = [ "trigger-build-first" ], + waitForFinish=True, + updateSourceStamp=True +)) +f.addStep(steps.Trigger ( + schedulerNames = [ "trigger-build-rest" ], + waitForFinish=True, + updateSourceStamp=True +)) +f.addStep(steps.Trigger ( + schedulerNames = [ "trigger-build-analyses", "trigger-distcheck", + "trigger-build-docs" ], + waitForFinish=True, + updateSourceStamp=True +)) +f.addStep(steps.Trigger ( + schedulerNames = [ "trigger-rpm" ], + waitForFinish=True, + updateSourceStamp=True +)) + +c['builders'].append( + util.BuilderConfig(name="commit-builder", + slavenames=[w["bot"] for w in workers.values() if not w["vm"]], + factory=f) +) + +####### STATUS TARGETS + +# 'status' is a list of Status Targets. The results of each build will be +# pushed to these targets. buildbot/status/*.py has a variety to choose from, +# including web pages, email senders, and IRC bots. + +c['status'] = [] + +from buildbot.status import html +from buildbot.status.web import authz, auth + +authz_cfg=authz.Authz( + # change any of these to True to enable; see the manual for more + # options + #auth=auth.BasicAuth([("pyflakes","pyflakes")]), + auth=util.BasicAuth(users), + gracefulShutdown = False, + forceBuild = 'auth', # use this to test your slave once it is set up + forceAllBuilds = 'auth', # ..or this + pingBuilder = 'auth', + stopBuild = 'auth', + stopAllBuilds = 'auth', + cancelPendingBuild = 'auth', + cancelAllPendingBuilds = 'auth', + pauseSlave = 'auth', +) +c['status'].append(html.WebStatus(http_port=8010, authz=authz_cfg)) + +c['status'].append(status.MailNotifier( + fromaddr="buildbot@quagga.net", + extraRecipients=["paul@jakma.org"], + sendToInterestedUsers=False, +)) + +c['status'].append (status.IRC( + "irc.freenode.net", "bb-quagga", + useColors=True, + channels=[{"channel": "#quagga"}], + notify_events={ + 'exception': 1, + 'successToFailure': 1, + 'failureToSuccess': 1, + }, +)) + +####### PROJECT IDENTITY + +# the 'title' string will appear at the top of this buildbot +# installation's html.WebStatus home page (linked to the +# 'titleURL') and is embedded in the title of the waterfall HTML page. + +c['title'] = "Quagga" +c['titleURL'] = "https://www.quagga.net/" + +# the 'buildbotURL' string should point to the location where the buildbot's +# internal web server (usually the html.WebStatus page) is visible. This +# typically uses the port number set in the Waterfall 'status' entry, but +# with an externally-visible host name which the buildbot cannot figure out +# without some help. + +c['buildbotURL'] = "http://buildbot.quagga.net/" + +####### DB URL + +c['db'] = { + # This specifies what database buildbot uses to store its state. You can leave + # this at its default for all but the largest installations. + 'db_url' : "sqlite:///state.sqlite", +} + +#### debug +c['debugPassword'] = debugPassword diff --git a/infra/buildbot/master/pass.cfg b/infra/buildbot/master/pass.cfg new file mode 100644 index 000000000..34a9340bd --- /dev/null +++ b/infra/buildbot/master/pass.cfg @@ -0,0 +1,21 @@ +# -*- python -*- +# ex: set syntax=python: + +# example pass.cfg + +# privileged users for webui and try building +#users = [ +# ('foo', 'password123'), +#] + +workers_pass = { + "fedora-24": "aaaaaaa", + "centos-7": "bbbbbbb", + "debian-8": "ccccccc", + "debian-9": "ddddddd", + "freebsd-10": "eeeeeee", + "freebsd-11": "fffffff", +} + +#### debug +#debugPassword = "abcdefghijklmnopqrstuvwxyz" diff --git a/infra/buildbot/worker/buildbot-slave.service b/infra/buildbot/worker/buildbot-slave.service new file mode 100644 index 000000000..dcb136fa5 --- /dev/null +++ b/infra/buildbot/worker/buildbot-slave.service @@ -0,0 +1,13 @@ +[Unit] +Description=Buildbot slave Daemon + +[Service] +WorkingDirectory=/home/buildbot +User=buildbot +Group=buildbot +ExecStart=/usr/bin/buildslave start --nodaemon +ExecStop=/usr/bin/buildslave stop +ExecReload=/usr/bin/buildslave reconfig + +[Install] +WantedBy=multi-user.target diff --git a/infra/buildbot/worker/buildbot-slave.xml b/infra/buildbot/worker/buildbot-slave.xml new file mode 100644 index 000000000..d4177a7a0 --- /dev/null +++ b/infra/buildbot/worker/buildbot-slave.xml @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/infra/patchwork/pass.py b/infra/patchwork/pass.py new file mode 100644 index 000000000..65142328d --- /dev/null +++ b/infra/patchwork/pass.py @@ -0,0 +1,4 @@ +# example pass file +SECRET_KEY = "aaaaaaaaaaaaaaaaaa" +EMAIL_HOST_PASSWORD = os.getenv('EMAIL_HOST_PASSWORD', '') +DATABASES['default']['PASSWORD'] = os.environ.get('DATABASE_PASSWORD', 'bbbbbbbbbbbbbbbbbbbbb'); diff --git a/infra/patchwork/production.py b/infra/patchwork/production.py new file mode 100644 index 000000000..1683c0c54 --- /dev/null +++ b/infra/patchwork/production.py @@ -0,0 +1,89 @@ +""" +Sample production-ready settings for patchwork project. + +Most of these are commented out as they will be installation dependent. + +Design based on: + http://www.revsys.com/blog/2014/nov/21/recommended-django-project-layout/ +""" + +from __future__ import absolute_import + +import os + +import django + +from .base import * # noqa + +DEBUG = True +# +# Core settings +# https://docs.djangoproject.com/en/1.8/ref/settings/#core-settings +# + +# Security +# +# You'll need to replace this to a random string. The following python code can +# be used to generate a secret key: +# +# import string, random +# chars = string.letters + string.digits + string.punctuation +# print repr("".join([random.choice(chars) for i in range(0,50)])) + +# Email +# +# Replace this with your own details + +EMAIL_HOST = os.getenv('EMAIL_HOST', 'localhost') +EMAIL_PORT = os.getenv('EMAIL_PORT', 25) +EMAIL_HOST_USER = os.getenv('EMAIL_HOST_USER', '') +# password goes in pass.py +EMAIL_USE_TLS = True + +DEFAULT_FROM_EMAIL = 'Patchwork ' +SERVER_EMAIL = DEFAULT_FROM_EMAIL +NOTIFICATION_FROM_EMAIL = DEFAULT_FROM_EMAIL + +ADMINS = ( + ('Paul Jakma', 'paul@quagga.net'), +) + +# Database +# +# If you're using a postgres database, connecting over a local unix-domain +# socket, then the following setting should work for you. Otherwise, +# see https://docs.djangoproject.com/en/1.8/ref/settings/#databases + +# password goes in pass.py +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.mysql', + 'NAME': os.environ.get('DATABASE_NAME', 'patchwork'), + 'USER': os.environ.get('DATABASE_USER', 'patchwork'), + 'HOST': '127.0.0.1', + }, +} + + +# +# Static files settings +# https://docs.djangoproject.com/en/1.8/ref/settings/#static-files +# https://docs.djangoproject.com/en/1.8/ref/contrib/staticfiles/#manifeststaticfilesstorage +# + +STATIC_ROOT = os.environ.get('STATIC_ROOT', '/home/patchwork/htdocs/static') + + +if django.VERSION >= (1, 7): + STATICFILES_STORAGE = \ + 'django.contrib.staticfiles.storage.ManifestStaticFilesStorage' + +ENABLE_XMLRPC = True + +LANGUAGE_CODE='en-gb' +TIME_ZONE='GMT' +ALLOWED_HOSTS=['patchwork.quagga.net','testpw.quagga.net', 'http', 'http.quagga.net', '*.quagga.net'] + +with open("patchwork/settings/pass.py") as f: + code = compile(f.read(), "patchwork/settings/pass.py", 'exec') + exec(code) diff --git a/infra/patchwork/systemd/patchwork-delivery.socket b/infra/patchwork/systemd/patchwork-delivery.socket new file mode 100644 index 000000000..bfa675285 --- /dev/null +++ b/infra/patchwork/systemd/patchwork-delivery.socket @@ -0,0 +1,15 @@ +[Unit] +Description=Patchwork unix pipe to accept list mail delivery on + +[Socket] +#ListenStream=/tmp/patchwork.sock +ListenStream=127.0.0.1:8001 +SocketUser=patchwork +SocketGroup=patchwork +Accept=yes + +#ListenFIFO=/tmp/patchwork-fifo.sock + + +[Install] +WantedBy=sockets.target diff --git a/infra/patchwork/systemd/patchwork-delivery@.service b/infra/patchwork/systemd/patchwork-delivery@.service new file mode 100644 index 000000000..d152b2d65 --- /dev/null +++ b/infra/patchwork/systemd/patchwork-delivery@.service @@ -0,0 +1,15 @@ +[Unit] +Description=Patchwork list mail socket processing script + +[Service] +EnvironmentFile=/home/patchwork/patchwork.env +ExecStart=-/home/patchwork/patchwork/patchwork/bin/parsemail.sh +StandardInput=socket +StandardOutput=inherit +StandardError=journal +User=patchwork +Group=patchwork + +[Install] +WantedBy=multi-user.target +Also=patchwork-delivery.socket diff --git a/infra/patchwork/systemd/patchwork.service b/infra/patchwork/systemd/patchwork.service new file mode 100644 index 000000000..90ec4bb22 --- /dev/null +++ b/infra/patchwork/systemd/patchwork.service @@ -0,0 +1,12 @@ +[Unit] +Description=Patchwork Daemon + +[Service] +WorkingDirectory=/home/patchwork/patchwork +User=patchwork +Group=patchwork +EnvironmentFile=/home/patchwork/patchwork.env +ExecStart=/usr/bin/python3 manage.py runserver 0.0.0.0:8000 + +[Install] +WantedBy=multi-user.target diff --git a/isisd/AUTHORS b/isisd/AUTHORS index d9f98b22a..80b3a28e1 100644 --- a/isisd/AUTHORS +++ b/isisd/AUTHORS @@ -1,3 +1,5 @@ -Sampo Saaristo -Ofer Wald -Hannes Gredler +Sampo Saaristo +Ofer Wald +Hannes Gredler +Subbaiah Venkata +Olivier Dugeon diff --git a/isisd/Makefile.am b/isisd/Makefile.am index 26b8ee7c1..bfe2e9477 100644 --- a/isisd/Makefile.am +++ b/isisd/Makefile.am @@ -1,13 +1,12 @@ ## Process this file with automake to produce Makefile.in. -INCLUDES = @INCLUDES@ -I.. -I$(top_srcdir) -I$(top_srcdir)/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 LIBS = @LIBS@ -AM_CFLAGS = $(PICFLAGS) -AM_LDFLAGS = $(PILDFLAGS) +AM_CFLAGS = $(WERROR) noinst_LIBRARIES = libisis.a sbin_PROGRAMS = isisd @@ -17,14 +16,16 @@ libisis_a_SOURCES = \ isis_adjacency.c isis_lsp.c dict.c isis_circuit.c isis_pdu.c \ isis_tlv.c isisd.c isis_misc.c isis_zebra.c isis_dr.c \ isis_flags.c isis_dynhn.c iso_checksum.c isis_csm.c isis_events.c \ - isis_spf.c isis_route.c isis_routemap.c + isis_spf.c isis_redist.c isis_route.c isis_routemap.c isis_te.c \ + isis_vty.c noinst_HEADERS = \ isisd.h isis_pdu.h isis_tlv.h isis_adjacency.h isis_constants.h \ isis_lsp.h dict.h isis_circuit.h isis_misc.h isis_network.h \ isis_zebra.h isis_dr.h isis_flags.h isis_dynhn.h isis_common.h \ - iso_checksum.h isis_csm.h isis_events.h isis_spf.h isis_route.h \ + iso_checksum.h isis_csm.h isis_events.h isis_spf.h isis_redist.h \ + isis_route.h isis_routemap.h isis_te.h \ include-netbsd/clnp.h include-netbsd/esis.h include-netbsd/iso.h isisd_SOURCES = \ diff --git a/isisd/dict.c b/isisd/dict.c index a78b82ac4..bbcb42134 100644 --- a/isisd/dict.c +++ b/isisd/dict.c @@ -15,17 +15,11 @@ * contain a copyright notice related to this source. */ -#include -#include #include "zebra.h" #include "zassert.h" -#define DICT_IMPLEMENTATION +#include "memory.h" #include "dict.h" -#ifdef KAZLIB_RCSID -static const char rcsid[] = "Id: dict.c,v 1.40.2.7 2000/11/13 01:36:44 kaz"; -#endif - /* * These macros provide short convenient names for structure members, * which are embellished with dict_ prefixes so that they are @@ -243,7 +237,7 @@ static int verify_dict_has_node(dnode_t *nil, dnode_t *root, dnode_t *node) dict_t *dict_create(dictcount_t maxcount, dict_comp_t comp) { - dict_t *new = malloc(sizeof *new); + dict_t *new = XCALLOC(MTYPE_ISIS_DICT, sizeof(dict_t)); if (new) { new->compare = comp; @@ -284,7 +278,7 @@ void dict_set_allocator(dict_t *dict, dnode_alloc_t al, void dict_destroy(dict_t *dict) { assert (dict_isempty(dict)); - free(dict); + XFREE(MTYPE_ISIS_DICT, dict); } /* @@ -307,9 +301,6 @@ void dict_free_nodes(dict_t *dict) void dict_free(dict_t *dict) { -#ifdef KAZLIB_OBSOLESCENT_DEBUG - assert ("call to obsolescent function dict_free()" && 0); -#endif dict_free_nodes(dict); } @@ -810,7 +801,7 @@ dnode_t *dict_delete(dict_t *dict, dnode_t *delete) int dict_alloc_insert(dict_t *dict, const void *key, void *data) { - dnode_t *node = dict->allocnode(dict->context); + dnode_t *node = dict->allocnode (dict->context); if (node) { dnode_init(node, data); @@ -946,17 +937,17 @@ int dict_contains(dict_t *dict, dnode_t *node) static dnode_t *dnode_alloc(void *context) { - return malloc(sizeof *dnode_alloc(NULL)); + return XCALLOC(MTYPE_ISIS_DICT_NODE, sizeof(dnode_t)); } static void dnode_free(dnode_t *node, void *context) { - free(node); + XFREE(MTYPE_ISIS_DICT_NODE, node); } dnode_t *dnode_create(void *data) { - dnode_t *new = malloc(sizeof *new); + dnode_t *new = XCALLOC(MTYPE_ISIS_DICT_NODE, sizeof(dnode_t)); if (new) { new->data = data; new->parent = NULL; @@ -978,7 +969,7 @@ dnode_t *dnode_init(dnode_t *dnode, void *data) void dnode_destroy(dnode_t *dnode) { assert (!dnode_is_in_a_dict(dnode)); - free(dnode); + XFREE(MTYPE_ISIS_DICT_NODE, dnode); } void *dnode_get(dnode_t *dnode) @@ -1232,7 +1223,7 @@ static int comparef(const void *key1, const void *key2) static char *dupstring(char *str) { int sz = strlen(str) + 1; - char *new = malloc(sz); + char *new = XCALLOC(MTYPE_ISIS_TMP, sz); if (new) memcpy(new, str, sz); return new; @@ -1347,7 +1338,7 @@ int main(void) "s switch to non-functioning allocator\n" "q quit"; - for (i = 0; i < sizeof darray / sizeof *darray; i++) + for (i = 0; i < 10; i++) dict_init(&darray[i], DICTCOUNT_T_MAX, comparef); for (;;) { diff --git a/isisd/dict.h b/isisd/dict.h index 9395d1c08..93edb7d60 100644 --- a/isisd/dict.h +++ b/isisd/dict.h @@ -22,9 +22,6 @@ #define DICT_H #include -#ifdef KAZLIB_SIDEEFFECT_DEBUG -#include "sfx.h" -#endif /* * Blurb for inclusion into C++ translation units @@ -44,16 +41,12 @@ typedef unsigned long dictcount_t; typedef enum { dnode_red, dnode_black } dnode_color_t; typedef struct dnode_t { - #if defined(DICT_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG) struct dnode_t *dict_left; struct dnode_t *dict_right; struct dnode_t *dict_parent; dnode_color_t dict_color; const void *dict_key; void *dict_data; - #else - int dict_dummy; - #endif } dnode_t; typedef int (*dict_comp_t)(const void *, const void *); @@ -61,7 +54,6 @@ typedef dnode_t *(*dnode_alloc_t)(void *); typedef void (*dnode_free_t)(dnode_t *, void *); typedef struct dict_t { - #if defined(DICT_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG) dnode_t dict_nilnode; dictcount_t dict_nodecount; dictcount_t dict_maxcount; @@ -70,20 +62,13 @@ typedef struct dict_t { dnode_free_t dict_freenode; void *dict_context; int dict_dupes; - #else - int dict_dummmy; - #endif } dict_t; typedef void (*dnode_process_t)(dict_t *, dnode_t *, void *); typedef struct dict_load_t { - #if defined(DICT_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG) dict_t *dict_dictptr; dnode_t dict_nilnode; - #else - int dict_dummmy; - #endif } dict_load_t; extern dict_t *dict_create(dictcount_t, dict_comp_t); @@ -124,18 +109,12 @@ extern void dict_load_next(dict_load_t *, dnode_t *, const void *); extern void dict_load_end(dict_load_t *); extern void dict_merge(dict_t *, dict_t *); -#if defined(DICT_IMPLEMENTATION) || !defined(KAZLIB_OPAQUE_DEBUG) -#ifdef KAZLIB_SIDEEFFECT_DEBUG -#define dict_isfull(D) (SFX_CHECK(D)->dict_nodecount == (D)->dict_maxcount) -#else #define dict_isfull(D) ((D)->dict_nodecount == (D)->dict_maxcount) -#endif #define dict_count(D) ((D)->dict_nodecount) #define dict_isempty(D) ((D)->dict_nodecount == 0) #define dnode_get(N) ((N)->dict_data) #define dnode_getkey(N) ((N)->dict_key) #define dnode_put(N, X) ((N)->dict_data = (X)) -#endif #ifdef __cplusplus } diff --git a/isisd/include-netbsd/iso.h b/isisd/include-netbsd/iso.h index 1a80aec8e..42b9bc88d 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 de34bea9d..8afabede4 100644 --- a/isisd/isis_adjacency.c +++ b/isisd/isis_adjacency.c @@ -36,6 +36,7 @@ #include "isisd/include-netbsd/iso.h" #include "isisd/isis_constants.h" #include "isisd/isis_common.h" +#include "isisd/isis_flags.h" #include "isisd/isisd.h" #include "isisd/isis_circuit.h" #include "isisd/isis_adjacency.h" @@ -43,11 +44,15 @@ #include "isisd/isis_dr.h" #include "isisd/isis_dynhn.h" #include "isisd/isis_pdu.h" +#include "isisd/isis_tlv.h" +#include "isisd/isis_lsp.h" +#include "isisd/isis_spf.h" +#include "isisd/isis_events.h" extern struct isis *isis; static struct isis_adjacency * -adj_alloc (u_char * id) +adj_alloc (const u_char *id) { struct isis_adjacency *adj; @@ -58,7 +63,7 @@ adj_alloc (u_char * id) } struct isis_adjacency * -isis_new_adj (u_char * id, u_char * snpa, int level, +isis_new_adj (const u_char * id, const u_char * snpa, int level, struct isis_circuit *circuit) { struct isis_adjacency *adj; @@ -73,9 +78,9 @@ isis_new_adj (u_char * id, u_char * snpa, int level, } if (snpa) { - memcpy (adj->snpa, snpa, 6); + memcpy (adj->snpa, snpa, ETH_ALEN); } else { - memset (adj->snpa, ' ', 6); + memset (adj->snpa, ' ', ETH_ALEN); } adj->circuit = circuit; @@ -99,7 +104,7 @@ isis_new_adj (u_char * id, u_char * snpa, int level, } struct isis_adjacency * -isis_adj_lookup (u_char * sysid, struct list *adjdb) +isis_adj_lookup (const u_char * sysid, struct list *adjdb) { struct isis_adjacency *adj; struct listnode *node; @@ -112,7 +117,7 @@ isis_adj_lookup (u_char * sysid, struct list *adjdb) } struct isis_adjacency * -isis_adj_lookup_snpa (u_char * ssnpa, struct list *adjdb) +isis_adj_lookup_snpa (const u_char * ssnpa, struct list *adjdb) { struct listnode *node; struct isis_adjacency *adj; @@ -125,37 +130,60 @@ isis_adj_lookup_snpa (u_char * ssnpa, struct list *adjdb) } void -isis_delete_adj (struct isis_adjacency *adj, struct list *adjdb) +isis_delete_adj (void *arg) { + struct isis_adjacency *adj = arg; + if (!adj) return; - /* When we recieve a NULL list, we will know its p2p. */ - if (adjdb) - listnode_delete (adjdb, adj); - THREAD_OFF (adj->t_expire); + THREAD_TIMER_OFF (adj->t_expire); + + /* remove from SPF trees */ + spftree_area_adj_del (adj->circuit->area, adj); + if (adj->area_addrs) + list_delete (adj->area_addrs); if (adj->ipv4_addrs) list_delete (adj->ipv4_addrs); #ifdef HAVE_IPV6 if (adj->ipv6_addrs) list_delete (adj->ipv6_addrs); #endif - + XFREE (MTYPE_ISIS_ADJACENCY, adj); return; } +static const char * +adj_state2string (int state) +{ + + switch (state) + { + case ISIS_ADJ_INITIALIZING: + return "Initializing"; + case ISIS_ADJ_UP: + return "Up"; + case ISIS_ADJ_DOWN: + return "Down"; + default: + return "Unknown"; + } + + return NULL; /* not reached */ +} + void -isis_adj_state_change (struct isis_adjacency *adj, enum isis_adj_state state, +isis_adj_state_change (struct isis_adjacency *adj, enum isis_adj_state new_state, const char *reason) { int old_state; - int level = adj->level; + int level; struct isis_circuit *circuit; old_state = adj->adj_state; - adj->adj_state = state; + adj->adj_state = new_state; circuit = adj->circuit; @@ -163,42 +191,107 @@ isis_adj_state_change (struct isis_adjacency *adj, enum isis_adj_state state, { zlog_debug ("ISIS-Adj (%s): Adjacency state change %d->%d: %s", circuit->area->area_tag, - old_state, state, reason ? reason : "unspecified"); + old_state, new_state, reason ? reason : "unspecified"); } - if (circuit->circ_type == CIRCUIT_T_BROADCAST) + if (circuit->area->log_adj_changes) { - if (state == ISIS_ADJ_UP) - circuit->upadjcount[level - 1]++; - if (state == ISIS_ADJ_DOWN) - { - listnode_delete (adj->circuit->u.bc.adjdb[level - 1], adj); - circuit->upadjcount[level - 1]--; - } + const char *adj_name; + struct isis_dynhn *dyn; - list_delete_all_node (circuit->u.bc.lan_neighs[level - 1]); - isis_adj_build_neigh_list (circuit->u.bc.adjdb[level - 1], - circuit->u.bc.lan_neighs[level - 1]); + dyn = dynhn_find_by_id (adj->sysid); + if (dyn) + adj_name = (const char *)dyn->name.name; + else + adj_name = sysid_print (adj->sysid); + + zlog_info ("%%ADJCHANGE: Adjacency to %s (%s) changed from %s to %s, %s", + adj_name, + adj->circuit->interface->name, + adj_state2string (old_state), + adj_state2string (new_state), + reason ? reason : "unspecified"); } - else if (state == ISIS_ADJ_UP) - { /* p2p interface */ - if (adj->sys_type == ISIS_SYSTYPE_UNKNOWN) - send_hello (circuit, 1); - - /* update counter & timers for debugging purposes */ - adj->last_flap = time (NULL); - adj->flaps++; - - /* 7.3.17 - going up on P2P -> send CSNP */ - /* FIXME: yup, I know its wrong... but i will do it! (for now) */ - send_csnp (circuit, 1); - send_csnp (circuit, 2); + + if (circuit->circ_type == CIRCUIT_T_BROADCAST) + { + for (level = IS_LEVEL_1; level <= IS_LEVEL_2; level++) + { + if ((adj->level & level) == 0) + continue; + if (new_state == ISIS_ADJ_UP) + { + circuit->upadjcount[level - 1]++; + isis_event_adjacency_state_change (adj, new_state); + /* update counter & timers for debugging purposes */ + adj->last_flap = time (NULL); + adj->flaps++; + } + else if (new_state == ISIS_ADJ_DOWN) + { + listnode_delete (circuit->u.bc.adjdb[level - 1], adj); + circuit->upadjcount[level - 1]--; + if (circuit->upadjcount[level - 1] == 0) + { + /* Clean lsp_queue when no adj is up. */ + if (circuit->lsp_queue) + list_delete_all_node (circuit->lsp_queue); + } + isis_event_adjacency_state_change (adj, new_state); + isis_delete_adj (adj); + } + + if (circuit->u.bc.lan_neighs[level - 1]) + { + list_delete_all_node (circuit->u.bc.lan_neighs[level - 1]); + isis_adj_build_neigh_list (circuit->u.bc.adjdb[level - 1], + circuit->u.bc.lan_neighs[level - 1]); + } + + /* On adjacency state change send new pseudo LSP if we are the DR */ + if (circuit->u.bc.is_dr[level - 1]) + lsp_regenerate_schedule_pseudo (circuit, level); + } } - else if (state == ISIS_ADJ_DOWN) - { /* p2p interface */ - adj->circuit->u.p2p.neighbor = NULL; - isis_delete_adj (adj, NULL); + else if (circuit->circ_type == CIRCUIT_T_P2P) + { + for (level = IS_LEVEL_1; level <= IS_LEVEL_2; level++) + { + if ((adj->level & level) == 0) + continue; + if (new_state == ISIS_ADJ_UP) + { + circuit->upadjcount[level - 1]++; + isis_event_adjacency_state_change (adj, new_state); + + if (adj->sys_type == ISIS_SYSTYPE_UNKNOWN) + send_hello (circuit, level); + + /* update counter & timers for debugging purposes */ + adj->last_flap = time (NULL); + adj->flaps++; + + /* 7.3.17 - going up on P2P -> send CSNP */ + /* FIXME: yup, I know its wrong... but i will do it! (for now) */ + send_csnp (circuit, level); + } + else if (new_state == ISIS_ADJ_DOWN) + { + if (adj->circuit->u.p2p.neighbor == adj) + adj->circuit->u.p2p.neighbor = NULL; + circuit->upadjcount[level - 1]--; + if (circuit->upadjcount[level - 1] == 0) + { + /* Clean lsp_queue when no adj is up. */ + if (circuit->lsp_queue) + list_delete_all_node (circuit->lsp_queue); + } + isis_event_adjacency_state_change (adj, new_state); + isis_delete_adj (adj); + } + } } + return; } @@ -221,11 +314,11 @@ isis_adj_print (struct isis_adjacency *adj) zlog_debug ("%s", dyn->name.name); zlog_debug ("SystemId %20s SNPA %s, level %d\nHolding Time %d", - adj->sysid ? sysid_print (adj->sysid) : "unknown", - snpa_print (adj->snpa), adj->level, adj->hold_time); + sysid_print (adj->sysid), snpa_print (adj->snpa), + adj->level, adj->hold_time); if (adj->ipv4_addrs && listcount (adj->ipv4_addrs) > 0) { - zlog_debug ("IPv4 Addresses:"); + zlog_debug ("IPv4 Address(es):"); for (ALL_LIST_ELEMENTS_RO (adj->ipv4_addrs, node, ipv4_addr)) zlog_debug ("%s", inet_ntoa (*ipv4_addr)); @@ -234,7 +327,7 @@ isis_adj_print (struct isis_adjacency *adj) #ifdef HAVE_IPV6 if (adj->ipv6_addrs && listcount (adj->ipv6_addrs) > 0) { - zlog_debug ("IPv6 Addresses:"); + zlog_debug ("IPv6 Address(es):"); for (ALL_LIST_ELEMENTS_RO (adj->ipv6_addrs, node, ipv6_addr)) { inet_ntop (AF_INET6, ipv6_addr, (char *)ip6, INET6_ADDRSTRLEN); @@ -251,14 +344,12 @@ int isis_adj_expire (struct thread *thread) { struct isis_adjacency *adj; - int level; /* * Get the adjacency */ adj = THREAD_ARG (thread); assert (adj); - level = adj->level; adj->t_expire = NULL; /* trigger the adj expire event */ @@ -267,32 +358,12 @@ isis_adj_expire (struct thread *thread) return 0; } -static const char * -adj_state2string (int state) -{ - - switch (state) - { - case ISIS_ADJ_INITIALIZING: - return "Initializing"; - case ISIS_ADJ_UP: - return "Up"; - case ISIS_ADJ_DOWN: - return "Down"; - default: - return "Unknown"; - } - - return NULL; /* not reached */ -} - /* - * show clns/isis neighbor (detail) + * show isis neighbor [detail] */ -static void -isis_adj_print_vty2 (struct isis_adjacency *adj, struct vty *vty, char detail) +void +isis_adj_print_vty (struct isis_adjacency *adj, struct vty *vty, char detail) { - #ifdef HAVE_IPV6 struct in6_addr *ipv6_addr; u_char ip6[INET6_ADDRSTRLEN]; @@ -306,14 +377,8 @@ isis_adj_print_vty2 (struct isis_adjacency *adj, struct vty *vty, char detail) dyn = dynhn_find_by_id (adj->sysid); if (dyn) vty_out (vty, " %-20s", dyn->name.name); - else if (adj->sysid) - { - vty_out (vty, " %-20s", sysid_print (adj->sysid)); - } else - { - vty_out (vty, " unknown "); - } + vty_out (vty, " %-20s", sysid_print (adj->sysid)); if (detail == ISIS_UI_LEVEL_BRIEF) { @@ -325,7 +390,8 @@ isis_adj_print_vty2 (struct isis_adjacency *adj, struct vty *vty, char detail) vty_out (vty, "%-13s", adj_state2string (adj->adj_state)); now = time (NULL); if (adj->last_upd) - vty_out (vty, "%-9lu", adj->last_upd + adj->hold_time - now); + vty_out (vty, "%-9llu", + (unsigned long long)adj->last_upd + adj->hold_time - now); else vty_out (vty, "- "); vty_out (vty, "%-10s", snpa_print (adj->snpa)); @@ -335,10 +401,11 @@ isis_adj_print_vty2 (struct isis_adjacency *adj, struct vty *vty, char detail) if (detail == ISIS_UI_LEVEL_DETAIL) { level = adj->level; + vty_out (vty, "%s", VTY_NEWLINE); if (adj->circuit) - vty_out (vty, "%s Interface: %s", VTY_NEWLINE, adj->circuit->interface->name); /* interface name */ + vty_out (vty, " Interface: %s", adj->circuit->interface->name); else - vty_out (vty, "NULL circuit!%s", VTY_NEWLINE); + vty_out (vty, " Interface: NULL circuit"); vty_out (vty, ", Level: %u", adj->level); /* level */ vty_out (vty, ", State: %s", adj_state2string (adj->adj_state)); now = time (NULL); @@ -347,40 +414,54 @@ isis_adj_print_vty2 (struct isis_adjacency *adj, struct vty *vty, char detail) time2string (adj->last_upd + adj->hold_time - now)); else vty_out (vty, ", Expires in %s", time2string (adj->hold_time)); - vty_out (vty, "%s Adjacency flaps: %u", VTY_NEWLINE, adj->flaps); + vty_out (vty, "%s", VTY_NEWLINE); + vty_out (vty, " Adjacency flaps: %u", adj->flaps); vty_out (vty, ", Last: %s ago", time2string (now - adj->last_flap)); - vty_out (vty, "%s Circuit type: %s", - VTY_NEWLINE, circuit_t2string (adj->circuit_t)); + vty_out (vty, "%s", VTY_NEWLINE); + vty_out (vty, " Circuit type: %s", circuit_t2string (adj->circuit_t)); vty_out (vty, ", Speaks: %s", nlpid2string (&adj->nlpids)); - vty_out (vty, "%s SNPA: %s", VTY_NEWLINE, snpa_print (adj->snpa)); - dyn = dynhn_find_by_id (adj->lanid); - if (dyn) - vty_out (vty, ", LAN id: %s.%02x", - dyn->name.name, adj->lanid[ISIS_SYS_ID_LEN]); - else - vty_out (vty, ", LAN id: %s.%02x", - sysid_print (adj->lanid), adj->lanid[ISIS_SYS_ID_LEN]); - - vty_out (vty, "%s Priority: %u", - VTY_NEWLINE, adj->prio[adj->level - 1]); - - vty_out (vty, ", %s, DIS flaps: %u, Last: %s ago%s", - isis_disflag2string (adj->dis_record[ISIS_LEVELS + level - 1]. - dis), adj->dischanges[level - 1], - time2string (now - - (adj->dis_record[ISIS_LEVELS + level - 1]. - last_dis_change)), VTY_NEWLINE); + vty_out (vty, "%s", VTY_NEWLINE); + vty_out (vty, " SNPA: %s", snpa_print (adj->snpa)); + if (adj->circuit && (adj->circuit->circ_type == CIRCUIT_T_BROADCAST)) + { + dyn = dynhn_find_by_id (adj->lanid); + if (dyn) + vty_out (vty, ", LAN id: %s.%02x", + dyn->name.name, adj->lanid[ISIS_SYS_ID_LEN]); + else + vty_out (vty, ", LAN id: %s.%02x", + sysid_print (adj->lanid), adj->lanid[ISIS_SYS_ID_LEN]); + + vty_out (vty, "%s", VTY_NEWLINE); + vty_out (vty, " LAN Priority: %u", adj->prio[adj->level - 1]); + + vty_out (vty, ", %s, DIS flaps: %u, Last: %s ago", + isis_disflag2string (adj->dis_record[ISIS_LEVELS + level - 1]. + dis), adj->dischanges[level - 1], + time2string (now - + (adj->dis_record[ISIS_LEVELS + level - 1]. + last_dis_change))); + } + vty_out (vty, "%s", VTY_NEWLINE); + if (adj->area_addrs && listcount (adj->area_addrs) > 0) + { + struct area_addr *area_addr; + vty_out (vty, " Area Address(es):%s", VTY_NEWLINE); + for (ALL_LIST_ELEMENTS_RO (adj->area_addrs, node, area_addr)) + vty_out (vty, " %s%s", isonet_print (area_addr->area_addr, + area_addr->addr_len), VTY_NEWLINE); + } if (adj->ipv4_addrs && listcount (adj->ipv4_addrs) > 0) { - vty_out (vty, " IPv4 Addresses:%s", VTY_NEWLINE); + vty_out (vty, " IPv4 Address(es):%s", VTY_NEWLINE); for (ALL_LIST_ELEMENTS_RO (adj->ipv4_addrs, node, ip_addr)) vty_out (vty, " %s%s", inet_ntoa (*ip_addr), VTY_NEWLINE); } #ifdef HAVE_IPV6 if (adj->ipv6_addrs && listcount (adj->ipv6_addrs) > 0) { - vty_out (vty, " IPv6 Addresses:%s", VTY_NEWLINE); + vty_out (vty, " IPv6 Address(es):%s", VTY_NEWLINE); for (ALL_LIST_ELEMENTS_RO (adj->ipv6_addrs, node, ipv6_addr)) { inet_ntop (AF_INET6, ipv6_addr, (char *)ip6, INET6_ADDRSTRLEN); @@ -393,53 +474,6 @@ isis_adj_print_vty2 (struct isis_adjacency *adj, struct vty *vty, char detail) return; } -void -isis_adj_print_vty (struct isis_adjacency *adj, struct vty *vty) -{ - isis_adj_print_vty2 (adj, vty, ISIS_UI_LEVEL_BRIEF); -} - -void -isis_adj_print_vty_detail (struct isis_adjacency *adj, struct vty *vty) -{ - isis_adj_print_vty2 (adj, vty, ISIS_UI_LEVEL_DETAIL); -} - -void -isis_adj_print_vty_extensive (struct isis_adjacency *adj, struct vty *vty) -{ - isis_adj_print_vty2 (adj, vty, ISIS_UI_LEVEL_EXTENSIVE); -} - -void -isis_adj_p2p_print_vty (struct isis_adjacency *adj, struct vty *vty) -{ - isis_adj_print_vty2 (adj, vty, ISIS_UI_LEVEL_BRIEF); -} - -void -isis_adj_p2p_print_vty_detail (struct isis_adjacency *adj, struct vty *vty) -{ - isis_adj_print_vty2 (adj, vty, ISIS_UI_LEVEL_DETAIL); -} - -void -isis_adj_p2p_print_vty_extensive (struct isis_adjacency *adj, struct vty *vty) -{ - isis_adj_print_vty2 (adj, vty, ISIS_UI_LEVEL_EXTENSIVE); -} - -void -isis_adjdb_iterate (struct list *adjdb, void (*func) (struct isis_adjacency *, - void *), void *arg) -{ - struct listnode *node, *nnode; - struct isis_adjacency *adj; - - for (ALL_LIST_ELEMENTS (adjdb, node, nnode, adj)) - (*func) (adj, arg); -} - void isis_adj_build_neigh_list (struct list *adjdb, struct list *list) { @@ -473,6 +507,11 @@ isis_adj_build_up_list (struct list *adjdb, struct list *list) struct isis_adjacency *adj; struct listnode *node; + if (adjdb == NULL) { + zlog_warn ("isis_adj_build_up_list(): adjacency DB is empty"); + return; + } + if (!list) { zlog_warn ("isis_adj_build_up_list(): NULL list"); diff --git a/isisd/isis_adjacency.h b/isisd/isis_adjacency.h index 99a8bb228..99d0c493b 100644 --- a/isisd/isis_adjacency.h +++ b/isisd/isis_adjacency.h @@ -44,6 +44,7 @@ enum isis_system_type enum isis_adj_state { + ISIS_ADJ_UNKNOWN, ISIS_ADJ_INITIALIZING, ISIS_ADJ_UP, ISIS_ADJ_DOWN @@ -83,8 +84,10 @@ struct isis_adjacency struct list *area_addrs; /* areaAdressesOfNeighbour */ struct nlpids nlpids; /* protocols spoken ... */ struct list *ipv4_addrs; + struct in_addr router_address; #ifdef HAVE_IPV6 struct list *ipv6_addrs; + struct in6_addr router_address6; #endif /* HAVE_IPV6 */ u_char prio[ISIS_LEVELS]; /* priorityOfNeighbour for DIS */ int circuit_t; /* from hello PDU hdr */ @@ -98,30 +101,18 @@ struct isis_adjacency struct isis_circuit *circuit; /* back pointer */ }; -struct isis_adjacency *isis_adj_lookup (u_char * sysid, struct list *adjdb); -struct isis_adjacency *isis_adj_lookup_snpa (u_char * ssnpa, +struct isis_adjacency *isis_adj_lookup (const u_char * sysid, struct list *adjdb); +struct isis_adjacency *isis_adj_lookup_snpa (const u_char * ssnpa, struct list *adjdb); -struct isis_adjacency *isis_new_adj (u_char * id, u_char * snpa, int level, +struct isis_adjacency *isis_new_adj (const u_char * id, const u_char * snpa, int level, struct isis_circuit *circuit); -void isis_delete_adj (struct isis_adjacency *adj, struct list *adjdb); +void isis_delete_adj (void *adj); void isis_adj_state_change (struct isis_adjacency *adj, enum isis_adj_state state, const char *reason); void isis_adj_print (struct isis_adjacency *adj); int isis_adj_expire (struct thread *thread); -void isis_adj_print_vty (struct isis_adjacency *adj, struct vty *vty); -void isis_adj_print_vty_detail (struct isis_adjacency *adj, struct vty *vty); -void isis_adj_print_vty_extensive (struct isis_adjacency *adj, - struct vty *vty); -void isis_adj_p2p_print_vty (struct isis_adjacency *adj, struct vty *vty); -void isis_adj_p2p_print_vty_detail (struct isis_adjacency *adj, - struct vty *vty); -void isis_adj_p2p_print_vty_extensive (struct isis_adjacency *adj, - struct vty *vty); - +void isis_adj_print_vty (struct isis_adjacency *adj, struct vty *vty, char detail); void isis_adj_build_neigh_list (struct list *adjdb, struct list *list); void isis_adj_build_up_list (struct list *adjdb, struct list *list); -void isis_adjdb_iterate (struct list *adjdb, - void (*func) (struct isis_adjacency *, - void *), void *arg); #endif /* ISIS_ADJACENCY_H */ diff --git a/isisd/isis_bpf.c b/isisd/isis_bpf.c index 05f11386c..889b4c309 100644 --- a/isisd/isis_bpf.c +++ b/isisd/isis_bpf.c @@ -29,6 +29,7 @@ #include #include "log.h" +#include "network.h" #include "stream.h" #include "if.h" @@ -301,7 +302,17 @@ int isis_send_pdu_bcast (struct isis_circuit *circuit, int level) { struct ether_header *eth; - int written; + ssize_t written; + size_t buflen; + + buflen = stream_get_endp (circuit->snd_stream) + LLC_LEN + ETHER_HDR_LEN; + if (buflen > sizeof (sock_buff)) + { + zlog_warn ("isis_send_pdu_bcast: sock_buff size %zu is less than " + "output pdu size %zu on circuit %s", + sizeof (sock_buff), buflen, circuit->interface->name); + return ISIS_WARNING; + } stream_set_getp (circuit->snd_stream, 0); @@ -328,9 +339,15 @@ isis_send_pdu_bcast (struct isis_circuit *circuit, int level) stream_get_endp (circuit->snd_stream)); /* now we can send this */ - written = write (circuit->fd, sock_buff, - stream_get_endp (circuit->snd_stream) - + LLC_LEN + ETHER_HDR_LEN); + written = write (circuit->fd, sock_buff, buflen); + if (written < 0) + { + zlog_warn("IS-IS bpf: could not transmit packet on %s: %s", + circuit->interface->name, safe_strerror(errno)); + if (ERRNO_IO_RETRY(errno)) + return ISIS_WARNING; + return ISIS_ERROR; + } return ISIS_OK; } diff --git a/isisd/isis_circuit.c b/isisd/isis_circuit.c index 99e2bf6f2..1d1a59ef8 100644 --- a/isisd/isis_circuit.c +++ b/isisd/isis_circuit.c @@ -36,6 +36,7 @@ #include "linklist.h" #include "command.h" #include "thread.h" +#include "vty.h" #include "hash.h" #include "prefix.h" #include "stream.h" @@ -44,6 +45,7 @@ #include "isisd/include-netbsd/iso.h" #include "isisd/isis_constants.h" #include "isisd/isis_common.h" +#include "isisd/isis_flags.h" #include "isisd/isis_circuit.h" #include "isisd/isis_tlv.h" #include "isisd/isis_lsp.h" @@ -53,18 +55,14 @@ #include "isisd/isis_constants.h" #include "isisd/isis_adjacency.h" #include "isisd/isis_dr.h" -#include "isisd/isis_flags.h" #include "isisd/isisd.h" #include "isisd/isis_csm.h" #include "isisd/isis_events.h" - -extern struct thread_master *master; -extern struct isis *isis; +#include "isisd/isis_te.h" /* * Prototypes. */ -void isis_circuit_down(struct isis_circuit *); int isis_interface_config_write(struct vty *); int isis_if_new_hook(struct interface *); int isis_if_delete_hook(struct interface *); @@ -76,55 +74,62 @@ isis_circuit_new () int i; circuit = XCALLOC (MTYPE_ISIS_CIRCUIT, sizeof (struct isis_circuit)); - if (circuit) - { - /* set default metrics for circuit */ - for (i = 0; i < 2; i++) - { - circuit->metrics[i].metric_default = DEFAULT_CIRCUIT_METRICS; - circuit->metrics[i].metric_expense = METRICS_UNSUPPORTED; - circuit->metrics[i].metric_error = METRICS_UNSUPPORTED; - circuit->metrics[i].metric_delay = METRICS_UNSUPPORTED; - circuit->te_metric[i] = DEFAULT_CIRCUIT_METRICS; - } - } - else + if (circuit == NULL) { zlog_err ("Can't malloc isis circuit"); return NULL; } + /* + * Default values + */ + circuit->is_type = IS_LEVEL_1_AND_2; + circuit->flags = 0; + circuit->pad_hellos = 1; + for (i = 0; i < 2; i++) + { + circuit->hello_interval[i] = DEFAULT_HELLO_INTERVAL; + circuit->hello_multiplier[i] = DEFAULT_HELLO_MULTIPLIER; + circuit->csnp_interval[i] = DEFAULT_CSNP_INTERVAL; + circuit->psnp_interval[i] = DEFAULT_PSNP_INTERVAL; + circuit->priority[i] = DEFAULT_PRIORITY; + circuit->metric[i] = DEFAULT_CIRCUIT_METRIC; + circuit->te_metric[i] = DEFAULT_CIRCUIT_METRIC; + } + + circuit->mtc = mpls_te_circuit_new(); + return circuit; } +void +isis_circuit_del (struct isis_circuit *circuit) +{ + if (!circuit) + return; + + isis_circuit_if_unbind (circuit, circuit->interface); + + /* and lastly the circuit itself */ + XFREE (MTYPE_ISIS_CIRCUIT, circuit); + + return; +} + void isis_circuit_configure (struct isis_circuit *circuit, struct isis_area *area) { - int i; + assert (area); circuit->area = area; + /* - * The level for the circuit is same as for the area, unless configured - * otherwise. - */ - circuit->circuit_is_type = area->is_type; - /* - * Default values + * Whenever the is-type of an area is changed, the is-type of each circuit + * in that area is updated to a non-empty subset of the area is-type. + * Inversely, when configuring a new circuit, this property should be + * ensured as well. */ - for (i = 0; i < 2; i++) - { - circuit->hello_interval[i] = HELLO_INTERVAL; - circuit->hello_multiplier[i] = HELLO_MULTIPLIER; - circuit->csnp_interval[i] = CSNP_INTERVAL; - circuit->psnp_interval[i] = PSNP_INTERVAL; - circuit->u.bc.priority[i] = DEFAULT_PRIORITY; - } - if (circuit->circ_type == CIRCUIT_T_BROADCAST) - { - circuit->u.bc.adjdb[0] = list_new (); - circuit->u.bc.adjdb[1] = list_new (); - circuit->u.bc.pad_hellos = 1; - } - circuit->lsp_interval = LSP_INTERVAL; + if (area->is_type != IS_LEVEL_1_AND_2) + circuit->is_type = area->is_type; /* * Add the circuit into area @@ -132,25 +137,20 @@ isis_circuit_configure (struct isis_circuit *circuit, struct isis_area *area) listnode_add (area->circuit_list, circuit); circuit->idx = flags_get_index (&area->flags); - circuit->lsp_queue = list_new (); return; } void -isis_circuit_deconfigure (struct isis_circuit *circuit, - struct isis_area *area) +isis_circuit_deconfigure (struct isis_circuit *circuit, struct isis_area *area) { - - /* destroy adjacencies */ - if (circuit->u.bc.adjdb[0]) - isis_adjdb_iterate (circuit->u.bc.adjdb[0], (void(*) (struct isis_adjacency *, void *)) isis_delete_adj, circuit->u.bc.adjdb[0]); - if (circuit->u.bc.adjdb[1]) - isis_adjdb_iterate (circuit->u.bc.adjdb[1], (void(*) (struct isis_adjacency *, void *)) isis_delete_adj, circuit->u.bc.adjdb[1]); - /* Remove circuit from area */ - listnode_delete (area->circuit_list, circuit); /* Free the index of SRM and SSN flags */ flags_free_index (&area->flags, circuit->idx); + circuit->idx = 0; + /* Remove circuit from area */ + assert (circuit->area == area); + listnode_delete (area->circuit_list, circuit); + circuit->area = NULL; return; } @@ -166,8 +166,11 @@ circuit_lookup_by_ifp (struct interface *ifp, struct list *list) for (ALL_LIST_ELEMENTS_RO (list, node, circuit)) if (circuit->interface == ifp) - return circuit; - + { + assert (ifp->info == circuit); + return circuit; + } + return NULL; } @@ -178,83 +181,57 @@ circuit_scan_by_ifp (struct interface *ifp) struct listnode *node; struct isis_circuit *circuit; - if (!isis->area_list) - return NULL; + if (ifp->info) + return (struct isis_circuit *)ifp->info; - for (ALL_LIST_ELEMENTS_RO (isis->area_list, node, area)) + if (isis->area_list) { - circuit = circuit_lookup_by_ifp (ifp, area->circuit_list); - if (circuit) - return circuit; + for (ALL_LIST_ELEMENTS_RO (isis->area_list, node, area)) + { + circuit = circuit_lookup_by_ifp (ifp, area->circuit_list); + if (circuit) + return circuit; + } } - return circuit_lookup_by_ifp (ifp, isis->init_circ_list); } -void -isis_circuit_del (struct isis_circuit *circuit) -{ - - if (!circuit) - return; - - if (circuit->circ_type == CIRCUIT_T_BROADCAST) - { - /* destroy adjacency databases */ - if (circuit->u.bc.adjdb[0]) - list_delete (circuit->u.bc.adjdb[0]); - if (circuit->u.bc.adjdb[1]) - list_delete (circuit->u.bc.adjdb[1]); - /* destroy neighbour lists */ - if (circuit->u.bc.lan_neighs[0]) - list_delete (circuit->u.bc.lan_neighs[0]); - if (circuit->u.bc.lan_neighs[1]) - list_delete (circuit->u.bc.lan_neighs[1]); - /* destroy addresses */ - } - if (circuit->ip_addrs) - list_delete (circuit->ip_addrs); -#ifdef HAVE_IPV6 - if (circuit->ipv6_link) - list_delete (circuit->ipv6_link); - if (circuit->ipv6_non_link) - list_delete (circuit->ipv6_non_link); -#endif /* HAVE_IPV6 */ - - /* and lastly the circuit itself */ - XFREE (MTYPE_ISIS_CIRCUIT, circuit); - - return; -} - void isis_circuit_add_addr (struct isis_circuit *circuit, struct connected *connected) { + struct listnode *node; struct prefix_ipv4 *ipv4; u_char buf[BUFSIZ]; #ifdef HAVE_IPV6 struct prefix_ipv6 *ipv6; #endif /* HAVE_IPV6 */ - if (!circuit->ip_addrs) - circuit->ip_addrs = list_new (); -#ifdef HAVE_IPV6 - if (!circuit->ipv6_link) - circuit->ipv6_link = list_new (); - if (!circuit->ipv6_non_link) - circuit->ipv6_non_link = list_new (); -#endif /* HAVE_IPV6 */ - memset (&buf, 0, BUFSIZ); if (connected->address->family == AF_INET) { + u_int32_t addr = connected->address->u.prefix4.s_addr; + addr = ntohl (addr); + if (IPV4_NET0(addr) || + IPV4_NET127(addr) || + IN_CLASSD(addr) || + IPV4_LINKLOCAL(addr)) + return; + + for (ALL_LIST_ELEMENTS_RO (circuit->ip_addrs, node, ipv4)) + if (prefix_same ((struct prefix *) ipv4, connected->address)) + return; + ipv4 = prefix_ipv4_new (); ipv4->prefixlen = connected->address->prefixlen; ipv4->prefix = connected->address->u.prefix4; listnode_add (circuit->ip_addrs, ipv4); + + /* Update MPLS TE Local IP address parameter */ + set_circuitparams_local_ipaddr (circuit->mtc, ipv4->prefix); + if (circuit->area) - lsp_regenerate_schedule (circuit->area); + lsp_regenerate_schedule (circuit->area, circuit->is_type, 0); #ifdef EXTREME_DEBUG prefix2str (connected->address, buf, BUFSIZ); @@ -265,6 +242,16 @@ isis_circuit_add_addr (struct isis_circuit *circuit, #ifdef HAVE_IPV6 if (connected->address->family == AF_INET6) { + if (IN6_IS_ADDR_LOOPBACK(&connected->address->u.prefix6)) + return; + + for (ALL_LIST_ELEMENTS_RO (circuit->ipv6_link, node, ipv6)) + if (prefix_same ((struct prefix *) ipv6, connected->address)) + return; + for (ALL_LIST_ELEMENTS_RO (circuit->ipv6_non_link, node, ipv6)) + if (prefix_same ((struct prefix *) ipv6, connected->address)) + return; + ipv6 = prefix_ipv6_new (); ipv6->prefixlen = connected->address->prefixlen; ipv6->prefix = connected->address->u.prefix6; @@ -274,7 +261,7 @@ isis_circuit_add_addr (struct isis_circuit *circuit, else listnode_add (circuit->ipv6_non_link, ipv6); if (circuit->area) - lsp_regenerate_schedule (circuit->area); + lsp_regenerate_schedule (circuit->area, circuit->is_type, 0); #ifdef EXTREME_DEBUG prefix2str (connected->address, buf, BUFSIZ); @@ -306,21 +293,30 @@ isis_circuit_del_addr (struct isis_circuit *circuit, ipv4->prefix = connected->address->u.prefix4; for (ALL_LIST_ELEMENTS_RO (circuit->ip_addrs, node, ip)) - if (prefix_same ((struct prefix *) ip, (struct prefix *) &ipv4)) + if (prefix_same ((struct prefix *) ip, (struct prefix *) ipv4)) break; if (ip) { listnode_delete (circuit->ip_addrs, ip); - if (circuit->area) - lsp_regenerate_schedule (circuit->area); + if (circuit->area) + lsp_regenerate_schedule (circuit->area, circuit->is_type, 0); } else { prefix2str (connected->address, (char *)buf, BUFSIZ); - zlog_warn("Nonexitant ip address %s removal attempt from circuit \ - %d", buf, circuit->circuit_id); + zlog_warn ("Nonexitant ip address %s removal attempt from \ + circuit %d", buf, circuit->circuit_id); + zlog_warn ("Current ip addresses on %s:", circuit->interface->name); + for (ALL_LIST_ELEMENTS_RO(circuit->ip_addrs, node, ip)) + { + prefix2str((struct prefix*)ip, (char *)buf, BUFSIZ); + zlog_warn(" %s", buf); + } + zlog_warn("End of addresses"); } + + prefix_ipv4_free (ipv4); } #ifdef HAVE_IPV6 if (connected->address->family == AF_INET6) @@ -359,72 +355,120 @@ isis_circuit_del_addr (struct isis_circuit *circuit, if (!found) { prefix2str (connected->address, (char *)buf, BUFSIZ); - zlog_warn("Nonexitant ip address %s removal attempt from \ - circuit %d", buf, circuit->circuit_id); + zlog_warn ("Nonexitant ip address %s removal attempt from \ + circuit %d", buf, circuit->circuit_id); + zlog_warn ("Current ip addresses on %s:", circuit->interface->name); + for (ALL_LIST_ELEMENTS_RO(circuit->ipv6_link, node, ip6)) + { + prefix2str((struct prefix*)ip6, (char *)buf, BUFSIZ); + zlog_warn(" %s", buf); + } + zlog_warn(" -----"); + for (ALL_LIST_ELEMENTS_RO(circuit->ipv6_non_link, node, ip6)) + { + prefix2str((struct prefix*)ip6, (char *)buf, BUFSIZ); + zlog_warn(" %s", buf); + } + zlog_warn("End of addresses"); } - else - if (circuit->area) - lsp_regenerate_schedule (circuit->area); + else if (circuit->area) + lsp_regenerate_schedule (circuit->area, circuit->is_type, 0); + + prefix_ipv6_free (ipv6); } #endif /* HAVE_IPV6 */ return; } +static u_char +isis_circuit_id_gen (struct interface *ifp) +{ + u_char id = 0; + char ifname[16]; + unsigned int i; + int start = -1, end = -1; + + /* + * Get a stable circuit id from ifname. This makes + * the ifindex from flapping when netdevs are created + * and deleted on the fly. Note that this circuit id + * is used in pseudo lsps so it is better to be stable. + * The following code works on any reasonanle ifname + * like: eth1 or trk-1.1 etc. + */ + for (i = 0; i < strlen (ifp->name); i++) + { + if (isdigit((unsigned char)ifp->name[i])) + { + if (start < 0) + { + start = i; + end = i + 1; + } + else + { + end = i + 1; + } + } + else if (start >= 0) + break; + } + + if ((start >= 0) && (end >= start) && (end - start) < 16) + { + memset (ifname, 0, 16); + strncpy (ifname, &ifp->name[start], end - start); + id = (u_char)atoi(ifname); + } + + /* Try to be unique. */ + if (!id) + id = (u_char)((ifp->ifindex & 0xff) | 0x80); + + return id; +} + void isis_circuit_if_add (struct isis_circuit *circuit, struct interface *ifp) { struct listnode *node, *nnode; struct connected *conn; - circuit->interface = ifp; - ifp->info = circuit; - - circuit->circuit_id = ifp->ifindex % 255; /* FIXME: Why not ? */ + circuit->circuit_id = isis_circuit_id_gen (ifp); + isis_circuit_if_bind (circuit, ifp); /* isis_circuit_update_addrs (circuit, ifp); */ if (if_is_broadcast (ifp)) { - circuit->circ_type = CIRCUIT_T_BROADCAST; - /* - * Get the Hardware Address - */ -#ifdef HAVE_STRUCT_SOCKADDR_DL -#ifndef SUNOS_5 - if (circuit->interface->sdl.sdl_alen != ETHER_ADDR_LEN) - zlog_warn ("unsupported link layer"); - else - memcpy (circuit->u.bc.snpa, LLADDR (&circuit->interface->sdl), - ETH_ALEN); -#endif -#else - if (circuit->interface->hw_addr_len != ETH_ALEN) - { - zlog_warn ("unsupported link layer"); - } + if (circuit->circ_type_config == CIRCUIT_T_P2P) + circuit->circ_type = CIRCUIT_T_P2P; else - { - memcpy (circuit->u.bc.snpa, circuit->interface->hw_addr, ETH_ALEN); - } -#ifdef EXTREME_DEGUG - zlog_debug ("isis_circuit_if_add: if_id %d, isomtu %d snpa %s", - circuit->interface->ifindex, ISO_MTU (circuit), - snpa_print (circuit->u.bc.snpa)); - -#endif /* EXTREME_DEBUG */ -#endif /* HAVE_STRUCT_SOCKADDR_DL */ + circuit->circ_type = CIRCUIT_T_BROADCAST; } else if (if_is_pointopoint (ifp)) { circuit->circ_type = CIRCUIT_T_P2P; } + else if (if_is_loopback (ifp)) + { + circuit->circ_type = CIRCUIT_T_LOOPBACK; + circuit->is_passive = 1; + } else { /* It's normal in case of loopback etc. */ if (isis->debugs & DEBUG_EVENTS) - zlog_debug ("isis_circuit_if_add: unsupported media"); + zlog_debug ("isis_circuit_if_add: unsupported media"); + circuit->circ_type = CIRCUIT_T_UNKNOWN; } + circuit->ip_addrs = list_new (); +#ifdef HAVE_IPV6 + circuit->ipv6_link = list_new (); + circuit->ipv6_non_link = list_new (); +#endif /* HAVE_IPV6 */ + for (ALL_LIST_ELEMENTS (ifp->connected, node, nnode, conn)) isis_circuit_add_addr (circuit, conn); @@ -432,88 +476,178 @@ isis_circuit_if_add (struct isis_circuit *circuit, struct interface *ifp) } void -isis_circuit_update_params (struct isis_circuit *circuit, - struct interface *ifp) +isis_circuit_if_del (struct isis_circuit *circuit, struct interface *ifp) { - assert (circuit); + struct listnode *node, *nnode; + struct connected *conn; - if (circuit->circuit_id != ifp->ifindex) - { - zlog_warn ("changing circuit_id %d->%d", circuit->circuit_id, - ifp->ifindex); - circuit->circuit_id = ifp->ifindex % 255; - } + assert (circuit->interface == ifp); - /* FIXME: Why is this needed? shouldn't we compare to the area's mtu */ - /* Ofer, this was here in case someone changes the mtu (e.g. with ifconfig) - The areas MTU is the minimum of mtu's of circuits in the area - now we can't catch the change - if (circuit->mtu != ifp->mtu) { - zlog_warn ("changing circuit mtu %d->%d", circuit->mtu, - ifp->mtu); - circuit->mtu = ifp->mtu; - } - */ - /* - * Get the Hardware Address - */ -#ifdef HAVE_STRUCT_SOCKADDR_DL -#ifndef SUNOS_5 - if (circuit->interface->sdl.sdl_alen != ETHER_ADDR_LEN) - zlog_warn ("unsupported link layer"); - else - memcpy (circuit->u.bc.snpa, LLADDR (&circuit->interface->sdl), ETH_ALEN); -#endif -#else - if (circuit->interface->hw_addr_len != ETH_ALEN) - { - zlog_warn ("unsupported link layer"); - } - else - { - if (memcmp (circuit->u.bc.snpa, circuit->interface->hw_addr, ETH_ALEN)) - { - zlog_warn ("changing circuit snpa %s->%s", - snpa_print (circuit->u.bc.snpa), - snpa_print (circuit->interface->hw_addr)); - } - } -#endif + /* destroy addresses */ + for (ALL_LIST_ELEMENTS (ifp->connected, node, nnode, conn)) + isis_circuit_del_addr (circuit, conn); - if (if_is_broadcast (ifp)) + if (circuit->ip_addrs) { - circuit->circ_type = CIRCUIT_T_BROADCAST; + assert (listcount(circuit->ip_addrs) == 0); + list_delete (circuit->ip_addrs); + circuit->ip_addrs = NULL; } - else if (if_is_pointopoint (ifp)) + +#ifdef HAVE_IPV6 + if (circuit->ipv6_link) { - circuit->circ_type = CIRCUIT_T_P2P; + assert (listcount(circuit->ipv6_link) == 0); + list_delete (circuit->ipv6_link); + circuit->ipv6_link = NULL; } - else + + if (circuit->ipv6_non_link) { - zlog_warn ("isis_circuit_update_params: unsupported media"); + assert (listcount(circuit->ipv6_non_link) == 0); + list_delete (circuit->ipv6_non_link); + circuit->ipv6_non_link = NULL; } +#endif /* HAVE_IPV6 */ + + circuit->circ_type = CIRCUIT_T_UNKNOWN; + circuit->circuit_id = 0; return; } void -isis_circuit_if_del (struct isis_circuit *circuit) +isis_circuit_if_bind (struct isis_circuit *circuit, struct interface *ifp) +{ + assert (circuit != NULL); + assert (ifp != NULL); + if (circuit->interface) + assert (circuit->interface == ifp); + else + circuit->interface = ifp; + if (ifp->info) + assert (ifp->info == circuit); + else + ifp->info = circuit; + isis_link_params_update (circuit, ifp); +} + +void +isis_circuit_if_unbind (struct isis_circuit *circuit, struct interface *ifp) { - circuit->interface->info = NULL; + assert (circuit != NULL); + assert (ifp != NULL); + assert (circuit->interface == ifp); + assert (ifp->info == circuit); circuit->interface = NULL; + ifp->info = NULL; +} - return; +static void +isis_circuit_update_all_srmflags (struct isis_circuit *circuit, int is_set) +{ + struct isis_area *area; + struct isis_lsp *lsp; + dnode_t *dnode, *dnode_next; + int level; + + assert (circuit); + area = circuit->area; + assert (area); + for (level = ISIS_LEVEL1; level <= ISIS_LEVEL2; level++) + { + if (level & circuit->is_type) + { + if (area->lspdb[level - 1] && + dict_count (area->lspdb[level - 1]) > 0) + { + for (dnode = dict_first (area->lspdb[level - 1]); + dnode != NULL; dnode = dnode_next) + { + dnode_next = dict_next (area->lspdb[level - 1], dnode); + lsp = dnode_get (dnode); + if (is_set) + { + ISIS_SET_FLAG (lsp->SRMflags, circuit); + } + else + { + ISIS_CLEAR_FLAG (lsp->SRMflags, circuit); + } + } + } + } + } +} + +size_t +isis_circuit_pdu_size(struct isis_circuit *circuit) +{ + return ISO_MTU(circuit); } void +isis_circuit_stream(struct isis_circuit *circuit, struct stream **stream) +{ + size_t stream_size = isis_circuit_pdu_size(circuit); + + if (!*stream) + { + *stream = stream_new(stream_size); + } + else + { + if (STREAM_SIZE(*stream) != stream_size) + stream_resize(*stream, stream_size); + stream_reset(*stream); + } +} + +int isis_circuit_up (struct isis_circuit *circuit) { + int retv; + + /* Set the flags for all the lsps of the circuit. */ + isis_circuit_update_all_srmflags (circuit, 1); + + if (circuit->state == C_STATE_UP) + return ISIS_OK; + + if (circuit->is_passive) + return ISIS_OK; + + if (circuit->area->lsp_mtu > isis_circuit_pdu_size(circuit)) + { + zlog_err("Interface MTU %zu on %s is too low to support area lsp mtu %u!", + isis_circuit_pdu_size(circuit), circuit->interface->name, + circuit->area->lsp_mtu); + isis_circuit_update_all_srmflags(circuit, 0); + return ISIS_ERROR; + } if (circuit->circ_type == CIRCUIT_T_BROADCAST) { - if (circuit->area->min_bcast_mtu == 0 || - ISO_MTU (circuit) < circuit->area->min_bcast_mtu) - circuit->area->min_bcast_mtu = ISO_MTU (circuit); + /* + * Get the Hardware Address + */ + if (circuit->interface->hw_addr_len != ETH_ALEN) + { + zlog_warn ("unsupported link layer"); + } + else + { + memcpy (circuit->u.bc.snpa, circuit->interface->hw_addr, ETH_ALEN); + } +#ifdef EXTREME_DEGUG + zlog_debug ("isis_circuit_if_add: if_id %d, isomtu %d snpa %s", + circuit->interface->ifindex, ISO_MTU (circuit), + snpa_print (circuit->u.bc.snpa)); +#endif /* EXTREME_DEBUG */ + + circuit->u.bc.adjdb[0] = list_new (); + circuit->u.bc.adjdb[1] = list_new (); + /* * ISO 10589 - 8.4.1 Enabling of broadcast circuits */ @@ -524,98 +658,182 @@ isis_circuit_up (struct isis_circuit *circuit) /* 8.4.1 a) commence sending of IIH PDUs */ - if (circuit->circuit_is_type & IS_LEVEL_1) - { - thread_add_event (master, send_lan_l1_hello, circuit, 0); - circuit->u.bc.lan_neighs[0] = list_new (); - } + if (circuit->is_type & IS_LEVEL_1) + { + thread_add_event (master, send_lan_l1_hello, circuit, 0); + circuit->u.bc.lan_neighs[0] = list_new (); + } - if (circuit->circuit_is_type & IS_LEVEL_2) - { - thread_add_event (master, send_lan_l2_hello, circuit, 0); - circuit->u.bc.lan_neighs[1] = list_new (); - } + if (circuit->is_type & IS_LEVEL_2) + { + thread_add_event (master, send_lan_l2_hello, circuit, 0); + circuit->u.bc.lan_neighs[1] = list_new (); + } /* 8.4.1 b) FIXME: solicit ES - 8.4.6 */ /* 8.4.1 c) FIXME: listen for ESH PDUs */ /* 8.4.1 d) */ /* dr election will commence in... */ - if (circuit->circuit_is_type & IS_LEVEL_1) - THREAD_TIMER_ON (master, circuit->u.bc.t_run_dr[0], isis_run_dr_l1, - circuit, 2 * circuit->hello_interval[0]); - if (circuit->circuit_is_type & IS_LEVEL_2) - THREAD_TIMER_ON (master, circuit->u.bc.t_run_dr[1], isis_run_dr_l2, - circuit, 2 * circuit->hello_interval[1]); + if (circuit->is_type & IS_LEVEL_1) + THREAD_TIMER_ON (master, circuit->u.bc.t_run_dr[0], isis_run_dr_l1, + circuit, 2 * circuit->hello_interval[0]); + if (circuit->is_type & IS_LEVEL_2) + THREAD_TIMER_ON (master, circuit->u.bc.t_run_dr[1], isis_run_dr_l2, + circuit, 2 * circuit->hello_interval[1]); } else { /* initializing the hello send threads * for a ptp IF */ + circuit->u.p2p.neighbor = NULL; thread_add_event (master, send_p2p_hello, circuit, 0); - } /* initializing PSNP timers */ - if (circuit->circuit_is_type & IS_LEVEL_1) - { - THREAD_TIMER_ON (master, circuit->t_send_psnp[0], send_l1_psnp, circuit, - isis_jitter (circuit->psnp_interval[0], PSNP_JITTER)); - } + if (circuit->is_type & IS_LEVEL_1) + THREAD_TIMER_ON (master, circuit->t_send_psnp[0], send_l1_psnp, circuit, + isis_jitter (circuit->psnp_interval[0], PSNP_JITTER)); + + if (circuit->is_type & IS_LEVEL_2) + THREAD_TIMER_ON (master, circuit->t_send_psnp[1], send_l2_psnp, circuit, + isis_jitter (circuit->psnp_interval[1], PSNP_JITTER)); - if (circuit->circuit_is_type & IS_LEVEL_2) + /* unified init for circuits; ignore warnings below this level */ + retv = isis_sock_init (circuit); + if (retv != ISIS_OK) { - THREAD_TIMER_ON (master, circuit->t_send_psnp[1], send_l2_psnp, circuit, - isis_jitter (circuit->psnp_interval[1], PSNP_JITTER)); + isis_circuit_down (circuit); + return retv; } - /* initialize the circuit streams */ - if (circuit->rcv_stream == NULL) - circuit->rcv_stream = stream_new (ISO_MTU (circuit)); - - if (circuit->snd_stream == NULL) - circuit->snd_stream = stream_new (ISO_MTU (circuit)); - - /* unified init for circuits */ - isis_sock_init (circuit); + /* initialize the circuit streams after opening connection */ + isis_circuit_stream(circuit, &circuit->rcv_stream); + isis_circuit_stream(circuit, &circuit->snd_stream); #ifdef GNU_LINUX THREAD_READ_ON (master, circuit->t_read, isis_receive, circuit, - circuit->fd); + circuit->fd); #else THREAD_TIMER_ON (master, circuit->t_read, isis_receive, circuit, - circuit->fd); + circuit->fd); #endif - return; + + circuit->lsp_queue = list_new (); + circuit->lsp_queue_last_cleared = time (NULL); + + return ISIS_OK; } void isis_circuit_down (struct isis_circuit *circuit) { - /* Cancel all active threads -- FIXME: wrong place */ - /* HT: Read thread if GNU_LINUX, TIMER thread otherwise. */ - THREAD_OFF (circuit->t_read); + if (circuit->state != C_STATE_UP) + return; + + /* Clear the flags for all the lsps of the circuit. */ + isis_circuit_update_all_srmflags (circuit, 0); + if (circuit->circ_type == CIRCUIT_T_BROADCAST) { + /* destroy neighbour lists */ + if (circuit->u.bc.lan_neighs[0]) + { + list_delete (circuit->u.bc.lan_neighs[0]); + circuit->u.bc.lan_neighs[0] = NULL; + } + if (circuit->u.bc.lan_neighs[1]) + { + list_delete (circuit->u.bc.lan_neighs[1]); + circuit->u.bc.lan_neighs[1] = NULL; + } + /* destroy adjacency databases */ + if (circuit->u.bc.adjdb[0]) + { + circuit->u.bc.adjdb[0]->del = isis_delete_adj; + list_delete (circuit->u.bc.adjdb[0]); + circuit->u.bc.adjdb[0] = NULL; + } + if (circuit->u.bc.adjdb[1]) + { + circuit->u.bc.adjdb[1]->del = isis_delete_adj; + list_delete (circuit->u.bc.adjdb[1]); + circuit->u.bc.adjdb[1] = NULL; + } + if (circuit->u.bc.is_dr[0]) + { + isis_dr_resign (circuit, 1); + circuit->u.bc.is_dr[0] = 0; + } + memset (circuit->u.bc.l1_desig_is, 0, ISIS_SYS_ID_LEN + 1); + if (circuit->u.bc.is_dr[1]) + { + isis_dr_resign (circuit, 2); + circuit->u.bc.is_dr[1] = 0; + } + memset (circuit->u.bc.l2_desig_is, 0, ISIS_SYS_ID_LEN + 1); + memset (circuit->u.bc.snpa, 0, ETH_ALEN); + THREAD_TIMER_OFF (circuit->u.bc.t_send_lan_hello[0]); THREAD_TIMER_OFF (circuit->u.bc.t_send_lan_hello[1]); THREAD_TIMER_OFF (circuit->u.bc.t_run_dr[0]); THREAD_TIMER_OFF (circuit->u.bc.t_run_dr[1]); + THREAD_TIMER_OFF (circuit->u.bc.t_refresh_pseudo_lsp[0]); + THREAD_TIMER_OFF (circuit->u.bc.t_refresh_pseudo_lsp[1]); + circuit->lsp_regenerate_pending[0] = 0; + circuit->lsp_regenerate_pending[1] = 0; } else if (circuit->circ_type == CIRCUIT_T_P2P) { + isis_delete_adj (circuit->u.p2p.neighbor); + circuit->u.p2p.neighbor = NULL; THREAD_TIMER_OFF (circuit->u.p2p.t_send_p2p_hello); } - if (circuit->t_send_psnp[0]) { - THREAD_TIMER_OFF (circuit->t_send_psnp[0]); - } - if (circuit->t_send_psnp[1]) { - THREAD_TIMER_OFF (circuit->t_send_psnp[1]); - } + /* Cancel all active threads */ + THREAD_TIMER_OFF (circuit->t_send_csnp[0]); + THREAD_TIMER_OFF (circuit->t_send_csnp[1]); + THREAD_TIMER_OFF (circuit->t_send_psnp[0]); + THREAD_TIMER_OFF (circuit->t_send_psnp[1]); + THREAD_OFF (circuit->t_read); + + if (circuit->lsp_queue) + { + circuit->lsp_queue->del = NULL; + list_delete (circuit->lsp_queue); + circuit->lsp_queue = NULL; + } + + /* send one gratuitous hello to spead up convergence */ + if (circuit->is_type & IS_LEVEL_1) + send_hello (circuit, IS_LEVEL_1); + if (circuit->is_type & IS_LEVEL_2) + send_hello (circuit, IS_LEVEL_2); + + circuit->upadjcount[0] = 0; + circuit->upadjcount[1] = 0; + /* close the socket */ - close (circuit->fd); + if (circuit->fd) + { + close (circuit->fd); + circuit->fd = 0; + } + + if (circuit->rcv_stream != NULL) + { + stream_free (circuit->rcv_stream); + circuit->rcv_stream = NULL; + } + + if (circuit->snd_stream != NULL) + { + stream_free (circuit->snd_stream); + circuit->snd_stream = NULL; + } + + thread_cancel_event (master, circuit); return; } @@ -640,1474 +858,548 @@ circuit_update_nlpids (struct isis_circuit *circuit) return; } -int -isis_interface_config_write (struct vty *vty) +void +isis_circuit_print_vty (struct isis_circuit *circuit, struct vty *vty, + char detail) { - - int write = 0; - struct listnode *node, *node2; - struct interface *ifp; - struct isis_area *area; - struct isis_circuit *c; - int i; - - for (ALL_LIST_ELEMENTS_RO (iflist, node, ifp)) - { - /* IF name */ - vty_out (vty, "interface %s%s", ifp->name, VTY_NEWLINE); - write++; - /* IF desc */ - if (ifp->desc) - { - vty_out (vty, " description %s%s", ifp->desc, VTY_NEWLINE); - write++; - } - /* ISIS Circuit */ - for (ALL_LIST_ELEMENTS_RO (isis->area_list, node2, area)) - { - c = circuit_lookup_by_ifp (ifp, area->circuit_list); - if (c) - { - if (c->ip_router) - { - vty_out (vty, " ip router isis %s%s", area->area_tag, - VTY_NEWLINE); - write++; - } -#ifdef HAVE_IPV6 - if (c->ipv6_router) - { - vty_out (vty, " ipv6 router isis %s%s", area->area_tag, - VTY_NEWLINE); - write++; - } -#endif /* HAVE_IPV6 */ - - /* ISIS - circuit type */ - if (c->circuit_is_type == IS_LEVEL_1) - { - vty_out (vty, " isis circuit-type level-1%s", VTY_NEWLINE); - write++; - } - else - { - if (c->circuit_is_type == IS_LEVEL_2) - { - vty_out (vty, " isis circuit-type level-2-only%s", - VTY_NEWLINE); - write++; - } - } - - /* ISIS - CSNP interval - FIXME: compare to cisco */ - if (c->csnp_interval[0] == c->csnp_interval[1]) - { - if (c->csnp_interval[0] != CSNP_INTERVAL) - { - vty_out (vty, " isis csnp-interval %d%s", - c->csnp_interval[0], VTY_NEWLINE); - write++; - } - } - else - { - for (i = 0; i < 2; i++) - { - if (c->csnp_interval[1] != CSNP_INTERVAL) - { - vty_out (vty, " isis csnp-interval %d level-%d%s", - c->csnp_interval[1], i + 1, VTY_NEWLINE); - write++; - } - } - } - - /* ISIS - Hello padding - Defaults to true so only display if false */ - if (c->circ_type == CIRCUIT_T_BROADCAST && !c->u.bc.pad_hellos) - { - vty_out (vty, " no isis hello padding%s", VTY_NEWLINE); - write++; - } - - /* ISIS - Hello interval - FIXME: compare to cisco */ - if (c->hello_interval[0] == c->hello_interval[1]) - { - if (c->hello_interval[0] != HELLO_INTERVAL) - { - vty_out (vty, " isis hello-interval %d%s", - c->hello_interval[0], VTY_NEWLINE); - write++; - } - } - else - { - for (i = 0; i < 2; i++) - { - if (c->hello_interval[i] != HELLO_INTERVAL) - { - if (c->hello_interval[i] == HELLO_MINIMAL) - { - vty_out (vty, - " isis hello-interval minimal level-%d%s", - i + 1, VTY_NEWLINE); - } - else - { - vty_out (vty, " isis hello-interval %d level-%d%s", - c->hello_interval[i], i + 1, VTY_NEWLINE); - } - write++; - } - } - } - - /* ISIS - Hello Multiplier */ - if (c->hello_multiplier[0] == c->hello_multiplier[1]) - { - if (c->hello_multiplier[0] != HELLO_MULTIPLIER) - { - vty_out (vty, " isis hello-multiplier %d%s", - c->hello_multiplier[0], VTY_NEWLINE); - write++; - } - } - else - { - for (i = 0; i < 2; i++) - { - if (c->hello_multiplier[i] != HELLO_MULTIPLIER) - { - vty_out (vty, " isis hello-multiplier %d level-%d%s", - c->hello_multiplier[i], i + 1, VTY_NEWLINE); - write++; - } - } - } - /* ISIS - Priority */ - if (c->circ_type == CIRCUIT_T_BROADCAST) - { - if (c->u.bc.priority[0] == c->u.bc.priority[1]) - { - if (c->u.bc.priority[0] != DEFAULT_PRIORITY) - { - vty_out (vty, " isis priority %d%s", - c->u.bc.priority[0], VTY_NEWLINE); - write++; - } - } - else - { - for (i = 0; i < 2; i++) - { - if (c->u.bc.priority[i] != DEFAULT_PRIORITY) - { - vty_out (vty, " isis priority %d level-%d%s", - c->u.bc.priority[i], i + 1, VTY_NEWLINE); - write++; - } - } - } - } - /* ISIS - Metric */ - if (c->te_metric[0] == c->te_metric[1]) - { - if (c->te_metric[0] != DEFAULT_CIRCUIT_METRICS) - { - vty_out (vty, " isis metric %d%s", c->te_metric[0], - VTY_NEWLINE); - write++; - } - } - else - { - for (i = 0; i < 2; i++) - { - if (c->te_metric[i] != DEFAULT_CIRCUIT_METRICS) - { - vty_out (vty, " isis metric %d level-%d%s", - c->te_metric[i], i + 1, VTY_NEWLINE); - write++; - } - } - } - if (c->passwd.type==ISIS_PASSWD_TYPE_HMAC_MD5) - { - vty_out (vty, " isis password md5 %s%s", c->passwd.passwd, - VTY_NEWLINE); - write++; - } - else - { - if (c->passwd.type==ISIS_PASSWD_TYPE_CLEARTXT) - { - vty_out (vty, " isis password clear %s%s", c->passwd.passwd, - VTY_NEWLINE); - write++; - } - } - - } - } - vty_out (vty, "!%s", VTY_NEWLINE); - } - - return write; -} - -DEFUN (ip_router_isis, - ip_router_isis_cmd, - "ip router isis WORD", - "Interface Internet Protocol config commands\n" - "IP router interface commands\n" - "IS-IS Routing for IP\n" - "Routing process tag\n") -{ - struct isis_circuit *c; - struct interface *ifp; - struct isis_area *area; - - ifp = (struct interface *) vty->index; - assert (ifp); - - area = isis_area_lookup (argv[0]); - - /* Prevent more than one circuit per interface */ - if (area) - c = circuit_lookup_by_ifp (ifp, area->circuit_list); - else - c = NULL; - if (c && (ifp->info != NULL)) - { -#ifdef HAVE_IPV6 - if (c->ipv6_router == 0) - { -#endif /* HAVE_IPV6 */ - /* FIXME: Find the way to warn only vty users. */ - /* vty_out (vty, "ISIS circuit is already defined%s", VTY_NEWLINE); */ - return CMD_WARNING; -#ifdef HAVE_IPV6 - } -#endif /* HAVE_IPV6 */ - } - - /* this is here for ciscopability */ - if (!area) - { - /* FIXME: Find the way to warn only vty users. */ - /* vty_out (vty, "Can't find ISIS instance %s", VTY_NEWLINE); */ - return CMD_WARNING; - } - - if (!c) - { - c = circuit_lookup_by_ifp (ifp, isis->init_circ_list); - c = isis_csm_state_change (ISIS_ENABLE, c, area); - c->interface = ifp; /* this is automatic */ - ifp->info = c; /* hardly related to the FSM */ - } - - if (!c) - return CMD_WARNING; - - c->ip_router = 1; - area->ip_circuits++; - circuit_update_nlpids (c); - - vty->node = INTERFACE_NODE; - - return CMD_SUCCESS; -} - -DEFUN (no_ip_router_isis, - no_ip_router_isis_cmd, - "no ip router isis WORD", - NO_STR - "Interface Internet Protocol config commands\n" - "IP router interface commands\n" - "IS-IS Routing for IP\n" - "Routing process tag\n") -{ - struct isis_circuit *circuit = NULL; - struct interface *ifp; - struct isis_area *area; - struct listnode *node; - - ifp = (struct interface *) vty->index; - assert (ifp); - - area = isis_area_lookup (argv[0]); - if (!area) - { - vty_out (vty, "Can't find ISIS instance %s", VTY_NEWLINE); - return CMD_WARNING; - } - for (ALL_LIST_ELEMENTS_RO (area->circuit_list, node, circuit)) - if (circuit->interface == ifp) - break; - if (!circuit) - { - vty_out (vty, "Can't find ISIS interface %s", VTY_NEWLINE); - return CMD_WARNING; - } - circuit->ip_router = 0; - area->ip_circuits--; -#ifdef HAVE_IPV6 - if (circuit->ipv6_router == 0) -#endif - isis_csm_state_change (ISIS_DISABLE, circuit, area); - - return CMD_SUCCESS; -} - -DEFUN (isis_circuit_type, - isis_circuit_type_cmd, - "isis circuit-type (level-1|level-1-2|level-2-only)", - "IS-IS commands\n" - "Configure circuit type for interface\n" - "Level-1 only adjacencies are formed\n" - "Level-1-2 adjacencies are formed\n" - "Level-2 only adjacencies are formed\n") -{ - struct isis_circuit *circuit; - struct interface *ifp; - int circuit_t; - int is_type; - - ifp = vty->index; - circuit = ifp->info; - /* UGLY - will remove l8r */ - if (circuit == NULL) - { - return CMD_WARNING; - } - - /* XXX what to do when ip_router_isis is not executed */ - if (circuit->area == NULL) - return CMD_WARNING; - - assert (circuit); - - circuit_t = string2circuit_t (argv[0]); - - if (!circuit_t) - { - vty_out (vty, "Unknown circuit-type %s", VTY_NEWLINE); - return CMD_SUCCESS; - } - - is_type = circuit->area->is_type; - if (is_type == IS_LEVEL_1_AND_2 || is_type == circuit_t) - isis_event_circuit_type_change (circuit, circuit_t); - else - { - vty_out (vty, "invalid circuit level for area %s.%s", - circuit->area->area_tag, VTY_NEWLINE); - } - - return CMD_SUCCESS; -} - -DEFUN (no_isis_circuit_type, - no_isis_circuit_type_cmd, - "no isis circuit-type (level-1|level-1-2|level-2-only)", - NO_STR - "IS-IS commands\n" - "Configure circuit type for interface\n" - "Level-1 only adjacencies are formed\n" - "Level-1-2 adjacencies are formed\n" - "Level-2 only adjacencies are formed\n") -{ - struct isis_circuit *circuit; - struct interface *ifp; - - ifp = vty->index; - circuit = ifp->info; - if (circuit == NULL) - { - return CMD_WARNING; - } - - assert (circuit); - - /* - * Set the circuits level to its default value which is that of the area - */ - isis_event_circuit_type_change (circuit, circuit->area->is_type); - - return CMD_SUCCESS; -} - -DEFUN (isis_passwd_md5, - isis_passwd_md5_cmd, - "isis password md5 WORD", - "IS-IS commands\n" - "Configure the authentication password for interface\n" - "Authentication Type\n" - "Password\n") -{ - struct isis_circuit *circuit; - struct interface *ifp; - int len; - - ifp = vty->index; - circuit = ifp->info; - if (circuit == NULL) - { - return CMD_WARNING; - } - - len = strlen (argv[0]); - if (len > 254) - { - vty_out (vty, "Too long circuit password (>254)%s", VTY_NEWLINE); - return CMD_WARNING; - } - circuit->passwd.len = len; - circuit->passwd.type = ISIS_PASSWD_TYPE_HMAC_MD5; - strncpy ((char *)circuit->passwd.passwd, argv[0], 255); - - return CMD_SUCCESS; -} - -DEFUN (isis_passwd_clear, - isis_passwd_clear_cmd, - "isis password clear WORD", - "IS-IS commands\n" - "Configure the authentication password for interface\n" - "Authentication Type\n" - "Password\n") -{ - struct isis_circuit *circuit; - struct interface *ifp; - int len; - - ifp = vty->index; - circuit = ifp->info; - if (circuit == NULL) - { - return CMD_WARNING; - } - - len = strlen (argv[0]); - if (len > 254) - { - vty_out (vty, "Too long circuit password (>254)%s", VTY_NEWLINE); - return CMD_WARNING; - } - circuit->passwd.len = len; - circuit->passwd.type = ISIS_PASSWD_TYPE_CLEARTXT; - strncpy ((char *)circuit->passwd.passwd, argv[0], 255); - - return CMD_SUCCESS; -} - -DEFUN (no_isis_passwd, - no_isis_passwd_cmd, - "no isis password", - NO_STR - "IS-IS commands\n" - "Configure the authentication password for interface\n") -{ - struct isis_circuit *circuit; - struct interface *ifp; - - ifp = vty->index; - circuit = ifp->info; - if (circuit == NULL) - { - return CMD_WARNING; - } - - memset (&circuit->passwd, 0, sizeof (struct isis_passwd)); - - return CMD_SUCCESS; -} - -DEFUN (isis_priority, - isis_priority_cmd, - "isis priority <0-127>", - "IS-IS commands\n" - "Set priority for Designated Router election\n" - "Priority value\n") -{ - struct isis_circuit *circuit; - struct interface *ifp; - int prio; - - ifp = vty->index; - circuit = ifp->info; - if (circuit == NULL) - { - return CMD_WARNING; - } - assert (circuit); - - prio = atoi (argv[0]); - - circuit->u.bc.priority[0] = prio; - circuit->u.bc.priority[1] = prio; - - return CMD_SUCCESS; -} - -DEFUN (no_isis_priority, - no_isis_priority_cmd, - "no isis priority", - NO_STR - "IS-IS commands\n" - "Set priority for Designated Router election\n") -{ - struct isis_circuit *circuit; - struct interface *ifp; - - ifp = vty->index; - circuit = ifp->info; - if (circuit == NULL) - { - return CMD_WARNING; - } - assert (circuit); - - circuit->u.bc.priority[0] = DEFAULT_PRIORITY; - circuit->u.bc.priority[1] = DEFAULT_PRIORITY; - - return CMD_SUCCESS; -} - -ALIAS (no_isis_priority, - no_isis_priority_arg_cmd, - "no isis priority <0-127>", - NO_STR - "IS-IS commands\n" - "Set priority for Designated Router election\n" - "Priority value\n") - -DEFUN (isis_priority_l1, - isis_priority_l1_cmd, - "isis priority <0-127> level-1", - "IS-IS commands\n" - "Set priority for Designated Router election\n" - "Priority value\n" - "Specify priority for level-1 routing\n") -{ - struct isis_circuit *circuit; - struct interface *ifp; - int prio; - - ifp = vty->index; - circuit = ifp->info; - if (circuit == NULL) - { - return CMD_WARNING; - } - assert (circuit); - - prio = atoi (argv[0]); - - circuit->u.bc.priority[0] = prio; - - return CMD_SUCCESS; -} - -DEFUN (no_isis_priority_l1, - no_isis_priority_l1_cmd, - "no isis priority level-1", - NO_STR - "IS-IS commands\n" - "Set priority for Designated Router election\n" - "Specify priority for level-1 routing\n") -{ - struct isis_circuit *circuit; - struct interface *ifp; - - ifp = vty->index; - circuit = ifp->info; - if (circuit == NULL) - { - return CMD_WARNING; - } - assert (circuit); - - circuit->u.bc.priority[0] = DEFAULT_PRIORITY; - - return CMD_SUCCESS; -} - -ALIAS (no_isis_priority_l1, - no_isis_priority_l1_arg_cmd, - "no isis priority <0-127> level-1", - NO_STR - "IS-IS commands\n" - "Set priority for Designated Router election\n" - "Priority value\n" - "Specify priority for level-1 routing\n") - -DEFUN (isis_priority_l2, - isis_priority_l2_cmd, - "isis priority <0-127> level-2", - "IS-IS commands\n" - "Set priority for Designated Router election\n" - "Priority value\n" - "Specify priority for level-2 routing\n") -{ - struct isis_circuit *circuit; - struct interface *ifp; - int prio; - - ifp = vty->index; - circuit = ifp->info; - if (circuit == NULL) - { - return CMD_WARNING; - } - assert (circuit); - - prio = atoi (argv[0]); - - circuit->u.bc.priority[1] = prio; - - return CMD_SUCCESS; -} - -DEFUN (no_isis_priority_l2, - no_isis_priority_l2_cmd, - "no isis priority level-2", - NO_STR - "IS-IS commands\n" - "Set priority for Designated Router election\n" - "Specify priority for level-2 routing\n") -{ - struct isis_circuit *circuit; - struct interface *ifp; - - ifp = vty->index; - circuit = ifp->info; - if (circuit == NULL) - { - return CMD_WARNING; - } - assert (circuit); - - circuit->u.bc.priority[1] = DEFAULT_PRIORITY; - - return CMD_SUCCESS; -} - -ALIAS (no_isis_priority_l2, - no_isis_priority_l2_arg_cmd, - "no isis priority <0-127> level-2", - NO_STR - "IS-IS commands\n" - "Set priority for Designated Router election\n" - "Priority value\n" - "Specify priority for level-2 routing\n") - -/* Metric command */ - DEFUN (isis_metric, - isis_metric_cmd, - "isis metric <0-16777215>", - "IS-IS commands\n" - "Set default metric for circuit\n" - "Default metric value\n") -{ - struct isis_circuit *circuit; - struct interface *ifp; - int met; - - ifp = vty->index; - circuit = ifp->info; - if (circuit == NULL) - { - return CMD_WARNING; - } - assert (circuit); - - met = atoi (argv[0]); - - circuit->te_metric[0] = met; - circuit->te_metric[1] = met; - - if (met > 63) - met = 63; - - circuit->metrics[0].metric_default = met; - circuit->metrics[1].metric_default = met; - - return CMD_SUCCESS; -} - -DEFUN (no_isis_metric, - no_isis_metric_cmd, - "no isis metric", - NO_STR - "IS-IS commands\n" - "Set default metric for circuit\n") -{ - struct isis_circuit *circuit; - struct interface *ifp; - - ifp = vty->index; - circuit = ifp->info; - if (circuit == NULL) - { - return CMD_WARNING; - } - assert (circuit); - - circuit->te_metric[0] = DEFAULT_CIRCUIT_METRICS; - circuit->te_metric[1] = DEFAULT_CIRCUIT_METRICS; - circuit->metrics[0].metric_default = DEFAULT_CIRCUIT_METRICS; - circuit->metrics[1].metric_default = DEFAULT_CIRCUIT_METRICS; - - return CMD_SUCCESS; -} - -ALIAS (no_isis_metric, - no_isis_metric_arg_cmd, - "no isis metric <0-16777215>", - NO_STR - "IS-IS commands\n" - "Set default metric for circuit\n" - "Default metric value\n") - -/* end of metrics */ -DEFUN (isis_hello_interval, - isis_hello_interval_cmd, - "isis hello-interval (<1-65535>|minimal)", - "IS-IS commands\n" - "Set Hello interval\n" - "Hello interval value\n" - "Holdtime 1 seconds, interval depends on multiplier\n") -{ - struct isis_circuit *circuit; - struct interface *ifp; - int interval; - char c; - - ifp = vty->index; - circuit = ifp->info; - if (circuit == NULL) - { - return CMD_WARNING; - } - assert (circuit); - c = *argv[0]; - if (isdigit ((int) c)) - { - interval = atoi (argv[0]); - } - else - interval = HELLO_MINIMAL; /* FIXME: should be calculated */ - - circuit->hello_interval[0] = (u_int16_t) interval; - circuit->hello_interval[1] = (u_int16_t) interval; - - return CMD_SUCCESS; -} - -DEFUN (no_isis_hello_interval, - no_isis_hello_interval_cmd, - "no isis hello-interval", - NO_STR - "IS-IS commands\n" - "Set Hello interval\n") -{ - struct isis_circuit *circuit; - struct interface *ifp; - - ifp = vty->index; - circuit = ifp->info; - if (circuit == NULL) - { - return CMD_WARNING; - } - assert (circuit); - - - circuit->hello_interval[0] = HELLO_INTERVAL; /* Default is 1 sec. */ - circuit->hello_interval[1] = HELLO_INTERVAL; - - return CMD_SUCCESS; -} - -ALIAS (no_isis_hello_interval, - no_isis_hello_interval_arg_cmd, - "no isis hello-interval (<1-65535>|minimal)", - NO_STR - "IS-IS commands\n" - "Set Hello interval\n" - "Hello interval value\n" - "Holdtime 1 second, interval depends on multiplier\n") - -DEFUN (isis_hello_interval_l1, - isis_hello_interval_l1_cmd, - "isis hello-interval (<1-65535>|minimal) level-1", - "IS-IS commands\n" - "Set Hello interval\n" - "Hello interval value\n" - "Holdtime 1 second, interval depends on multiplier\n" - "Specify hello-interval for level-1 IIHs\n") -{ - struct isis_circuit *circuit; - struct interface *ifp; - long interval; - char c; - - ifp = vty->index; - circuit = ifp->info; - if (circuit == NULL) + if (detail == ISIS_UI_LEVEL_BRIEF) { - return CMD_WARNING; + vty_out (vty, " %-12s", circuit->interface->name); + vty_out (vty, "0x%-7x", circuit->circuit_id); + vty_out (vty, "%-9s", circuit_state2string (circuit->state)); + vty_out (vty, "%-9s", circuit_type2string (circuit->circ_type)); + vty_out (vty, "%-9s", circuit_t2string (circuit->is_type)); + vty_out (vty, "%s", VTY_NEWLINE); } - assert (circuit); - c = *argv[0]; - if (isdigit ((int) c)) + if (detail == ISIS_UI_LEVEL_DETAIL) { - interval = atoi (argv[0]); - } - else - interval = HELLO_MINIMAL; + struct listnode *node; + struct prefix *ip_addr; + u_char buf[BUFSIZ]; - circuit->hello_interval[0] = (u_int16_t) interval; - - return CMD_SUCCESS; -} - -DEFUN (no_isis_hello_interval_l1, - no_isis_hello_interval_l1_cmd, - "no isis hello-interval level-1", - NO_STR - "IS-IS commands\n" - "Set Hello interval\n" - "Specify hello-interval for level-1 IIHs\n") -{ - struct isis_circuit *circuit; - struct interface *ifp; - - ifp = vty->index; - circuit = ifp->info; - if (circuit == NULL) - { - return CMD_WARNING; + vty_out (vty, " Interface: %s", circuit->interface->name); + vty_out (vty, ", State: %s", circuit_state2string (circuit->state)); + if (circuit->is_passive) + vty_out (vty, ", Passive"); + else + vty_out (vty, ", Active"); + vty_out (vty, ", Circuit Id: 0x%x", circuit->circuit_id); + vty_out (vty, "%s", VTY_NEWLINE); + vty_out (vty, " Type: %s", circuit_type2string (circuit->circ_type)); + vty_out (vty, ", Level: %s", circuit_t2string (circuit->is_type)); + if (circuit->circ_type == CIRCUIT_T_BROADCAST) + vty_out (vty, ", SNPA: %-10s", snpa_print (circuit->u.bc.snpa)); + vty_out (vty, "%s", VTY_NEWLINE); + if (circuit->is_type & IS_LEVEL_1) + { + vty_out (vty, " Level-1 Information:%s", VTY_NEWLINE); + if (circuit->area->newmetric) + vty_out (vty, " Metric: %d", circuit->te_metric[0]); + else + vty_out (vty, " Metric: %d", + circuit->metric[0]); + if (!circuit->is_passive) + { + vty_out (vty, ", Active neighbors: %u%s", + circuit->upadjcount[0], VTY_NEWLINE); + vty_out (vty, " Hello interval: %u, " + "Holddown count: %u %s%s", + circuit->hello_interval[0], + circuit->hello_multiplier[0], + (circuit->pad_hellos ? "(pad)" : "(no-pad)"), + VTY_NEWLINE); + vty_out (vty, " CNSP interval: %u, " + "PSNP interval: %u%s", + circuit->csnp_interval[0], + circuit->psnp_interval[0], VTY_NEWLINE); + if (circuit->circ_type == CIRCUIT_T_BROADCAST) + vty_out (vty, " LAN Priority: %u, %s%s", + circuit->priority[0], + (circuit->u.bc.is_dr[0] ? \ + "is DIS" : "is not DIS"), VTY_NEWLINE); + } + else + { + vty_out (vty, "%s", VTY_NEWLINE); + } + } + if (circuit->is_type & IS_LEVEL_2) + { + vty_out (vty, " Level-2 Information:%s", VTY_NEWLINE); + if (circuit->area->newmetric) + vty_out (vty, " Metric: %d", circuit->te_metric[1]); + else + vty_out (vty, " Metric: %d", + circuit->metric[1]); + if (!circuit->is_passive) + { + vty_out (vty, ", Active neighbors: %u%s", + circuit->upadjcount[1], VTY_NEWLINE); + vty_out (vty, " Hello interval: %u, " + "Holddown count: %u %s%s", + circuit->hello_interval[1], + circuit->hello_multiplier[1], + (circuit->pad_hellos ? "(pad)" : "(no-pad)"), + VTY_NEWLINE); + vty_out (vty, " CNSP interval: %u, " + "PSNP interval: %u%s", + circuit->csnp_interval[1], + circuit->psnp_interval[1], VTY_NEWLINE); + if (circuit->circ_type == CIRCUIT_T_BROADCAST) + vty_out (vty, " LAN Priority: %u, %s%s", + circuit->priority[1], + (circuit->u.bc.is_dr[1] ? \ + "is DIS" : "is not DIS"), VTY_NEWLINE); + } + else + { + vty_out (vty, "%s", VTY_NEWLINE); + } + } + if (circuit->ip_addrs && listcount (circuit->ip_addrs) > 0) + { + vty_out (vty, " IP Prefix(es):%s", VTY_NEWLINE); + for (ALL_LIST_ELEMENTS_RO (circuit->ip_addrs, node, ip_addr)) + { + prefix2str (ip_addr, (char*)buf, BUFSIZ), + vty_out (vty, " %s%s", buf, VTY_NEWLINE); + } + } + if (circuit->ipv6_link && listcount(circuit->ipv6_link) > 0) + { + vty_out(vty, " IPv6 Link-Locals:%s", VTY_NEWLINE); + for (ALL_LIST_ELEMENTS_RO(circuit->ipv6_link, node, ip_addr)) + { + prefix2str(ip_addr, (char*)buf, BUFSIZ), + vty_out(vty, " %s%s", buf, VTY_NEWLINE); + } + } + if (circuit->ipv6_non_link && listcount(circuit->ipv6_non_link) > 0) + { + vty_out(vty, " IPv6 Prefixes:%s", VTY_NEWLINE); + for (ALL_LIST_ELEMENTS_RO(circuit->ipv6_non_link, node, ip_addr)) + { + prefix2str(ip_addr, (char*)buf, BUFSIZ), + vty_out(vty, " %s%s", buf, VTY_NEWLINE); + } + } + + vty_out (vty, "%s", VTY_NEWLINE); } - assert (circuit); - - - circuit->hello_interval[0] = HELLO_INTERVAL; /* Default is 1 sec. */ - - return CMD_SUCCESS; + return; } -ALIAS (no_isis_hello_interval_l1, - no_isis_hello_interval_l1_arg_cmd, - "no isis hello-interval (<1-65535>|minimal) level-1", - NO_STR - "IS-IS commands\n" - "Set Hello interval\n" - "Hello interval value\n" - "Holdtime 1 second, interval depends on multiplier\n" - "Specify hello-interval for level-1 IIHs\n") - -DEFUN (isis_hello_interval_l2, - isis_hello_interval_l2_cmd, - "isis hello-interval (<1-65535>|minimal) level-2", - "IS-IS commands\n" - "Set Hello interval\n" - "Hello interval value\n" - "Holdtime 1 second, interval depends on multiplier\n" - "Specify hello-interval for level-2 IIHs\n") +int +isis_interface_config_write (struct vty *vty) { - struct isis_circuit *circuit; + int write = 0; + struct listnode *node, *node2; struct interface *ifp; - long interval; - char c; - - ifp = vty->index; - circuit = ifp->info; - if (circuit == NULL) - { - return CMD_WARNING; - } - assert (circuit); - - c = *argv[0]; - if (isdigit ((int) c)) - { - interval = atoi (argv[0]); - } - else - interval = HELLO_MINIMAL; - - circuit->hello_interval[1] = (u_int16_t) interval; - - return CMD_SUCCESS; -} - -DEFUN (no_isis_hello_interval_l2, - no_isis_hello_interval_l2_cmd, - "no isis hello-interval level-2", - NO_STR - "IS-IS commands\n" - "Set Hello interval\n" - "Specify hello-interval for level-2 IIHs\n") -{ + struct isis_area *area; struct isis_circuit *circuit; - struct interface *ifp; + int i; - ifp = vty->index; - circuit = ifp->info; - if (circuit == NULL) + for (ALL_LIST_ELEMENTS_RO (iflist, node, ifp)) { - return CMD_WARNING; - } - assert (circuit); - - - circuit->hello_interval[1] = HELLO_INTERVAL; /* Default is 1 sec. */ - - return CMD_SUCCESS; -} - -ALIAS (no_isis_hello_interval_l2, - no_isis_hello_interval_l2_arg_cmd, - "no isis hello-interval (<1-65535>|minimal) level-2", - NO_STR - "IS-IS commands\n" - "Set Hello interval\n" - "Hello interval value\n" - "Holdtime 1 second, interval depends on multiplier\n" - "Specify hello-interval for level-2 IIHs\n") - -DEFUN (isis_hello_multiplier, - isis_hello_multiplier_cmd, - "isis hello-multiplier <3-1000>", - "IS-IS commands\n" - "Set multiplier for Hello holding time\n" - "Hello multiplier value\n") -{ - struct isis_circuit *circuit; - struct interface *ifp; - int mult; + /* IF name */ + vty_out (vty, "interface %s%s", ifp->name, VTY_NEWLINE); + write++; + /* IF desc */ + if (ifp->desc) + { + vty_out (vty, " description %s%s", ifp->desc, VTY_NEWLINE); + write++; + } + /* ISIS Circuit */ + for (ALL_LIST_ELEMENTS_RO (isis->area_list, node2, area)) + { + circuit = circuit_lookup_by_ifp (ifp, area->circuit_list); + if (circuit == NULL) + continue; + if (circuit->ip_router) + { + vty_out (vty, " ip router isis %s%s", area->area_tag, + VTY_NEWLINE); + write++; + } + if (circuit->is_passive) + { + vty_out (vty, " isis passive%s", VTY_NEWLINE); + write++; + } + if (circuit->circ_type_config == CIRCUIT_T_P2P) + { + vty_out (vty, " isis network point-to-point%s", VTY_NEWLINE); + write++; + } +#ifdef HAVE_IPV6 + if (circuit->ipv6_router) + { + vty_out (vty, " ipv6 router isis %s%s", area->area_tag, + VTY_NEWLINE); + write++; + } +#endif /* HAVE_IPV6 */ - ifp = vty->index; - circuit = ifp->info; - if (circuit == NULL) - { - return CMD_WARNING; + /* ISIS - circuit type */ + if (circuit->is_type == IS_LEVEL_1) + { + vty_out (vty, " isis circuit-type level-1%s", VTY_NEWLINE); + write++; + } + else + { + if (circuit->is_type == IS_LEVEL_2) + { + vty_out (vty, " isis circuit-type level-2-only%s", + VTY_NEWLINE); + write++; + } + } + + /* ISIS - CSNP interval */ + if (circuit->csnp_interval[0] == circuit->csnp_interval[1]) + { + if (circuit->csnp_interval[0] != DEFAULT_CSNP_INTERVAL) + { + vty_out (vty, " isis csnp-interval %d%s", + circuit->csnp_interval[0], VTY_NEWLINE); + write++; + } + } + else + { + for (i = 0; i < 2; i++) + { + if (circuit->csnp_interval[i] != DEFAULT_CSNP_INTERVAL) + { + vty_out (vty, " isis csnp-interval %d level-%d%s", + circuit->csnp_interval[i], i + 1, VTY_NEWLINE); + write++; + } + } + } + + /* ISIS - PSNP interval */ + if (circuit->psnp_interval[0] == circuit->psnp_interval[1]) + { + if (circuit->psnp_interval[0] != DEFAULT_PSNP_INTERVAL) + { + vty_out (vty, " isis psnp-interval %d%s", + circuit->psnp_interval[0], VTY_NEWLINE); + write++; + } + } + else + { + for (i = 0; i < 2; i++) + { + if (circuit->psnp_interval[i] != DEFAULT_PSNP_INTERVAL) + { + vty_out (vty, " isis psnp-interval %d level-%d%s", + circuit->psnp_interval[i], i + 1, VTY_NEWLINE); + write++; + } + } + } + + /* ISIS - Hello padding - Defaults to true so only display if false */ + if (circuit->pad_hellos == 0) + { + vty_out (vty, " no isis hello padding%s", VTY_NEWLINE); + write++; + } + + /* ISIS - Hello interval */ + if (circuit->hello_interval[0] == circuit->hello_interval[1]) + { + if (circuit->hello_interval[0] != DEFAULT_HELLO_INTERVAL) + { + vty_out (vty, " isis hello-interval %d%s", + circuit->hello_interval[0], VTY_NEWLINE); + write++; + } + } + else + { + for (i = 0; i < 2; i++) + { + if (circuit->hello_interval[i] != DEFAULT_HELLO_INTERVAL) + { + vty_out (vty, " isis hello-interval %d level-%d%s", + circuit->hello_interval[i], i + 1, VTY_NEWLINE); + write++; + } + } + } + + /* ISIS - Hello Multiplier */ + if (circuit->hello_multiplier[0] == circuit->hello_multiplier[1]) + { + if (circuit->hello_multiplier[0] != DEFAULT_HELLO_MULTIPLIER) + { + vty_out (vty, " isis hello-multiplier %d%s", + circuit->hello_multiplier[0], VTY_NEWLINE); + write++; + } + } + else + { + for (i = 0; i < 2; i++) + { + if (circuit->hello_multiplier[i] != DEFAULT_HELLO_MULTIPLIER) + { + vty_out (vty, " isis hello-multiplier %d level-%d%s", + circuit->hello_multiplier[i], i + 1, + VTY_NEWLINE); + write++; + } + } + } + + /* ISIS - Priority */ + if (circuit->priority[0] == circuit->priority[1]) + { + if (circuit->priority[0] != DEFAULT_PRIORITY) + { + vty_out (vty, " isis priority %d%s", + circuit->priority[0], VTY_NEWLINE); + write++; + } + } + else + { + for (i = 0; i < 2; i++) + { + if (circuit->priority[i] != DEFAULT_PRIORITY) + { + vty_out (vty, " isis priority %d level-%d%s", + circuit->priority[i], i + 1, VTY_NEWLINE); + write++; + } + } + } + + /* ISIS - Metric */ + if (circuit->te_metric[0] == circuit->te_metric[1]) + { + if (circuit->te_metric[0] != DEFAULT_CIRCUIT_METRIC) + { + vty_out (vty, " isis metric %d%s", circuit->te_metric[0], + VTY_NEWLINE); + write++; + } + } + else + { + for (i = 0; i < 2; i++) + { + if (circuit->te_metric[i] != DEFAULT_CIRCUIT_METRIC) + { + vty_out (vty, " isis metric %d level-%d%s", + circuit->te_metric[i], i + 1, VTY_NEWLINE); + write++; + } + } + } + if (circuit->passwd.type == ISIS_PASSWD_TYPE_HMAC_MD5) + { + vty_out (vty, " isis password md5 %s%s", circuit->passwd.passwd, + VTY_NEWLINE); + write++; + } + else if (circuit->passwd.type == ISIS_PASSWD_TYPE_CLEARTXT) + { + vty_out (vty, " isis password clear %s%s", circuit->passwd.passwd, + VTY_NEWLINE); + write++; + } + } + vty_out (vty, "!%s", VTY_NEWLINE); } - assert (circuit); - - mult = atoi (argv[0]); - - circuit->hello_multiplier[0] = (u_int16_t) mult; - circuit->hello_multiplier[1] = (u_int16_t) mult; - return CMD_SUCCESS; + return write; } -DEFUN (no_isis_hello_multiplier, - no_isis_hello_multiplier_cmd, - "no isis hello-multiplier", - NO_STR - "IS-IS commands\n" - "Set multiplier for Hello holding time\n") +struct isis_circuit * +isis_circuit_create (struct isis_area *area, struct interface *ifp) { - struct isis_circuit *circuit; - struct interface *ifp; - - ifp = vty->index; - circuit = ifp->info; - if (circuit == NULL) - { - return CMD_WARNING; - } - assert (circuit); - - circuit->hello_multiplier[0] = HELLO_MULTIPLIER; - circuit->hello_multiplier[1] = HELLO_MULTIPLIER; - - return CMD_SUCCESS; + struct isis_circuit *circuit = circuit_scan_by_ifp (ifp); + if (circuit && circuit->area) + return NULL; + circuit = isis_csm_state_change (ISIS_ENABLE, circuit, area); + if (circuit->state != C_STATE_CONF && circuit->state != C_STATE_UP) + return circuit; + isis_circuit_if_bind (circuit, ifp); + return circuit; } -ALIAS (no_isis_hello_multiplier, - no_isis_hello_multiplier_arg_cmd, - "no isis hello-multiplier <3-1000>", - NO_STR - "IS-IS commands\n" - "Set multiplier for Hello holding time\n" - "Hello multiplier value\n") - -DEFUN (isis_hello_multiplier_l1, - isis_hello_multiplier_l1_cmd, - "isis hello-multiplier <3-1000> level-1", - "IS-IS commands\n" - "Set multiplier for Hello holding time\n" - "Hello multiplier value\n" - "Specify hello multiplier for level-1 IIHs\n") +void +isis_circuit_af_set (struct isis_circuit *circuit, bool ip_router, bool ipv6_router) { - struct isis_circuit *circuit; - struct interface *ifp; - int mult; + struct isis_area *area = circuit->area; + bool change = circuit->ip_router != ip_router || circuit->ipv6_router != ipv6_router; + bool was_enabled = !!circuit->area; - ifp = vty->index; - circuit = ifp->info; - if (circuit == NULL) - { - return CMD_WARNING; - } - assert (circuit); - - mult = atoi (argv[0]); - - circuit->hello_multiplier[0] = (u_int16_t) mult; - - return CMD_SUCCESS; -} + area->ip_circuits += ip_router - circuit->ip_router; + area->ipv6_circuits += ipv6_router - circuit->ipv6_router; + circuit->ip_router = ip_router; + circuit->ipv6_router = ipv6_router; -DEFUN (no_isis_hello_multiplier_l1, - no_isis_hello_multiplier_l1_cmd, - "no isis hello-multiplier level-1", - NO_STR - "IS-IS commands\n" - "Set multiplier for Hello holding time\n" - "Specify hello multiplier for level-1 IIHs\n") -{ - struct isis_circuit *circuit; - struct interface *ifp; - - ifp = vty->index; - circuit = ifp->info; - if (circuit == NULL) - { - return CMD_WARNING; - } - assert (circuit); + if (!change) + return; - circuit->hello_multiplier[0] = HELLO_MULTIPLIER; + circuit_update_nlpids (circuit); - return CMD_SUCCESS; + if (!ip_router && !ipv6_router) + isis_csm_state_change (ISIS_DISABLE, circuit, area); + else if (!was_enabled) + isis_csm_state_change (ISIS_ENABLE, circuit, area); + else + lsp_regenerate_schedule(circuit->area, circuit->is_type, 0); } -ALIAS (no_isis_hello_multiplier_l1, - no_isis_hello_multiplier_l1_arg_cmd, - "no isis hello-multiplier <3-1000> level-1", - NO_STR - "IS-IS commands\n" - "Set multiplier for Hello holding time\n" - "Hello multiplier value\n" - "Specify hello multiplier for level-1 IIHs\n") - -DEFUN (isis_hello_multiplier_l2, - isis_hello_multiplier_l2_cmd, - "isis hello-multiplier <3-1000> level-2", - "IS-IS commands\n" - "Set multiplier for Hello holding time\n" - "Hello multiplier value\n" - "Specify hello multiplier for level-2 IIHs\n") +int +isis_circuit_passive_set (struct isis_circuit *circuit, bool passive) { - struct isis_circuit *circuit; - struct interface *ifp; - int mult; - - ifp = vty->index; - circuit = ifp->info; - if (circuit == NULL) - { - return CMD_WARNING; - } - assert (circuit); - - mult = atoi (argv[0]); + if (circuit->is_passive == passive) + return 0; - circuit->hello_multiplier[1] = (u_int16_t) mult; - - return CMD_SUCCESS; -} - -DEFUN (no_isis_hello_multiplier_l2, - no_isis_hello_multiplier_l2_cmd, - "no isis hello-multiplier level-2", - NO_STR - "IS-IS commands\n" - "Set multiplier for Hello holding time\n" - "Specify hello multiplier for level-2 IIHs\n") -{ - struct isis_circuit *circuit; - struct interface *ifp; + if (if_is_loopback (circuit->interface) && !passive) + return -1; - ifp = vty->index; - circuit = ifp->info; - if (circuit == NULL) + if (circuit->state != C_STATE_UP) { - return CMD_WARNING; + circuit->is_passive = passive; } - assert (circuit); - - circuit->hello_multiplier[1] = HELLO_MULTIPLIER; - - return CMD_SUCCESS; -} - -ALIAS (no_isis_hello_multiplier_l2, - no_isis_hello_multiplier_l2_arg_cmd, - "no isis hello-multiplier <3-1000> level-2", - NO_STR - "IS-IS commands\n" - "Set multiplier for Hello holding time\n" - "Hello multiplier value\n" - "Specify hello multiplier for level-2 IIHs\n") - -DEFUN (isis_hello, - isis_hello_cmd, - "isis hello padding", - "IS-IS commands\n" - "Add padding to IS-IS hello packets\n" - "Pad hello packets\n" - "\n") -{ - struct interface *ifp; - struct isis_circuit *circuit; - - ifp = vty->index; - circuit = ifp->info; - if (circuit == NULL) + else { - return CMD_WARNING; + struct isis_area *area = circuit->area; + isis_csm_state_change (ISIS_DISABLE, circuit, area); + circuit->is_passive = passive; + isis_csm_state_change (ISIS_ENABLE, circuit, area); } - assert (circuit); - - circuit->u.bc.pad_hellos = 1; - return CMD_SUCCESS; + return 0; } -DEFUN (no_isis_hello, - no_isis_hello_cmd, - "no isis hello padding", - NO_STR - "IS-IS commands\n" - "Add padding to IS-IS hello packets\n" - "Pad hello packets\n" - "\n") +int +isis_circuit_metric_set (struct isis_circuit *circuit, int level, int metric) { - struct isis_circuit *circuit; - struct interface *ifp; - - ifp = vty->index; - circuit = ifp->info; - if (circuit == NULL) - { - return CMD_WARNING; - } - assert (circuit); - - circuit->u.bc.pad_hellos = 0; - - return CMD_SUCCESS; + assert (level == IS_LEVEL_1 || level == IS_LEVEL_2); + if (metric > MAX_WIDE_LINK_METRIC) + return -1; + if (circuit->area && circuit->area->oldmetric + && metric > MAX_NARROW_LINK_METRIC) + return -1; + + circuit->te_metric[level - 1] = metric; + circuit->metric[level - 1] = metric; + + if (circuit->area) + lsp_regenerate_schedule (circuit->area, level, 0); + return 0; } -DEFUN (csnp_interval, - csnp_interval_cmd, - "isis csnp-interval <0-65535>", - "IS-IS commands\n" - "Set CSNP interval in seconds\n" - "CSNP interval value\n") +int +isis_circuit_passwd_unset (struct isis_circuit *circuit) { - struct isis_circuit *circuit; - struct interface *ifp; - unsigned long interval; - - ifp = vty->index; - circuit = ifp->info; - if (circuit == NULL) - { - return CMD_WARNING; - } - assert (circuit); - - interval = atol (argv[0]); - - circuit->csnp_interval[0] = (u_int16_t) interval; - circuit->csnp_interval[1] = (u_int16_t) interval; - - return CMD_SUCCESS; + memset(&circuit->passwd, 0, sizeof(circuit->passwd)); + return 0; } -DEFUN (no_csnp_interval, - no_csnp_interval_cmd, - "no isis csnp-interval", - NO_STR - "IS-IS commands\n" - "Set CSNP interval in seconds\n") +static int +isis_circuit_passwd_set (struct isis_circuit *circuit, u_char passwd_type, const char *passwd) { - struct isis_circuit *circuit; - struct interface *ifp; + int len; - ifp = vty->index; - circuit = ifp->info; - if (circuit == NULL) - { - return CMD_WARNING; - } - assert (circuit); + if (!passwd) + return -1; - circuit->csnp_interval[0] = CSNP_INTERVAL; - circuit->csnp_interval[1] = CSNP_INTERVAL; + len = strlen(passwd); + if (len > 254) + return -1; - return CMD_SUCCESS; + circuit->passwd.len = len; + strncpy((char *)circuit->passwd.passwd, passwd, 255); + circuit->passwd.type = passwd_type; + return 0; } -ALIAS (no_csnp_interval, - no_csnp_interval_arg_cmd, - "no isis csnp-interval <0-65535>", - NO_STR - "IS-IS commands\n" - "Set CSNP interval in seconds\n" - "CSNP interval value\n") - -DEFUN (csnp_interval_l1, - csnp_interval_l1_cmd, - "isis csnp-interval <0-65535> level-1", - "IS-IS commands\n" - "Set CSNP interval in seconds\n" - "CSNP interval value\n" - "Specify interval for level-1 CSNPs\n") +int +isis_circuit_passwd_cleartext_set (struct isis_circuit *circuit, const char *passwd) { - struct isis_circuit *circuit; - struct interface *ifp; - unsigned long interval; - - ifp = vty->index; - circuit = ifp->info; - if (circuit == NULL) - { - return CMD_WARNING; - } - assert (circuit); - - interval = atol (argv[0]); - - circuit->csnp_interval[0] = (u_int16_t) interval; - - return CMD_SUCCESS; + return isis_circuit_passwd_set (circuit, ISIS_PASSWD_TYPE_CLEARTXT, passwd); } -DEFUN (no_csnp_interval_l1, - no_csnp_interval_l1_cmd, - "no isis csnp-interval level-1", - NO_STR - "IS-IS commands\n" - "Set CSNP interval in seconds\n" - "Specify interval for level-1 CSNPs\n") +int +isis_circuit_passwd_hmac_md5_set (struct isis_circuit *circuit, const char *passwd) { - struct isis_circuit *circuit; - struct interface *ifp; - - ifp = vty->index; - circuit = ifp->info; - if (circuit == NULL) - { - return CMD_WARNING; - } - assert (circuit); - - circuit->csnp_interval[0] = CSNP_INTERVAL; - - return CMD_SUCCESS; + return isis_circuit_passwd_set (circuit, ISIS_PASSWD_TYPE_HMAC_MD5, passwd); } +struct cmd_node interface_node = { + INTERFACE_NODE, + "%s(config-if)# ", + 1, +}; -ALIAS (no_csnp_interval_l1, - no_csnp_interval_l1_arg_cmd, - "no isis csnp-interval <0-65535> level-1", - NO_STR - "IS-IS commands\n" - "Set CSNP interval in seconds\n" - "CSNP interval value\n" - "Specify interval for level-1 CSNPs\n") - -DEFUN (csnp_interval_l2, - csnp_interval_l2_cmd, - "isis csnp-interval <0-65535> level-2", - "IS-IS commands\n" - "Set CSNP interval in seconds\n" - "CSNP interval value\n" - "Specify interval for level-2 CSNPs\n") +int +isis_circuit_circ_type_set(struct isis_circuit *circuit, int circ_type) { - struct isis_circuit *circuit; - struct interface *ifp; - unsigned long interval; - - ifp = vty->index; - circuit = ifp->info; - if (circuit == NULL) - { - return CMD_WARNING; + /* Changing the network type to/of loopback or unknown interfaces + * is not supported. */ + if (circ_type == CIRCUIT_T_UNKNOWN + || circ_type == CIRCUIT_T_LOOPBACK + || circuit->circ_type == CIRCUIT_T_LOOPBACK) + { + if (circuit->circ_type != circ_type) + return -1; + else + return 0; } - assert (circuit); - - interval = atol (argv[0]); - circuit->csnp_interval[1] = (u_int16_t) interval; - - return CMD_SUCCESS; -} - -DEFUN (no_csnp_interval_l2, - no_csnp_interval_l2_cmd, - "no isis csnp-interval level-2", - NO_STR - "IS-IS commands\n" - "Set CSNP interval in seconds\n" - "Specify interval for level-2 CSNPs\n") -{ - struct isis_circuit *circuit; - struct interface *ifp; + if (circuit->circ_type == circ_type) + return 0; - ifp = vty->index; - circuit = ifp->info; - if (circuit == NULL) + if (circuit->state != C_STATE_UP) { - return CMD_WARNING; + circuit->circ_type = circ_type; + circuit->circ_type_config = circ_type; } - assert (circuit); - - circuit->csnp_interval[1] = CSNP_INTERVAL; - - return CMD_SUCCESS; -} - -ALIAS (no_csnp_interval_l2, - no_csnp_interval_l2_arg_cmd, - "no isis csnp-interval <0-65535> level-2", - NO_STR - "IS-IS commands\n" - "Set CSNP interval in seconds\n" - "CSNP interval value\n" - "Specify interval for level-2 CSNPs\n") - -#ifdef HAVE_IPV6 -DEFUN (ipv6_router_isis, - ipv6_router_isis_cmd, - "ipv6 router isis WORD", - "IPv6 interface subcommands\n" - "IPv6 Router interface commands\n" - "IS-IS Routing for IPv6\n" - "Routing process tag\n") -{ - struct isis_circuit *c; - struct interface *ifp; - struct isis_area *area; - - ifp = (struct interface *) vty->index; - assert (ifp); - - area = isis_area_lookup (argv[0]); - - /* Prevent more than one circuit per interface */ - if (area) - c = circuit_lookup_by_ifp (ifp, area->circuit_list); else - c = NULL; - - if (c && (ifp->info != NULL)) - { - if (c->ipv6_router == 1) - { - vty_out (vty, "ISIS circuit is already defined for IPv6%s", - VTY_NEWLINE); - return CMD_WARNING; - } - } - - /* this is here for ciscopability */ - if (!area) - { - vty_out (vty, "Can't find ISIS instance %s", VTY_NEWLINE); - return CMD_WARNING; - } - - if (!c) { - c = circuit_lookup_by_ifp (ifp, isis->init_circ_list); - c = isis_csm_state_change (ISIS_ENABLE, c, area); - c->interface = ifp; - ifp->info = c; - } - - if (!c) - return CMD_WARNING; - - c->ipv6_router = 1; - area->ipv6_circuits++; - circuit_update_nlpids (c); - - vty->node = INTERFACE_NODE; - - return CMD_SUCCESS; -} - -DEFUN (no_ipv6_router_isis, - no_ipv6_router_isis_cmd, - "no ipv6 router isis WORD", - NO_STR - "IPv6 interface subcommands\n" - "IPv6 Router interface commands\n" - "IS-IS Routing for IPv6\n" - "Routing process tag\n") -{ - struct isis_circuit *c; - struct interface *ifp; - struct isis_area *area; - - ifp = (struct interface *) vty->index; - /* UGLY - will remove l8r - if (circuit == NULL) { - return CMD_WARNING; - } */ - assert (ifp); + struct isis_area *area = circuit->area; + if (circ_type == CIRCUIT_T_BROADCAST + && !if_is_broadcast(circuit->interface)) + return -1; - area = isis_area_lookup (argv[0]); - if (!area) - { - vty_out (vty, "Can't find ISIS instance %s", VTY_NEWLINE); - return CMD_WARNING; + isis_csm_state_change(ISIS_DISABLE, circuit, area); + circuit->circ_type = circ_type; + circuit->circ_type_config = circ_type; + isis_csm_state_change(ISIS_ENABLE, circuit, area); } - - c = circuit_lookup_by_ifp (ifp, area->circuit_list); - if (!c) - return CMD_WARNING; - - c->ipv6_router = 0; - area->ipv6_circuits--; - if (c->ip_router == 0) - isis_csm_state_change (ISIS_DISABLE, c, area); - - return CMD_SUCCESS; + return 0; } -#endif /* HAVE_IPV6 */ - -static struct cmd_node interface_node = { - INTERFACE_NODE, - "%s(config-if)# ", - 1, -}; int isis_if_new_hook (struct interface *ifp) { -/* FIXME: Discuss if the circuit should be created here - ifp->info = XMALLOC (MTYPE_ISIS_IF_INFO, sizeof (struct isis_if_info)); */ - ifp->info = NULL; return 0; } int isis_if_delete_hook (struct interface *ifp) { -/* FIXME: Discuss if the circuit should be created here - XFREE (MTYPE_ISIS_IF_INFO, ifp->info);*/ - ifp->info = NULL; + struct isis_circuit *circuit; + /* Clean up the circuit data */ + if (ifp && ifp->info) + { + circuit = ifp->info; + isis_csm_state_change (IF_DOWN_FROM_Z, circuit, circuit->area); + isis_csm_state_change (ISIS_DISABLE, circuit, circuit->area); + } + return 0; } @@ -2115,76 +1407,17 @@ void isis_circuit_init () { /* Initialize Zebra interface data structure */ - if_init (); if_add_hook (IF_NEW_HOOK, isis_if_new_hook); if_add_hook (IF_DELETE_HOOK, isis_if_delete_hook); /* Install interface node */ install_node (&interface_node, isis_interface_config_write); install_element (CONFIG_NODE, &interface_cmd); + install_element (CONFIG_NODE, &no_interface_cmd); install_default (INTERFACE_NODE); install_element (INTERFACE_NODE, &interface_desc_cmd); install_element (INTERFACE_NODE, &no_interface_desc_cmd); - install_element (INTERFACE_NODE, &ip_router_isis_cmd); - install_element (INTERFACE_NODE, &no_ip_router_isis_cmd); - - install_element (INTERFACE_NODE, &isis_circuit_type_cmd); - install_element (INTERFACE_NODE, &no_isis_circuit_type_cmd); - - install_element (INTERFACE_NODE, &isis_passwd_clear_cmd); - install_element (INTERFACE_NODE, &isis_passwd_md5_cmd); - install_element (INTERFACE_NODE, &no_isis_passwd_cmd); - - install_element (INTERFACE_NODE, &isis_priority_cmd); - install_element (INTERFACE_NODE, &no_isis_priority_cmd); - install_element (INTERFACE_NODE, &no_isis_priority_arg_cmd); - install_element (INTERFACE_NODE, &isis_priority_l1_cmd); - install_element (INTERFACE_NODE, &no_isis_priority_l1_cmd); - install_element (INTERFACE_NODE, &no_isis_priority_l1_arg_cmd); - install_element (INTERFACE_NODE, &isis_priority_l2_cmd); - install_element (INTERFACE_NODE, &no_isis_priority_l2_cmd); - install_element (INTERFACE_NODE, &no_isis_priority_l2_arg_cmd); - - install_element (INTERFACE_NODE, &isis_metric_cmd); - install_element (INTERFACE_NODE, &no_isis_metric_cmd); - install_element (INTERFACE_NODE, &no_isis_metric_arg_cmd); - - install_element (INTERFACE_NODE, &isis_hello_interval_cmd); - install_element (INTERFACE_NODE, &no_isis_hello_interval_cmd); - install_element (INTERFACE_NODE, &no_isis_hello_interval_arg_cmd); - install_element (INTERFACE_NODE, &isis_hello_interval_l1_cmd); - install_element (INTERFACE_NODE, &no_isis_hello_interval_l1_cmd); - install_element (INTERFACE_NODE, &no_isis_hello_interval_l1_arg_cmd); - install_element (INTERFACE_NODE, &isis_hello_interval_l2_cmd); - install_element (INTERFACE_NODE, &no_isis_hello_interval_l2_cmd); - install_element (INTERFACE_NODE, &no_isis_hello_interval_l2_arg_cmd); - - install_element (INTERFACE_NODE, &isis_hello_multiplier_cmd); - install_element (INTERFACE_NODE, &no_isis_hello_multiplier_cmd); - install_element (INTERFACE_NODE, &no_isis_hello_multiplier_arg_cmd); - install_element (INTERFACE_NODE, &isis_hello_multiplier_l1_cmd); - install_element (INTERFACE_NODE, &no_isis_hello_multiplier_l1_cmd); - install_element (INTERFACE_NODE, &no_isis_hello_multiplier_l1_arg_cmd); - install_element (INTERFACE_NODE, &isis_hello_multiplier_l2_cmd); - install_element (INTERFACE_NODE, &no_isis_hello_multiplier_l2_cmd); - install_element (INTERFACE_NODE, &no_isis_hello_multiplier_l2_arg_cmd); - - install_element (INTERFACE_NODE, &isis_hello_cmd); - install_element (INTERFACE_NODE, &no_isis_hello_cmd); - install_element (INTERFACE_NODE, &csnp_interval_cmd); - install_element (INTERFACE_NODE, &no_csnp_interval_cmd); - install_element (INTERFACE_NODE, &no_csnp_interval_arg_cmd); - install_element (INTERFACE_NODE, &csnp_interval_l1_cmd); - install_element (INTERFACE_NODE, &no_csnp_interval_l1_cmd); - install_element (INTERFACE_NODE, &no_csnp_interval_l1_arg_cmd); - install_element (INTERFACE_NODE, &csnp_interval_l2_cmd); - install_element (INTERFACE_NODE, &no_csnp_interval_l2_cmd); - install_element (INTERFACE_NODE, &no_csnp_interval_l2_arg_cmd); - -#ifdef HAVE_IPV6 - install_element (INTERFACE_NODE, &ipv6_router_isis_cmd); - install_element (INTERFACE_NODE, &no_ipv6_router_isis_cmd); -#endif + isis_vty_init (); } diff --git a/isisd/isis_circuit.h b/isisd/isis_circuit.h index f32d1ddae..9ada1e26a 100644 --- a/isisd/isis_circuit.h +++ b/isisd/isis_circuit.h @@ -23,6 +23,12 @@ #ifndef ISIS_CIRCUIT_H #define ISIS_CIRCUIT_H +#include "vty.h" +#include "if.h" + +#include "isis_constants.h" +#include "isis_common.h" + #define CIRCUIT_MAX 255 struct password @@ -52,8 +58,6 @@ struct isis_bcast_info u_char l1_desig_is[ISIS_SYS_ID_LEN + 1]; /* level-1 DR */ u_char l2_desig_is[ISIS_SYS_ID_LEN + 1]; /* level-2 DR */ struct thread *t_refresh_pseudo_lsp[2]; /* refresh pseudo-node LSPs */ - int pad_hellos; /* add padding to Hello PDUs ? */ - u_char priority[2]; /* l1/2 IS Priority */ }; struct isis_p2p_info @@ -65,7 +69,6 @@ struct isis_p2p_info struct isis_circuit { int state; - int connected; u_char circuit_id; /* l1/l2 p2p/bcast CircuitID */ struct isis_area *area; /* back pointer to the area */ struct interface *interface; /* interface info from z */ @@ -79,56 +82,55 @@ struct isis_circuit struct thread *t_send_csnp[2]; struct thread *t_send_psnp[2]; struct list *lsp_queue; /* LSPs to be txed (both levels) */ + time_t lsp_queue_last_cleared;/* timestamp used to enforce transmit interval; + * for scalability, use one timestamp per + * circuit, instead of one per lsp per circuit + */ /* there is no real point in two streams, just for programming kicker */ int (*rx) (struct isis_circuit * circuit, u_char * ssnpa); struct stream *rcv_stream; /* Stream for receiving */ int (*tx) (struct isis_circuit * circuit, int level); struct stream *snd_stream; /* Stream for sending */ int idx; /* idx in S[RM|SN] flags */ -#define CIRCUIT_T_BROADCAST 0 -#define CIRCUIT_T_P2P 1 -#define CIRCUIT_T_STATIC_IN 2 -#define CIRCUIT_T_STATIC_OUT 3 -#define CIRCUIT_T_DA 4 +#define CIRCUIT_T_UNKNOWN 0 +#define CIRCUIT_T_BROADCAST 1 +#define CIRCUIT_T_P2P 2 +#define CIRCUIT_T_LOOPBACK 3 int circ_type; /* type of the physical interface */ + int circ_type_config; /* config type of the physical interface */ union { struct isis_bcast_info bc; struct isis_p2p_info p2p; } u; + u_char priority[2]; /* l1/2 IS configured priority */ + int pad_hellos; /* add padding to Hello PDUs ? */ char ext_domain; /* externalDomain (boolean) */ + int lsp_regenerate_pending[ISIS_LEVELS]; /* * Configurables */ struct isis_passwd passwd; /* Circuit rx/tx password */ - long lsp_interval; - int manual_l2_only; /* manualL2OnlyMode (boolean) */ - int circuit_is_type; /* circuit is type == level of circuit - * diffrenciated from circuit type (media) */ + int is_type; /* circuit is type == level of circuit + * differentiated from circuit type (media) */ u_int32_t hello_interval[2]; /* l1HelloInterval in msecs */ u_int16_t hello_multiplier[2]; /* l1HelloMultiplier */ u_int16_t csnp_interval[2]; /* level-1 csnp-interval in seconds */ u_int16_t psnp_interval[2]; /* level-1 psnp-interval in seconds */ - struct metric metrics[2]; /* l1XxxMetric */ + u_int8_t metric[2]; u_int32_t te_metric[2]; - struct password *c_rx_passwds; /* circuitReceivePasswords */ - struct password *c_tc_passwd; /* circuitTransmitPassword */ + struct mpls_te_circuit *mtc; /* Support for MPLS-TE parameters - see isis_te.[c,h] */ int ip_router; /* Route IP ? */ + int is_passive; /* Is Passive ? */ struct list *ip_addrs; /* our IP addresses */ #ifdef HAVE_IPV6 int ipv6_router; /* Route IPv6 ? */ struct list *ipv6_link; /* our link local IPv6 addresses */ struct list *ipv6_non_link; /* our non-link local IPv6 addresses */ #endif /* HAVE_IPV6 */ - /* - * RFC 2973 IS-IS Mesh Groups - */ -#define MESH_INACTIVE 0 -#define MESH_BLOCKED 1 -#define MESH_SET 2 - int mesh_enabled; /* meshGroupEnabled */ - u_int16_t mesh_group; /* meshGroup */ u_int16_t upadjcount[2]; +#define ISIS_CIRCUIT_FLAPPED_AFTER_SPF 0x01 + u_char flags; /* * Counters as in 10589--11.2.5.9 */ @@ -142,25 +144,44 @@ struct isis_circuit void isis_circuit_init (void); struct isis_circuit *isis_circuit_new (void); +void isis_circuit_del (struct isis_circuit *circuit); struct isis_circuit *circuit_lookup_by_ifp (struct interface *ifp, struct list *list); struct isis_circuit *circuit_scan_by_ifp (struct interface *ifp); -void isis_circuit_del (struct isis_circuit *circuit); void isis_circuit_configure (struct isis_circuit *circuit, struct isis_area *area); -void isis_circuit_up (struct isis_circuit *circuit); void isis_circuit_deconfigure (struct isis_circuit *circuit, struct isis_area *area); - -int isis_circuit_destroy (struct isis_circuit *circuit); void isis_circuit_if_add (struct isis_circuit *circuit, struct interface *ifp); -void isis_circuit_if_del (struct isis_circuit *circuit); -void circuit_update_nlpids (struct isis_circuit *circuit); -void isis_circuit_update_params (struct isis_circuit *circuit, - struct interface *ifp); +void isis_circuit_if_del (struct isis_circuit *circuit, + struct interface *ifp); +void isis_circuit_if_bind (struct isis_circuit *circuit, + struct interface *ifp); +void isis_circuit_if_unbind (struct isis_circuit *circuit, + struct interface *ifp); void isis_circuit_add_addr (struct isis_circuit *circuit, struct connected *conn); void isis_circuit_del_addr (struct isis_circuit *circuit, struct connected *conn); +int isis_circuit_up (struct isis_circuit *circuit); +void isis_circuit_down (struct isis_circuit *); +void circuit_update_nlpids (struct isis_circuit *circuit); +void isis_circuit_print_vty (struct isis_circuit *circuit, struct vty *vty, + char detail); +size_t isis_circuit_pdu_size(struct isis_circuit *circuit); +void isis_circuit_stream(struct isis_circuit *circuit, struct stream **stream); + +struct isis_circuit *isis_circuit_create (struct isis_area *area, struct interface *ifp); +void isis_circuit_af_set (struct isis_circuit *circuit, bool ip_router, bool ipv6_router); +int isis_circuit_passive_set (struct isis_circuit *circuit, bool passive); +void isis_circuit_is_type_set (struct isis_circuit *circuit, int is_type); +int isis_circuit_circ_type_set (struct isis_circuit *circuit, int circ_type); + +int isis_circuit_metric_set (struct isis_circuit *circuit, int level, int metric); + +int isis_circuit_passwd_unset (struct isis_circuit *circuit); +int isis_circuit_passwd_cleartext_set (struct isis_circuit *circuit, const char *passwd); +int isis_circuit_passwd_hmac_md5_set (struct isis_circuit *circuit, const char *passwd); + #endif /* _ZEBRA_ISIS_CIRCUIT_H */ diff --git a/isisd/isis_common.h b/isisd/isis_common.h index 334d33940..d158961b9 100644 --- a/isisd/isis_common.h +++ b/isisd/isis_common.h @@ -21,6 +21,9 @@ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +#ifndef ISIS_COMMON_H +#define ISIS_COMMON_H + /* * Area Address */ @@ -65,11 +68,4 @@ struct nlpids u_char nlpids[4]; /* FIXME: enough ? */ }; -/* - * Flags structure for SSN and SRM flags - */ -struct flags -{ - int maxindex; - struct list *free_idcs; -}; +#endif diff --git a/isisd/isis_constants.h b/isisd/isis_constants.h index 1b75ba6b1..8b21894e4 100644 --- a/isisd/isis_constants.h +++ b/isisd/isis_constants.h @@ -27,18 +27,19 @@ * Architectural constant values from p. 35 of ISO/IEC 10589 */ -#define MAX_LINK_METRIC 63 -#define MAX_PATH_METRIC 1023 +#define MAX_NARROW_LINK_METRIC 63 +#define MAX_NARROW_PATH_METRIC 1023 +#define MAX_WIDE_LINK_METRIC 0x00FFFFFF /* RFC4444 */ +#define MAX_WIDE_PATH_METRIC 0xFE000000 /* RFC3787 */ #define ISO_SAP 0xFE #define INTRADOMAIN_ROUTEING_SELECTOR 0 #define SEQUENCE_MODULUS 4294967296 -#define RECEIVE_LSP_BUFFER_SIZE 1492 /* * implementation specific jitter values */ -#define IIH_JITTER 25 /* % */ +#define IIH_JITTER 10 /* % */ #define MAX_AGE_JITTER 5 /* % */ #define MAX_LSP_GEN_JITTER 5 /* % */ #define CSNP_JITTER 10 /* % */ @@ -46,36 +47,59 @@ #define RANDOM_SPREAD 100000.0 +#define ISIS_LEVELS 2 +#define ISIS_LEVEL1 1 +#define ISIS_LEVEL2 2 + /* * Default values - * ISO - 10589 - * Section 7.3.21 - Parameters + * ISO - 10589 Section 7.3.21 - Parameters + * RFC 4444 */ #define MAX_AGE 1200 #define ZERO_AGE_LIFETIME 60 -#define MAX_LSP_GEN_INTERVAL 900 -#define MIN_LSP_GEN_INTERVAL 30 +#define MIN_LSP_LIFETIME 350 +#define MAX_LSP_LIFETIME 65535 +#define DEFAULT_LSP_LIFETIME 1200 + +#define MIN_MAX_LSP_GEN_INTERVAL 1 +#define MAX_MAX_LSP_GEN_INTERVAL 65235 +#define DEFAULT_MAX_LSP_GEN_INTERVAL 900 + +#define MIN_MIN_LSP_GEN_INTERVAL 1 +#define MAX_MIN_LSP_GEN_INTERVAL 120 /* RFC 4444 says 65535 */ +#define DEFAULT_MIN_LSP_GEN_INTERVAL 30 + #define MIN_LSP_TRANS_INTERVAL 5 -#define ISIS_MIN_LSP_LIFETIME 380 -#define CSNP_INTERVAL 10 -#define PSNP_INTERVAL 2 -#define ISIS_MAX_PATH_SPLITS 3 -#define ISIS_LEVELS 2 -#define ISIS_LEVEL1 1 -#define ISIS_LEVEL2 2 +#define MIN_CSNP_INTERVAL 1 +#define MAX_CSNP_INTERVAL 600 +#define DEFAULT_CSNP_INTERVAL 10 + +#define MIN_PSNP_INTERVAL 1 +#define MAX_PSNP_INTERVAL 120 +#define DEFAULT_PSNP_INTERVAL 2 + +#define MIN_HELLO_INTERVAL 1 +#define MAX_HELLO_INTERVAL 600 +#define DEFAULT_HELLO_INTERVAL 3 + +#define MIN_HELLO_MULTIPLIER 2 +#define MAX_HELLO_MULTIPLIER 100 +#define DEFAULT_HELLO_MULTIPLIER 10 -#define HELLO_INTERVAL 10 -#define HELLO_MINIMAL HELLO_INTERVAL -#define HELLO_MULTIPLIER 3 +#define MIN_PRIORITY 0 +#define MAX_PRIORITY 127 #define DEFAULT_PRIORITY 64 -/* different vendors implement different values 5-10 on average */ -#define LSP_GEN_INTERVAL_DEFAULT 10 -#define LSP_INTERVAL 33 /* msecs */ -#define DEFAULT_CIRCUIT_METRICS 10 -#define METRICS_UNSUPPORTED 0x80 -#define PERIODIC_SPF_INTERVAL 60 /* at the top of my head */ -#define MINIMUM_SPF_INTERVAL 5 /* .. same here */ + +/* min and max metric varies by new vs old metric types */ +#define DEFAULT_CIRCUIT_METRIC 10 + +#define METRICS_UNSUPPORTED 0x80 + +#define MINIMUM_SPF_INTERVAL 1 + +#define ISIS_MAX_PATH_SPLITS 64 /* * NLPID values @@ -104,6 +128,7 @@ #define SNPA_ADDRSTRLEN 18 #define ISIS_SYS_ID_LEN 6 +#define ISIS_NSEL_LEN 1 #define SYSID_STRLEN 24 /* @@ -136,8 +161,8 @@ * packets, using isomtu = mtu - LLC_LEN */ #define ISO_MTU(C) \ - (C->circ_type==CIRCUIT_T_BROADCAST) ? \ - (C->interface->mtu - LLC_LEN) : (C->interface->mtu) + ((if_is_broadcast ((C)->interface)) ? \ + (C->interface->mtu - LLC_LEN) : (C->interface->mtu)) #ifndef ETH_ALEN #define ETH_ALEN 6 diff --git a/isisd/isis_csm.c b/isisd/isis_csm.c index 6cdde46a9..0f642a777 100644 --- a/isisd/isis_csm.c +++ b/isisd/isis_csm.c @@ -36,6 +36,7 @@ #include "isisd/include-netbsd/iso.h" #include "isisd/isis_constants.h" #include "isisd/isis_common.h" +#include "isisd/isis_flags.h" #include "isisd/isis_circuit.h" #include "isisd/isis_tlv.h" #include "isisd/isis_lsp.h" @@ -45,7 +46,6 @@ #include "isisd/isis_constants.h" #include "isisd/isis_adjacency.h" #include "isisd/isis_dr.h" -#include "isisd/isis_flags.h" #include "isisd/isisd.h" #include "isisd/isis_csm.h" #include "isisd/isis_events.h" @@ -85,6 +85,7 @@ isis_csm_state_change (int event, struct isis_circuit *circuit, void *arg) case C_STATE_NA: if (circuit) zlog_warn ("Non-null circuit while state C_STATE_NA"); + assert (circuit == NULL); switch (event) { case ISIS_ENABLE: @@ -100,30 +101,36 @@ 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; } break; case C_STATE_INIT: + assert (circuit); switch (event) { case ISIS_ENABLE: isis_circuit_configure (circuit, (struct isis_area *) arg); - isis_circuit_up (circuit); + if (isis_circuit_up (circuit) != ISIS_OK) + { + isis_circuit_deconfigure (circuit, (struct isis_area *) arg); + break; + } circuit->state = C_STATE_UP; - circuit->connected = 1; - isis_event_circuit_state_change (circuit, 1); + isis_event_circuit_state_change (circuit, circuit->area, 1); listnode_delete (isis->init_circ_list, circuit); break; case IF_UP_FROM_Z: + assert (circuit); zlog_warn ("circuit already connected"); break; case ISIS_DISABLE: zlog_warn ("circuit already disabled"); break; case IF_DOWN_FROM_Z: - isis_circuit_if_del (circuit); + isis_circuit_if_del (circuit, (struct interface *) arg); listnode_delete (isis->init_circ_list, circuit); isis_circuit_del (circuit); circuit = NULL; @@ -131,19 +138,38 @@ isis_csm_state_change (int event, struct isis_circuit *circuit, void *arg) } break; case C_STATE_CONF: + assert (circuit); switch (event) { case ISIS_ENABLE: zlog_warn ("circuit already enabled"); break; case IF_UP_FROM_Z: - if (!circuit->connected) { - isis_circuit_if_add (circuit, (struct interface *) arg); - isis_circuit_up (circuit); - } + isis_circuit_if_add (circuit, (struct interface *) arg); + if (isis_circuit_up (circuit) != ISIS_OK) + { + zlog_err("Could not bring up %s because of invalid config.", + circuit->interface->name); + zlog_err("Clearing config for %s. Please re-examine it.", + circuit->interface->name); + if (circuit->ip_router) + { + circuit->ip_router = 0; + circuit->area->ip_circuits--; + } + if (circuit->ipv6_router) + { + circuit->ipv6_router = 0; + circuit->area->ipv6_circuits--; + } + circuit_update_nlpids(circuit); + isis_circuit_deconfigure(circuit, circuit->area); + listnode_add (isis->init_circ_list, circuit); + circuit->state = C_STATE_INIT; + break; + } circuit->state = C_STATE_UP; - circuit->connected = 1; - isis_event_circuit_state_change (circuit, 1); + isis_event_circuit_state_change (circuit, circuit->area, 1); break; case ISIS_DISABLE: isis_circuit_deconfigure (circuit, (struct isis_area *) arg); @@ -156,6 +182,7 @@ isis_csm_state_change (int event, struct isis_circuit *circuit, void *arg) } break; case C_STATE_UP: + assert (circuit); switch (event) { case ISIS_ENABLE: @@ -165,14 +192,18 @@ isis_csm_state_change (int event, struct isis_circuit *circuit, void *arg) zlog_warn ("circuit already connected"); break; case ISIS_DISABLE: + isis_circuit_down (circuit); isis_circuit_deconfigure (circuit, (struct isis_area *) arg); - listnode_add (isis->init_circ_list, circuit); circuit->state = C_STATE_INIT; - isis_event_circuit_state_change (circuit, 0); + isis_event_circuit_state_change (circuit, + (struct isis_area *)arg, 0); + listnode_add (isis->init_circ_list, circuit); break; case IF_DOWN_FROM_Z: + isis_circuit_down (circuit); + isis_circuit_if_del (circuit, (struct interface *) arg); circuit->state = C_STATE_CONF; - isis_event_circuit_state_change (circuit, 0); + isis_event_circuit_state_change (circuit, circuit->area, 0); break; } break; diff --git a/isisd/isis_dlpi.c b/isisd/isis_dlpi.c index fe872a952..7c7e0909e 100644 --- a/isisd/isis_dlpi.c +++ b/isisd/isis_dlpi.c @@ -33,6 +33,7 @@ #include #include "log.h" +#include "network.h" #include "stream.h" #include "if.h" @@ -90,13 +91,14 @@ static u_short pf_filter[] = * interfaces plus the (optional; not needed) Solaris packet filter module. */ -static void +static int dlpisend (int fd, const void *cbuf, size_t cbuflen, const void *dbuf, size_t dbuflen, int flags) { const struct strbuf *ctlptr = NULL; const struct strbuf *dataptr = NULL; struct strbuf ctlbuf, databuf; + int rv; if (cbuf != NULL) { @@ -115,8 +117,16 @@ dlpisend (int fd, const void *cbuf, size_t cbuflen, } /* We assume this doesn't happen often and isn't operationally significant */ - if (putmsg (fd, ctlptr, dataptr, flags) == -1) - zlog_debug ("%s: putmsg: %s", __func__, safe_strerror (errno)); + rv = putmsg(fd, ctlptr, dataptr, flags); + if (rv == -1 && dbuf == NULL) + { + /* + * For actual PDU transmission - recognizable buf dbuf != NULL, + * the error is passed upwards and should not be printed here. + */ + zlog_debug ("%s: putmsg: %s", __func__, safe_strerror (errno)); + } + return rv; } static ssize_t @@ -174,7 +184,7 @@ dlpiok (int fd, t_uscalar_t oprim) dl_ok_ack_t *doa = (dl_ok_ack_t *)dlpi_ctl; retv = dlpirctl (fd); - if (retv < DL_OK_ACK_SIZE || doa->dl_primitive != DL_OK_ACK || + if (retv < (ssize_t)DL_OK_ACK_SIZE || doa->dl_primitive != DL_OK_ACK || doa->dl_correct_primitive != oprim) { return -1; @@ -196,7 +206,7 @@ dlpiinfo (int fd) /* Info_req uses M_PCPROTO. */ dlpisend (fd, &dir, sizeof (dir), NULL, 0, RS_HIPRI); retv = dlpirctl (fd); - if (retv < DL_INFO_ACK_SIZE || dlpi_ctl[0] != DL_INFO_ACK) + if (retv < (ssize_t)DL_INFO_ACK_SIZE || dlpi_ctl[0] != DL_INFO_ACK) return -1; else return retv; @@ -251,7 +261,7 @@ dlpibind (int fd) dlpisend (fd, &dbr, sizeof (dbr), NULL, 0, 0); retv = dlpirctl (fd); - if (retv < DL_BIND_ACK_SIZE || dba->dl_primitive != DL_BIND_ACK) + if (retv < (ssize_t)DL_BIND_ACK_SIZE || dba->dl_primitive != DL_BIND_ACK) return -1; else return 0; @@ -287,12 +297,13 @@ dlpiaddr (int fd, u_char *addr) dlpisend (fd, &dpar, sizeof (dpar), NULL, 0, 0); retv = dlpirctl (fd); - if (retv < DL_PHYS_ADDR_ACK_SIZE || dpaa->dl_primitive != DL_PHYS_ADDR_ACK) + if (retv < (ssize_t)DL_PHYS_ADDR_ACK_SIZE + || dpaa->dl_primitive != DL_PHYS_ADDR_ACK) return -1; if (dpaa->dl_addr_offset < DL_PHYS_ADDR_ACK_SIZE || dpaa->dl_addr_length != ETHERADDRL || - dpaa->dl_addr_offset + dpaa->dl_addr_length > retv) + dpaa->dl_addr_offset + dpaa->dl_addr_length > (size_t)retv) return -1; bcopy((char *)dpaa + dpaa->dl_addr_offset, addr, ETHERADDRL); @@ -302,7 +313,7 @@ dlpiaddr (int fd, u_char *addr) static int open_dlpi_dev (struct isis_circuit *circuit) { - int fd, unit, retval; + int fd = -1, unit, retval; char devpath[MAXPATHLEN]; dl_info_ack_t *dia = (dl_info_ack_t *)dlpi_ctl; ssize_t acklen; @@ -403,8 +414,8 @@ open_dlpi_dev (struct isis_circuit *circuit) case DL_100BT: break; default: - zlog_warn ("%s: unexpected mac type on %s: %d", __func__, - circuit->interface->name, dia->dl_mac_type); + zlog_warn ("%s: unexpected mac type on %s: %lld", __func__, + circuit->interface->name, (long long)dia->dl_mac_type); close (fd); return ISIS_WARNING; } @@ -442,13 +453,9 @@ open_dlpi_dev (struct isis_circuit *circuit) * 8.4.2 - Broadcast subnetwork IIH PDUs */ retval = 0; - if (circuit->circuit_is_type & IS_LEVEL_1) - { - retval |= dlpimcast (fd, ALL_L1_ISS); - retval |= dlpimcast (fd, ALL_ISS); - } - if (circuit->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) { @@ -560,13 +567,13 @@ isis_recv_pdu_bcast (struct isis_circuit *circuit, u_char * ssnpa) return ISIS_WARNING; } - if (ctlbuf.len < DL_UNITDATA_IND_SIZE || + if (ctlbuf.len < (ssize_t)DL_UNITDATA_IND_SIZE || dui->dl_primitive != DL_UNITDATA_IND) return ISIS_WARNING; if (dui->dl_src_addr_length != ETHERADDRL + 2 || dui->dl_src_addr_offset < DL_UNITDATA_IND_SIZE || - dui->dl_src_addr_offset + dui->dl_src_addr_length > ctlbuf.len) + dui->dl_src_addr_offset + dui->dl_src_addr_length > (size_t)ctlbuf.len) return ISIS_WARNING; memcpy (ssnpa, (char *)dui + dui->dl_src_addr_offset + @@ -589,6 +596,17 @@ isis_send_pdu_bcast (struct isis_circuit *circuit, int level) dl_unitdata_req_t *dur = (dl_unitdata_req_t *)dlpi_ctl; char *dstaddr; u_short *dstsap; + int buflen; + int rv; + + buflen = stream_get_endp (circuit->snd_stream) + LLC_LEN; + if ((size_t)buflen > sizeof (sock_buff)) + { + zlog_warn ("isis_send_pdu_bcast: sock_buff size %zu is less than " + "output pdu size %d on circuit %s", + sizeof (sock_buff), buflen, circuit->interface->name); + return ISIS_WARNING; + } stream_set_getp (circuit->snd_stream, 0); @@ -612,15 +630,24 @@ isis_send_pdu_bcast (struct isis_circuit *circuit, int level) else memcpy (dstaddr, ALL_L2_ISS, ETHERADDRL); /* Note: DLPI SAP values are in host byte order */ - *dstsap = stream_get_endp (circuit->snd_stream) + LLC_LEN; + *dstsap = buflen; sock_buff[0] = ISO_SAP; sock_buff[1] = ISO_SAP; sock_buff[2] = 0x03; memcpy (sock_buff + LLC_LEN, circuit->snd_stream->data, stream_get_endp (circuit->snd_stream)); - dlpisend (circuit->fd, dur, sizeof (*dur) + dur->dl_dest_addr_length, - sock_buff, stream_get_endp (circuit->snd_stream) + LLC_LEN, 0); + rv = dlpisend(circuit->fd, dur, sizeof (*dur) + dur->dl_dest_addr_length, + sock_buff, buflen, 0); + if (rv < 0) + { + zlog_warn("IS-IS dlpi: could not transmit packet on %s: %s", + circuit->interface->name, safe_strerror(errno)); + if (ERRNO_IO_RETRY(errno)) + return ISIS_WARNING; + return ISIS_ERROR; + } + return ISIS_OK; } diff --git a/isisd/isis_dr.c b/isisd/isis_dr.c index 8d306c8f5..bc6ec1196 100644 --- a/isisd/isis_dr.c +++ b/isisd/isis_dr.c @@ -47,9 +47,6 @@ #include "isisd/isis_dr.h" #include "isisd/isis_events.h" -extern struct isis *isis; -extern struct thread_master *master; - const char * isis_disflag2string (int disflag) { @@ -137,15 +134,14 @@ isis_dr_elect (struct isis_circuit *circuit, int level) int biggest_prio = -1; int cmp_res, retval = ISIS_OK; - own_prio = circuit->u.bc.priority[level - 1]; + own_prio = circuit->priority[level - 1]; adjdb = circuit->u.bc.adjdb[level - 1]; if (!adjdb) { zlog_warn ("isis_dr_elect() adjdb == NULL"); - retval = ISIS_WARNING; list_delete (list); - goto out; + return ISIS_WARNING; } isis_adj_build_up_list (adjdb, list); @@ -189,42 +185,34 @@ isis_dr_elect (struct isis_circuit *circuit, int level) if (!adj_dr) { /* - * Could not find the DR - means we are alone and thus the DR + * Could not find the DR - means we are alone. Resign if we were DR. */ - if (!circuit->u.bc.is_dr[level - 1]) - { - list_delete (list); - list = NULL; - return isis_dr_commence (circuit, level); - } - goto out; + if (circuit->u.bc.is_dr[level - 1]) + retval = isis_dr_resign (circuit, level); + list_delete (list); + return retval; } /* * Now we have the DR adjacency, compare it to self */ - if (adj_dr->prio[level - 1] < own_prio - || (adj_dr->prio[level - 1] == own_prio - && memcmp (adj_dr->snpa, circuit->u.bc.snpa, ETH_ALEN) < 0)) + if (adj_dr->prio[level - 1] < own_prio || + (adj_dr->prio[level - 1] == own_prio && + memcmp (adj_dr->snpa, circuit->u.bc.snpa, ETH_ALEN) < 0)) { - if (!circuit->u.bc.is_dr[level - 1]) - { - /* - * We are the DR - */ + adj_dr->dis_record[level - 1].dis = ISIS_IS_NOT_DIS; + adj_dr->dis_record[level - 1].last_dis_change = time (NULL); - /* rotate the history log */ - for (ALL_LIST_ELEMENTS_RO (list, node, adj)) - isis_check_dr_change (adj, level); + /* rotate the history log */ + for (ALL_LIST_ELEMENTS_RO (list, node, adj)) + isis_check_dr_change (adj, level); - /* commence */ - list_delete (list); - return isis_dr_commence (circuit, level); - } + /* We are the DR, commence DR */ + if (circuit->u.bc.is_dr[level - 1] == 0 && listcount (list) > 0) + retval = isis_dr_commence (circuit, level); } else { - /* ok we have found the DIS - lets mark the adjacency */ /* set flag for show output */ adj_dr->dis_record[level - 1].dis = ISIS_IS_DIS; @@ -240,16 +228,10 @@ isis_dr_elect (struct isis_circuit *circuit, int level) /* * We are not DR - if we were -> resign */ - if (circuit->u.bc.is_dr[level - 1]) - { - list_delete (list); - return isis_dr_resign (circuit, level); - } + retval = isis_dr_resign (circuit, level); } -out: - if (list) - list_delete (list); + list_delete (list); return retval; } @@ -264,11 +246,12 @@ isis_dr_resign (struct isis_circuit *circuit, int level) circuit->u.bc.run_dr_elect[level - 1] = 0; THREAD_TIMER_OFF (circuit->u.bc.t_run_dr[level - 1]); THREAD_TIMER_OFF (circuit->u.bc.t_refresh_pseudo_lsp[level - 1]); + circuit->lsp_regenerate_pending[level - 1] = 0; memcpy (id, isis->sysid, ISIS_SYS_ID_LEN); LSP_PSEUDO_ID (id) = circuit->circuit_id; LSP_FRAGMENT (id) = 0; - lsp_purge_dr (id, circuit, level); + lsp_purge_pseudo (id, circuit, level); if (level == 1) { @@ -327,7 +310,7 @@ isis_dr_commence (struct isis_circuit *circuit, int level) if (LSP_PSEUDO_ID (old_dr)) { /* there was a dr elected, purge its LSPs from the db */ - lsp_purge_dr (old_dr, circuit, level); + lsp_purge_pseudo (old_dr, circuit, level); } memcpy (circuit->u.bc.l1_desig_is, isis->sysid, ISIS_SYS_ID_LEN); *(circuit->u.bc.l1_desig_is + ISIS_SYS_ID_LEN) = circuit->circuit_id; @@ -335,7 +318,7 @@ isis_dr_commence (struct isis_circuit *circuit, int level) assert (circuit->circuit_id); /* must be non-zero */ /* if (circuit->t_send_l1_psnp) thread_cancel (circuit->t_send_l1_psnp); */ - lsp_l1_pseudo_generate (circuit); + lsp_generate_pseudo (circuit, 1); THREAD_TIMER_OFF (circuit->u.bc.t_run_dr[0]); THREAD_TIMER_ON (master, circuit->u.bc.t_run_dr[0], isis_run_dr_l1, @@ -353,7 +336,7 @@ isis_dr_commence (struct isis_circuit *circuit, int level) if (LSP_PSEUDO_ID (old_dr)) { /* there was a dr elected, purge its LSPs from the db */ - lsp_purge_dr (old_dr, circuit, level); + lsp_purge_pseudo (old_dr, circuit, level); } memcpy (circuit->u.bc.l2_desig_is, isis->sysid, ISIS_SYS_ID_LEN); *(circuit->u.bc.l2_desig_is + ISIS_SYS_ID_LEN) = circuit->circuit_id; @@ -361,7 +344,7 @@ isis_dr_commence (struct isis_circuit *circuit, int level) assert (circuit->circuit_id); /* must be non-zero */ /* if (circuit->t_send_l1_psnp) thread_cancel (circuit->t_send_l1_psnp); */ - lsp_l2_pseudo_generate (circuit); + lsp_generate_pseudo (circuit, 2); THREAD_TIMER_OFF (circuit->u.bc.t_run_dr[1]); THREAD_TIMER_ON (master, circuit->u.bc.t_run_dr[1], isis_run_dr_l2, diff --git a/isisd/isis_dynhn.c b/isisd/isis_dynhn.c index 0b758c856..412f098a1 100644 --- a/isisd/isis_dynhn.c +++ b/isisd/isis_dynhn.c @@ -41,8 +41,6 @@ #include "isisd/isis_misc.h" #include "isisd/isis_constants.h" -extern struct isis *isis; -extern struct thread_master *master; extern struct host host; struct list *dyn_cache = NULL; @@ -51,7 +49,8 @@ static int dyn_cache_cleanup (struct thread *); void dyn_cache_init (void) { - dyn_cache = list_new (); + if (dyn_cache == NULL) + dyn_cache = list_new (); THREAD_TIMER_ON (master, isis->t_dync_clean, dyn_cache_cleanup, NULL, 120); return; } @@ -67,8 +66,8 @@ dyn_cache_cleanup (struct thread *thread) for (ALL_LIST_ELEMENTS (dyn_cache, node, nnode, dyn)) { - if ((now - dyn->refresh) < (MAX_AGE + 120)) - continue; + if ((now - dyn->refresh) < MAX_LSP_LIFETIME) + continue; list_delete_node (dyn_cache, node); XFREE (MTYPE_ISIS_DYNHN, dyn); @@ -79,7 +78,7 @@ dyn_cache_cleanup (struct thread *thread) } struct isis_dynhn * -dynhn_find_by_id (u_char * id) +dynhn_find_by_id (const u_char * id) { struct listnode *node = NULL; struct isis_dynhn *dyn = NULL; @@ -91,8 +90,21 @@ dynhn_find_by_id (u_char * id) return NULL; } +struct isis_dynhn * +dynhn_find_by_name (const char *hostname) +{ + struct listnode *node = NULL; + struct isis_dynhn *dyn = NULL; + + for (ALL_LIST_ELEMENTS_RO (dyn_cache, node, dyn)) + if (strncmp ((char *)dyn->name.name, hostname, 255) == 0) + return dyn; + + return NULL; +} + void -isis_dynhn_insert (u_char * id, struct hostname *hostname, int level) +isis_dynhn_insert (const u_char * id, struct hostname *hostname, int level) { struct isis_dynhn *dyn; @@ -122,6 +134,19 @@ isis_dynhn_insert (u_char * id, struct hostname *hostname, int level) return; } +void +isis_dynhn_remove (const u_char * id) +{ + struct isis_dynhn *dyn; + + dyn = dynhn_find_by_id (id); + if (!dyn) + return; + listnode_delete (dyn_cache, dyn); + XFREE (MTYPE_ISIS_DYNHN, dyn); + return; +} + /* * Level System ID Dynamic Hostname (notag) * 2 0000.0000.0001 foo-gw diff --git a/isisd/isis_dynhn.h b/isisd/isis_dynhn.h index 37a7b03c0..f06a067be 100644 --- a/isisd/isis_dynhn.h +++ b/isisd/isis_dynhn.h @@ -32,8 +32,10 @@ struct isis_dynhn }; void dyn_cache_init (void); -void isis_dynhn_insert (u_char * id, struct hostname *hostname, int level); -struct isis_dynhn *dynhn_find_by_id (u_char * id); +void isis_dynhn_insert (const u_char * id, struct hostname *hostname, int level); +void isis_dynhn_remove (const u_char * id); +struct isis_dynhn *dynhn_find_by_id (const u_char * id); +struct isis_dynhn *dynhn_find_by_name (const char *hostname); void dynhn_print_all (struct vty *vty); #endif /* _ZEBRA_ISIS_DYNHN_H */ diff --git a/isisd/isis_events.c b/isisd/isis_events.c index 438009224..abc4471ca 100644 --- a/isisd/isis_events.c +++ b/isisd/isis_events.c @@ -30,11 +30,13 @@ #include "hash.h" #include "prefix.h" #include "stream.h" +#include "table.h" #include "isisd/dict.h" #include "isisd/include-netbsd/iso.h" #include "isisd/isis_constants.h" #include "isisd/isis_common.h" +#include "isisd/isis_flags.h" #include "isisd/isis_circuit.h" #include "isisd/isis_tlv.h" #include "isisd/isis_lsp.h" @@ -44,15 +46,11 @@ #include "isisd/isis_constants.h" #include "isisd/isis_adjacency.h" #include "isisd/isis_dr.h" -#include "isisd/isis_flags.h" #include "isisd/isisd.h" #include "isisd/isis_csm.h" #include "isisd/isis_events.h" #include "isisd/isis_spf.h" -extern struct thread_master *master; -extern struct isis *isis; - /* debug isis-spf spf-events 4w4d: ISIS-Spf (tlt): L2 SPF needed, new adjacency, from 0x609229F4 4w4d: ISIS-Spf (tlt): L2, 0000.0000.0042.01-00 TLV contents changed, code 0x2 @@ -62,93 +60,36 @@ extern struct isis *isis; */ void -isis_event_circuit_state_change (struct isis_circuit *circuit, int up) +isis_event_circuit_state_change (struct isis_circuit *circuit, + struct isis_area *area, int up) { - struct isis_area *area; - - area = circuit->area; - assert (area); area->circuit_state_changes++; if (isis->debugs & DEBUG_EVENTS) - zlog_debug ("ISIS-Evt (%s) circuit %s", circuit->area->area_tag, - up ? "up" : "down"); + zlog_debug ("ISIS-Evt (%s) circuit %s", area->area_tag, + up ? "up" : "down"); /* * Regenerate LSPs this affects */ - lsp_regenerate_schedule (area); + lsp_regenerate_schedule (area, IS_LEVEL_1 | IS_LEVEL_2, 0); return; } -void -isis_event_system_type_change (struct isis_area *area, int newtype) -{ - struct listnode *node; - struct isis_circuit *circuit; - - if (isis->debugs & DEBUG_EVENTS) - zlog_debug ("ISIS-Evt (%s) system type change %s -> %s", area->area_tag, - circuit_t2string (area->is_type), circuit_t2string (newtype)); - - if (area->is_type == newtype) - return; /* No change */ - - switch (area->is_type) - { - case IS_LEVEL_1: - if (area->lspdb[1] == NULL) - area->lspdb[1] = lsp_db_init (); - lsp_l2_generate (area); - break; - case IS_LEVEL_1_AND_2: - if (newtype == IS_LEVEL_1) - { - lsp_db_destroy (area->lspdb[1]); - } - else - { - lsp_db_destroy (area->lspdb[0]); - } - break; - case IS_LEVEL_2: - if (area->lspdb[0] == NULL) - area->lspdb[0] = lsp_db_init (); - lsp_l1_generate (area); - break; - default: - break; - } - - area->is_type = newtype; - for (ALL_LIST_ELEMENTS_RO (area->circuit_list, node, circuit)) - isis_event_circuit_type_change (circuit, newtype); - - spftree_area_init (area); - lsp_regenerate_schedule (area); - - return; -} - -void -isis_event_area_addr_change (struct isis_area *area) -{ - -} - static void circuit_commence_level (struct isis_circuit *circuit, int level) { if (level == 1) { - THREAD_TIMER_ON (master, circuit->t_send_psnp[0], send_l1_psnp, circuit, - isis_jitter (circuit->psnp_interval[0], PSNP_JITTER)); + if (! circuit->is_passive) + THREAD_TIMER_ON (master, circuit->t_send_psnp[0], send_l1_psnp, circuit, + isis_jitter (circuit->psnp_interval[0], PSNP_JITTER)); if (circuit->circ_type == CIRCUIT_T_BROADCAST) { THREAD_TIMER_ON (master, circuit->u.bc.t_run_dr[0], isis_run_dr_l1, - circuit, 2 * circuit->hello_interval[1]); + circuit, 2 * circuit->hello_interval[0]); THREAD_TIMER_ON (master, circuit->u.bc.t_send_lan_hello[0], send_lan_l1_hello, circuit, @@ -160,8 +101,9 @@ circuit_commence_level (struct isis_circuit *circuit, int level) } else { - THREAD_TIMER_ON (master, circuit->t_send_psnp[1], send_l2_psnp, circuit, - isis_jitter (circuit->psnp_interval[1], PSNP_JITTER)); + if (! circuit->is_passive) + THREAD_TIMER_ON (master, circuit->t_send_psnp[1], send_l2_psnp, circuit, + isis_jitter (circuit->psnp_interval[1], PSNP_JITTER)); if (circuit->circ_type == CIRCUIT_T_BROADCAST) { @@ -193,23 +135,33 @@ circuit_resign_level (struct isis_circuit *circuit, int level) THREAD_TIMER_OFF (circuit->u.bc.t_send_lan_hello[idx]); THREAD_TIMER_OFF (circuit->u.bc.t_run_dr[idx]); THREAD_TIMER_OFF (circuit->u.bc.t_refresh_pseudo_lsp[idx]); + circuit->lsp_regenerate_pending[idx] = 0; circuit->u.bc.run_dr_elect[idx] = 0; + if (circuit->u.bc.lan_neighs[idx] != NULL) { + list_delete (circuit->u.bc.lan_neighs[idx]); + circuit->u.bc.lan_neighs[idx] = NULL; + } } return; } void -isis_event_circuit_type_change (struct isis_circuit *circuit, int newtype) +isis_circuit_is_type_set (struct isis_circuit *circuit, int newtype) { + if (circuit->state != C_STATE_UP) + { + circuit->is_type = newtype; + return; + } if (isis->debugs & DEBUG_EVENTS) zlog_debug ("ISIS-Evt (%s) circuit type change %s -> %s", circuit->area->area_tag, - circuit_t2string (circuit->circuit_is_type), + circuit_t2string (circuit->is_type), circuit_t2string (newtype)); - if (circuit->circuit_is_type == newtype) + if (circuit->is_type == newtype) return; /* No change */ if (!(newtype & circuit->area->is_type)) @@ -221,30 +173,33 @@ isis_event_circuit_type_change (struct isis_circuit *circuit, int newtype) return; } - switch (circuit->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->circuit_is_type = newtype; - lsp_regenerate_schedule (circuit->area); + circuit->is_type = newtype; + lsp_regenerate_schedule (circuit->area, IS_LEVEL_1 | IS_LEVEL_2, 0); return; } @@ -286,7 +241,7 @@ isis_event_adjacency_state_change (struct isis_adjacency *adj, int newstate) adj->circuit->area->area_tag); /* LSP generation again */ - lsp_regenerate_schedule (adj->circuit->area); + lsp_regenerate_schedule (adj->circuit->area, IS_LEVEL_1 | IS_LEVEL_2, 0); return; } @@ -307,7 +262,7 @@ isis_event_dis_status_change (struct thread *thread) zlog_debug ("ISIS-Evt (%s) DIS status change", circuit->area->area_tag); /* LSP generation again */ - lsp_regenerate_schedule (circuit->area); + lsp_regenerate_schedule (circuit->area, IS_LEVEL_1 | IS_LEVEL_2, 0); return 0; } diff --git a/isisd/isis_events.h b/isisd/isis_events.h index 86bf051f3..e7cfa3509 100644 --- a/isisd/isis_events.h +++ b/isisd/isis_events.h @@ -22,17 +22,11 @@ #ifndef _ZEBRA_ISIS_EVENTS_H #define _ZEBRA_ISIS_EVENTS_H -/* - * Events related to area - */ -void isis_event_system_type_change (struct isis_area *area, int newtype); -void isis_event_area_addr_change (struct isis_area *area); - /* * Events related to circuit */ void isis_event_circuit_state_change (struct isis_circuit *circuit, - int state); + struct isis_area *area, int state); void isis_event_circuit_type_change (struct isis_circuit *circuit, int newtype); /* diff --git a/isisd/isis_flags.c b/isisd/isis_flags.c index 03c91101f..ec0eaa4f8 100644 --- a/isisd/isis_flags.c +++ b/isisd/isis_flags.c @@ -36,11 +36,11 @@ flags_initialize (struct flags *flags) flags->free_idcs = NULL; } -int +long int flags_get_index (struct flags *flags) { struct listnode *node; - int index; + long int index; if (flags->free_idcs == NULL || flags->free_idcs->count == 0) { @@ -49,7 +49,7 @@ flags_get_index (struct flags *flags) else { node = listhead (flags->free_idcs); - index = (int) listgetdata (node); + index = (long int) listgetdata (node); listnode_delete (flags->free_idcs, (void *) index); index--; } @@ -58,7 +58,7 @@ flags_get_index (struct flags *flags) } void -flags_free_index (struct flags *flags, int index) +flags_free_index (struct flags *flags, long int index) { if (index + 1 == flags->maxindex) { diff --git a/isisd/isis_flags.h b/isisd/isis_flags.h index 13dd9e145..e2e42adcc 100644 --- a/isisd/isis_flags.h +++ b/isisd/isis_flags.h @@ -26,28 +26,43 @@ /* The grand plan is to support 1024 circuits so we have 32*32 bit flags * the support will be achived using the newest drafts */ -#define ISIS_MAX_CIRCUITS 32 /* = 1024 */ /*FIXME:defined in lsp.h as well */ +#define ISIS_MAX_CIRCUITS 32 /* = 1024 */ -void flags_initialize (struct flags *flags); -struct flags *new_flags (int size); -int flags_get_index (struct flags *flags); -void flags_free_index (struct flags *flags, int index); +/* + * Flags structure for SSN and SRM flags + */ +struct flags +{ + int maxindex; + struct list *free_idcs; +}; +void flags_initialize (struct flags *flags); +long int flags_get_index (struct flags *flags); +void flags_free_index (struct flags *flags, long int index); int flags_any_set (u_int32_t * flags); #define ISIS_SET_FLAG(F,C) \ - F[C->idx>>5] |= (1<<(C->idx & 0x1F)); + { \ + F[C->idx>>5] |= (1<<(C->idx & 0x1F)); \ + } #define ISIS_CLEAR_FLAG(F,C) \ - F[C->idx>>5] &= ~(1<<(C->idx & 0x1F)); + { \ + F[C->idx>>5] &= ~(1<<(C->idx & 0x1F)); \ + } -#define ISIS_CHECK_FLAG(F, C) F[(C)->idx>>5] & (1<<(C->idx & 0x1F)) +#define ISIS_CHECK_FLAG(F, C) (F[(C)->idx>>5] & (1<<(C->idx & 0x1F))) /* sets all u_32int_t flags to 1 */ #define ISIS_FLAGS_SET_ALL(FLAGS) \ - memset(FLAGS,0xFF,ISIS_MAX_CIRCUITS*4); + { \ + memset(FLAGS,0xFF,ISIS_MAX_CIRCUITS*4); \ + } #define ISIS_FLAGS_CLEAR_ALL(FLAGS) \ - memset(FLAGS,0x00,ISIS_MAX_CIRCUITS*4); + { \ + memset(FLAGS,0x00,ISIS_MAX_CIRCUITS*4); \ + } #endif /* _ZEBRA_ISIS_FLAGS_H */ diff --git a/isisd/isis_lsp.c b/isisd/isis_lsp.c index fd40bb372..aac8451bf 100644 --- a/isisd/isis_lsp.c +++ b/isisd/isis_lsp.c @@ -5,9 +5,10 @@ * Copyright (C) 2001,2002 Sampo Saaristo * Tampere University of Technology * Institute of Communications Engineering + * Copyright (C) 2013-2015 Christian Franke * * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public Licenseas published by the Free + * 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. * @@ -34,10 +35,13 @@ #include "hash.h" #include "if.h" #include "checksum.h" +#include "md5.h" +#include "table.h" #include "isisd/dict.h" #include "isisd/isis_constants.h" #include "isisd/isis_common.h" +#include "isisd/isis_flags.h" #include "isisd/isis_circuit.h" #include "isisd/isisd.h" #include "isisd/isis_tlv.h" @@ -45,24 +49,23 @@ #include "isisd/isis_pdu.h" #include "isisd/isis_dynhn.h" #include "isisd/isis_misc.h" -#include "isisd/isis_flags.h" #include "isisd/isis_csm.h" #include "isisd/isis_adjacency.h" #include "isisd/isis_spf.h" +#include "isisd/isis_te.h" #ifdef TOPOLOGY_GENERATE #include "spgrid.h" #endif -#define LSP_MEMORY_PREASSIGN - -extern struct isis *isis; -extern struct thread_master *master; -extern struct in_addr router_id_zebra; - /* staticly assigned vars for printing purposes */ char lsp_bits_string[200]; /* FIXME: enough ? */ +static int lsp_l1_refresh (struct thread *thread); +static int lsp_l2_refresh (struct thread *thread); +static int lsp_l1_refresh_pseudo (struct thread *thread); +static int lsp_l2_refresh_pseudo (struct thread *thread); + int lsp_id_cmp (u_char * id1, u_char * id2) { @@ -90,7 +93,7 @@ lsp_search (u_char * id, dict_t * lspdb) zlog_debug ("searching db"); for (dn = dict_first (lspdb); dn; dn = dict_next (lspdb, dn)) { - zlog_debug ("%s\t%pX", rawlspid_print ((char *) dnode_getkey (dn)), + zlog_debug ("%s\t%pX", rawlspid_print ((u_char *) dnode_getkey (dn)), dnode_get (dn)); } #endif /* EXTREME DEBUG */ @@ -109,54 +112,58 @@ lsp_clear_data (struct isis_lsp *lsp) if (!lsp) return; + if (lsp->tlv_data.hostname) + isis_dynhn_remove (lsp->lsp_header->lsp_id); + if (lsp->own_lsp) { if (lsp->tlv_data.nlpids) - XFREE (MTYPE_ISIS_TLV, lsp->tlv_data.nlpids); + XFREE (MTYPE_ISIS_TLV, lsp->tlv_data.nlpids); if (lsp->tlv_data.hostname) - XFREE (MTYPE_ISIS_TLV, lsp->tlv_data.hostname); + XFREE (MTYPE_ISIS_TLV, lsp->tlv_data.hostname); + if (lsp->tlv_data.router_id) + XFREE (MTYPE_ISIS_TLV, lsp->tlv_data.router_id); } - if (lsp->tlv_data.is_neighs) - list_delete (lsp->tlv_data.is_neighs); - if (lsp->tlv_data.te_is_neighs) - list_delete (lsp->tlv_data.te_is_neighs); - if (lsp->tlv_data.area_addrs) - list_delete (lsp->tlv_data.area_addrs); - if (lsp->tlv_data.es_neighs) - list_delete (lsp->tlv_data.es_neighs); - if (lsp->tlv_data.ipv4_addrs) - list_delete (lsp->tlv_data.ipv4_addrs); - if (lsp->tlv_data.ipv4_int_reachs) - list_delete (lsp->tlv_data.ipv4_int_reachs); - if (lsp->tlv_data.ipv4_ext_reachs) - list_delete (lsp->tlv_data.ipv4_ext_reachs); - if (lsp->tlv_data.te_ipv4_reachs) - list_delete (lsp->tlv_data.te_ipv4_reachs); -#ifdef HAVE_IPV6 - if (lsp->tlv_data.ipv6_addrs) - list_delete (lsp->tlv_data.ipv6_addrs); - if (lsp->tlv_data.ipv6_reachs) - list_delete (lsp->tlv_data.ipv6_reachs); -#endif /* HAVE_IPV6 */ - - memset (&lsp->tlv_data, 0, sizeof (struct tlvs)); - return; + free_tlvs (&lsp->tlv_data); } static void lsp_destroy (struct isis_lsp *lsp) { + struct listnode *cnode, *lnode, *lnnode; + struct isis_lsp *lsp_in_list; + struct isis_circuit *circuit; + if (!lsp) return; + if (lsp->area->circuit_list) { + for (ALL_LIST_ELEMENTS_RO (lsp->area->circuit_list, cnode, circuit)) + { + if (circuit->lsp_queue == NULL) + continue; + for (ALL_LIST_ELEMENTS (circuit->lsp_queue, lnode, lnnode, lsp_in_list)) + if (lsp_in_list == lsp) + list_delete_node(circuit->lsp_queue, lnode); + } + } + ISIS_FLAGS_CLEAR_ALL (lsp->SSNflags); + ISIS_FLAGS_CLEAR_ALL (lsp->SRMflags); + lsp_clear_data (lsp); if (LSP_FRAGMENT (lsp->lsp_header->lsp_id) == 0 && lsp->lspu.frags) { list_delete (lsp->lspu.frags); + lsp->lspu.frags = NULL; } + isis_spf_schedule (lsp->area, lsp->level); +#ifdef HAVE_IPV6 + isis_spf_schedule6 (lsp->area, lsp->level); +#endif + if (lsp->pdu) stream_free (lsp->pdu); XFREE (MTYPE_ISIS_LSP, lsp); @@ -254,7 +261,7 @@ lsp_compare (char *areatag, struct isis_lsp *lsp, u_int32_t seq_num, { if (isis->debugs & DEBUG_SNP_PACKETS) { - zlog_debug ("ISIS-Snp (%s): LSP %s seq 0x%08x, cksum 0x%04x," + zlog_debug ("ISIS-Snp (%s): Compare LSP %s seq 0x%08x, cksum 0x%04x," " lifetime %us", areatag, rawlspid_print (lsp->lsp_header->lsp_id), @@ -269,11 +276,23 @@ lsp_compare (char *areatag, struct isis_lsp *lsp, u_int32_t seq_num, return LSP_EQUAL; } - if (ntohl (seq_num) >= ntohl (lsp->lsp_header->seq_num)) + /* + * LSPs with identical checksums should only be treated as newer if: + * a) The current LSP has a remaining lifetime != 0 and the other LSP has a + * remaining lifetime == 0. In this case, we should participate in the purge + * and should not treat the current LSP with remaining lifetime == 0 as older. + * b) The LSP has an incorrect checksum. In this case, we need to react as given + * in 7.3.16.2. + */ + if (ntohl (seq_num) > ntohl (lsp->lsp_header->seq_num) + || (ntohl(seq_num) == ntohl(lsp->lsp_header->seq_num) + && ( (lsp->lsp_header->rem_lifetime != 0 + && rem_lifetime == 0) + || lsp->lsp_header->checksum != checksum))) { if (isis->debugs & DEBUG_SNP_PACKETS) { - zlog_debug ("ISIS-Snp (%s): LSP %s seq 0x%08x, cksum 0x%04x," + zlog_debug ("ISIS-Snp (%s): Compare LSP %s seq 0x%08x, cksum 0x%04x," " lifetime %us", areatag, rawlspid_print (lsp->lsp_header->lsp_id), @@ -290,7 +309,7 @@ lsp_compare (char *areatag, struct isis_lsp *lsp, u_int32_t seq_num, if (isis->debugs & DEBUG_SNP_PACKETS) { zlog_debug - ("ISIS-Snp (%s): LSP %s seq 0x%08x, cksum 0x%04x, lifetime %us", + ("ISIS-Snp (%s): Compare LSP %s seq 0x%08x, cksum 0x%04x, lifetime %us", areatag, rawlspid_print (lsp->lsp_header->lsp_id), ntohl (seq_num), ntohs (checksum), ntohs (rem_lifetime)); zlog_debug ("ISIS-Snp (%s): is older than ours seq 0x%08x," @@ -303,6 +322,91 @@ lsp_compare (char *areatag, struct isis_lsp *lsp, u_int32_t seq_num, return LSP_OLDER; } +static void +lsp_auth_add (struct isis_lsp *lsp) +{ + struct isis_passwd *passwd; + unsigned char hmac_md5_hash[ISIS_AUTH_MD5_SIZE]; + + /* + * Add the authentication info if its present + */ + (lsp->level == IS_LEVEL_1) ? (passwd = &lsp->area->area_passwd) : + (passwd = &lsp->area->domain_passwd); + switch (passwd->type) + { + /* Cleartext */ + case ISIS_PASSWD_TYPE_CLEARTXT: + memcpy (&lsp->tlv_data.auth_info, passwd, sizeof (struct isis_passwd)); + tlv_add_authinfo (passwd->type, passwd->len, passwd->passwd, lsp->pdu); + break; + + /* HMAC MD5 */ + case ISIS_PASSWD_TYPE_HMAC_MD5: + /* Remember where TLV is written so we can later + * overwrite the MD5 hash */ + lsp->auth_tlv_offset = stream_get_endp (lsp->pdu); + memset(&hmac_md5_hash, 0, ISIS_AUTH_MD5_SIZE); + lsp->tlv_data.auth_info.type = ISIS_PASSWD_TYPE_HMAC_MD5; + lsp->tlv_data.auth_info.len = ISIS_AUTH_MD5_SIZE; + memcpy (&lsp->tlv_data.auth_info.passwd, hmac_md5_hash, + ISIS_AUTH_MD5_SIZE); + tlv_add_authinfo (passwd->type, ISIS_AUTH_MD5_SIZE, hmac_md5_hash, + lsp->pdu); + break; + + default: + break; + } +} + +static void +lsp_auth_update (struct isis_lsp *lsp) +{ + struct isis_passwd *passwd; + unsigned char hmac_md5_hash[ISIS_AUTH_MD5_SIZE]; + uint16_t checksum, rem_lifetime; + + /* For HMAC MD5 we need to recompute the md5 hash and store it */ + (lsp->level == IS_LEVEL_1) ? (passwd = &lsp->area->area_passwd) : + (passwd = &lsp->area->domain_passwd); + if (passwd->type != ISIS_PASSWD_TYPE_HMAC_MD5) + return; + + /* + * In transient conditions (when net is configured where authentication + * config and lsp regenerate schedule is not yet run), there could be + * an own_lsp with auth_tlv_offset set to 0. In such a case, simply + * return, when lsp_regenerate is run, lsp will have auth tlv. + */ + if (lsp->auth_tlv_offset == 0) + return; + + /* + * RFC 5304 set auth value, checksum and remaining lifetime to zero + * before computation and reset to old values after computation. + */ + checksum = lsp->lsp_header->checksum; + rem_lifetime = lsp->lsp_header->rem_lifetime; + lsp->lsp_header->checksum = 0; + lsp->lsp_header->rem_lifetime = 0; + /* Set the authentication value as well to zero */ + memset (STREAM_DATA (lsp->pdu) + lsp->auth_tlv_offset + 3, + 0, ISIS_AUTH_MD5_SIZE); + /* Compute autentication value */ + hmac_md5 (STREAM_DATA (lsp->pdu), stream_get_endp(lsp->pdu), + (unsigned char *) &passwd->passwd, passwd->len, + (unsigned char *) &hmac_md5_hash); + /* Copy the hash into the stream */ + memcpy (STREAM_DATA (lsp->pdu) + lsp->auth_tlv_offset + 3, + hmac_md5_hash, ISIS_AUTH_MD5_SIZE); + memcpy (&lsp->tlv_data.auth_info.passwd, hmac_md5_hash, + ISIS_AUTH_MD5_SIZE); + /* Copy back the checksum and remaining lifetime */ + lsp->lsp_header->checksum = checksum; + lsp->lsp_header->rem_lifetime = rem_lifetime; +} + void lsp_inc_seqnum (struct isis_lsp *lsp, u_int32_t seq_num) { @@ -311,11 +415,25 @@ lsp_inc_seqnum (struct isis_lsp *lsp, u_int32_t seq_num) if (seq_num == 0 || ntohl (lsp->lsp_header->seq_num) > seq_num) newseq = ntohl (lsp->lsp_header->seq_num) + 1; else - newseq = seq_num++; + newseq = seq_num + 1; lsp->lsp_header->seq_num = htonl (newseq); - fletcher_checksum (STREAM_DATA (lsp->pdu) + 12, - ntohs (lsp->lsp_header->pdu_len) - 12, 12); + + /* Recompute authentication and checksum information */ + lsp_auth_update (lsp); + /* ISO 10589 - 7.3.11 Generation of the checksum + * The checksum shall be computed over all fields in the LSP which appear + * after the Remaining Lifetime field. This field (and those appearing + * before it) are excluded so that the LSP may be aged by systems without + * requiring recomputation. + */ + fletcher_checksum(STREAM_DATA (lsp->pdu) + 12, + ntohs (lsp->lsp_header->pdu_len) - 12, 12); + + isis_spf_schedule (lsp->area, lsp->level); +#ifdef HAVE_IPV6 + isis_spf_schedule6 (lsp->area, lsp->level); +#endif return; } @@ -340,54 +458,42 @@ lsp_seqnum_update (struct isis_lsp *lsp0) return; } -int -isis_lsp_authinfo_check (struct stream *stream, struct isis_area *area, - int pdulen, struct isis_passwd *passwd) +static u_int8_t +lsp_bits_generate (int level, int overload_bit, int attached_bit) { - uint32_t expected = 0, found; - struct tlvs tlvs; - int retval = 0; - - expected |= TLVFLAG_AUTH_INFO; - retval = parse_tlvs (area->area_tag, stream->data + - ISIS_FIXED_HDR_LEN + ISIS_LSP_HDR_LEN, - pdulen - ISIS_FIXED_HDR_LEN - - ISIS_LSP_HDR_LEN, &expected, &found, &tlvs); - - if (retval || !(found & TLVFLAG_AUTH_INFO)) - return 1; /* Auth fail (parsing failed or no auth-tlv) */ - - switch (tlvs.auth_info.type) - { - case ISIS_PASSWD_TYPE_HMAC_MD5: - zlog_debug("Got LSP with ISIS_PASSWD_TYPE_HMAC_MD5"); - break; - case ISIS_PASSWD_TYPE_CLEARTXT: - zlog_debug("Got LSP with ISIS_PASSWD_TYPE_CLEARTXT"); - break; - default: - zlog_debug("Unknown authentication type in LSP"); - break; - } - - return 0; - /* return authentication_check (passwd, &tlvs.auth_info);*/ + u_int8_t lsp_bits = 0; + if (level == IS_LEVEL_1) + lsp_bits = IS_LEVEL_1; + else + lsp_bits = IS_LEVEL_1_AND_2; + if (overload_bit) + lsp_bits |= overload_bit; + if (attached_bit) + lsp_bits |= attached_bit; + return lsp_bits; } static void lsp_update_data (struct isis_lsp *lsp, struct stream *stream, - struct isis_area *area) + struct isis_area *area, int level) { uint32_t expected = 0, found; int retval; + /* free the old lsp data */ + lsp_clear_data (lsp); + /* copying only the relevant part of our stream */ + if (lsp->pdu != NULL) + stream_free (lsp->pdu); lsp->pdu = stream_dup (stream); - + /* setting pointers to the correct place */ lsp->isis_header = (struct isis_fixed_hdr *) (STREAM_DATA (lsp->pdu)); lsp->lsp_header = (struct isis_link_state_hdr *) (STREAM_DATA (lsp->pdu) + ISIS_FIXED_HDR_LEN); + lsp->area = area; + lsp->level = level; lsp->age_out = ZERO_AGE_LIFETIME; lsp->installed = time (NULL); /* @@ -396,8 +502,6 @@ lsp_update_data (struct isis_lsp *lsp, struct stream *stream, expected |= TLVFLAG_AUTH_INFO; expected |= TLVFLAG_AREA_ADDRS; expected |= TLVFLAG_IS_NEIGHS; - if ((lsp->lsp_header->lsp_bits & 3) == 3) /* a level 2 LSP */ - expected |= TLVFLAG_PARTITION_DESIG_LEVEL2_IS; expected |= TLVFLAG_NLPID; if (area->dynhostname) expected |= TLVFLAG_DYN_HOSTNAME; @@ -415,57 +519,58 @@ lsp_update_data (struct isis_lsp *lsp, struct stream *stream, expected |= TLVFLAG_IPV6_REACHABILITY; #endif /* HAVE_IPV6 */ - retval = parse_tlvs (area->area_tag, lsp->pdu->data + - ISIS_FIXED_HDR_LEN + ISIS_LSP_HDR_LEN, - ntohs (lsp->lsp_header->pdu_len) - ISIS_FIXED_HDR_LEN - - ISIS_LSP_HDR_LEN, &expected, &found, &lsp->tlv_data); + retval = parse_tlvs (area->area_tag, STREAM_DATA (lsp->pdu) + + ISIS_FIXED_HDR_LEN + ISIS_LSP_HDR_LEN, + ntohs (lsp->lsp_header->pdu_len) - + ISIS_FIXED_HDR_LEN - ISIS_LSP_HDR_LEN, + &expected, &found, &lsp->tlv_data, + NULL); + if (retval != ISIS_OK) + { + zlog_warn ("Could not parse LSP"); + return; + } - if (found & TLVFLAG_DYN_HOSTNAME) + if ((found & TLVFLAG_DYN_HOSTNAME) && (area->dynhostname)) { - if (area->dynhostname) - isis_dynhn_insert (lsp->lsp_header->lsp_id, lsp->tlv_data.hostname, - (lsp->lsp_header->lsp_bits & LSPBIT_IST) == - IS_LEVEL_1_AND_2 ? IS_LEVEL_2 : - (lsp->lsp_header->lsp_bits & LSPBIT_IST)); + isis_dynhn_insert (lsp->lsp_header->lsp_id, lsp->tlv_data.hostname, + (lsp->lsp_header->lsp_bits & LSPBIT_IST) == + IS_LEVEL_1_AND_2 ? IS_LEVEL_2 : IS_LEVEL_1); } + return; } void -lsp_update (struct isis_lsp *lsp, struct isis_link_state_hdr *lsp_hdr, - struct stream *stream, struct isis_area *area, int level) +lsp_update (struct isis_lsp *lsp, struct stream *stream, + struct isis_area *area, int level) { dnode_t *dnode = NULL; - /* Remove old LSP from LSP database. */ + /* Remove old LSP from database. This is required since the + * lsp_update_data will free the lsp->pdu (which has the key, lsp_id) + * and will update it with the new data in the stream. */ dnode = dict_lookup (area->lspdb[level - 1], lsp->lsp_header->lsp_id); if (dnode) dnode_destroy (dict_delete (area->lspdb[level - 1], dnode)); - /* free the old lsp data */ - XFREE (MTYPE_STREAM_DATA, lsp->pdu); - lsp_clear_data (lsp); - /* rebuild the lsp data */ - lsp_update_data (lsp, stream, area); + lsp_update_data (lsp, stream, area, level); - /* set the new values for lsp header */ - memcpy (lsp->lsp_header, lsp_hdr, ISIS_LSP_HDR_LEN); - - if (dnode) - lsp_insert (lsp, area->lspdb[level - 1]); + /* insert the lsp back into the database */ + lsp_insert (lsp, area->lspdb[level - 1]); } /* creation of LSP directly from what we received */ struct isis_lsp * lsp_new_from_stream_ptr (struct stream *stream, u_int16_t pdu_len, struct isis_lsp *lsp0, - struct isis_area *area) + struct isis_area *area, int level) { struct isis_lsp *lsp; lsp = XCALLOC (MTYPE_ISIS_LSP, sizeof (struct isis_lsp)); - lsp_update_data (lsp, stream, area); + lsp_update_data (lsp, stream, area, level); if (lsp0 == NULL) { @@ -487,24 +592,16 @@ lsp_new_from_stream_ptr (struct stream *stream, } struct isis_lsp * -lsp_new (u_char * lsp_id, u_int16_t rem_lifetime, u_int32_t seq_num, - u_int8_t lsp_bits, u_int16_t checksum, int level) +lsp_new(struct isis_area *area, u_char * lsp_id, + u_int16_t rem_lifetime, u_int32_t seq_num, + u_int8_t lsp_bits, u_int16_t checksum, int level) { struct isis_lsp *lsp; lsp = XCALLOC (MTYPE_ISIS_LSP, sizeof (struct isis_lsp)); - if (!lsp) - { - /* FIXME: set lspdbol bit */ - zlog_warn ("lsp_new(): out of memory"); - return NULL; - } -#ifdef LSP_MEMORY_PREASSIGN - lsp->pdu = stream_new (1514); /*Should be minimal mtu? yup... */ -#else - /* We need to do realloc on TLVs additions */ - lsp->pdu = malloc (ISIS_FIXED_HDR_LEN + ISIS_LSP_HDR_LEN); -#endif /* LSP_MEMORY_PREASSIGN */ + lsp->area = area; + + lsp->pdu = stream_new(LLC_LEN + area->lsp_mtu); if (LSP_FRAGMENT (lsp_id) == 0) lsp->lspu.frags = list_new (); lsp->isis_header = (struct isis_fixed_hdr *) (STREAM_DATA (lsp->pdu)); @@ -512,7 +609,7 @@ lsp_new (u_char * lsp_id, u_int16_t rem_lifetime, u_int32_t seq_num, (STREAM_DATA (lsp->pdu) + ISIS_FIXED_HDR_LEN); /* at first we fill the FIXED HEADER */ - (level == 1) ? fill_fixed_hdr (lsp->isis_header, L1_LINK_STATE) : + (level == IS_LEVEL_1) ? fill_fixed_hdr (lsp->isis_header, L1_LINK_STATE) : fill_fixed_hdr (lsp->isis_header, L2_LINK_STATE); /* now for the LSP HEADER */ @@ -529,9 +626,10 @@ lsp_new (u_char * lsp_id, u_int16_t rem_lifetime, u_int32_t seq_num, stream_forward_endp (lsp->pdu, ISIS_FIXED_HDR_LEN + ISIS_LSP_HDR_LEN); if (isis->debugs & DEBUG_EVENTS) - zlog_debug ("New LSP with ID %s-%02x-%02x seqnum %08x", + zlog_debug ("New LSP with ID %s-%02x-%02x len %d seqnum %08x", sysid_print (lsp_id), LSP_PSEUDO_ID (lsp->lsp_header->lsp_id), LSP_FRAGMENT (lsp->lsp_header->lsp_id), + ntohl (lsp->lsp_header->pdu_len), ntohl (lsp->lsp_header->seq_num)); return lsp; @@ -541,6 +639,13 @@ void lsp_insert (struct isis_lsp *lsp, dict_t * lspdb) { dict_alloc_insert (lspdb, lsp->lsp_header->lsp_id, lsp); + if (lsp->lsp_header->seq_num != 0) + { + isis_spf_schedule (lsp->area, lsp->level); +#ifdef HAVE_IPV6 + isis_spf_schedule6 (lsp->area, lsp->level); +#endif + } } /* @@ -577,12 +682,13 @@ lsp_build_list_nonzero_ht (u_char * start_id, u_char * stop_id, } /* - * Build a list of all LSPs bounded by start and stop ids + * Build a list of num_lsps LSPs bounded by start_id and stop_id. */ void -lsp_build_list (u_char * start_id, u_char * stop_id, +lsp_build_list (u_char * start_id, u_char * stop_id, u_char num_lsps, struct list *list, dict_t * lspdb) { + u_char count; dnode_t *first, *last, *curr; first = dict_lower_bound (lspdb, start_id); @@ -594,14 +700,18 @@ lsp_build_list (u_char * start_id, u_char * stop_id, curr = first; listnode_add (list, first->dict_data); + count = 1; while (curr) { curr = dict_next (lspdb, curr); if (curr) - listnode_add (list, curr->dict_data); - if (curr == last) - break; + { + listnode_add (list, curr->dict_data); + count++; + } + if (count == num_lsps || curr == last) + break; } return; @@ -611,11 +721,12 @@ lsp_build_list (u_char * start_id, u_char * stop_id, * Build a list of LSPs with SSN flag set for the given circuit */ void -lsp_build_list_ssn (struct isis_circuit *circuit, struct list *list, - dict_t * lspdb) +lsp_build_list_ssn (struct isis_circuit *circuit, u_char num_lsps, + struct list *list, dict_t * lspdb) { dnode_t *dnode, *next; struct isis_lsp *lsp; + u_char count = 0; dnode = dict_first (lspdb); while (dnode != NULL) @@ -623,7 +734,12 @@ lsp_build_list_ssn (struct isis_circuit *circuit, struct list *list, next = dict_next (lspdb, dnode); lsp = dnode_get (dnode); if (ISIS_CHECK_FLAG (lsp->SSNflags, circuit)) - listnode_add (list, lsp); + { + listnode_add (list, lsp); + ++count; + } + if (count == num_lsps) + break; dnode = next; } @@ -637,22 +753,11 @@ lsp_set_time (struct isis_lsp *lsp) if (lsp->lsp_header->rem_lifetime == 0) { - if (lsp->age_out != 0) - lsp->age_out--; + if (lsp->age_out > 0) + lsp->age_out--; return; } - /* If we are turning 0 */ - /* ISO 10589 - 7.3.16.4 first paragraph */ - - if (ntohs (lsp->lsp_header->rem_lifetime) == 1) - { - /* 7.3.16.4 a) set SRM flags on all */ - ISIS_FLAGS_SET_ALL (lsp->SRMflags); - /* 7.3.16.4 b) retain only the header FIXME */ - /* 7.3.16.4 c) record the time to purge FIXME (other way to do it) */ - } - lsp->lsp_header->rem_lifetime = htons (ntohs (lsp->lsp_header->rem_lifetime) - 1); } @@ -669,13 +774,11 @@ lspid_print (u_char * lsp_id, u_char * trg, char dynhost, char frag) dyn = NULL; if (dyn) - sprintf ((char *)id, "%.14s", dyn->name.name); - else if (!memcmp (isis->sysid, lsp_id, ISIS_SYS_ID_LEN) & dynhost) - sprintf ((char *)id, "%.14s", unix_hostname ()); + sprintf ((char *)id, "%.14s", dyn->name.name); + else if (!memcmp (isis->sysid, lsp_id, ISIS_SYS_ID_LEN) && dynhost) + sprintf ((char *)id, "%.14s", unix_hostname ()); else - { memcpy (id, sysid_print (lsp_id), 15); - } if (frag) sprintf ((char *)trg, "%s.%02x-%02x", id, LSP_PSEUDO_ID (lsp_id), LSP_FRAGMENT (lsp_id)); @@ -707,30 +810,32 @@ lsp_bits2string (u_char * lsp_bits) } /* this function prints the lsp on show isis database */ -static void -lsp_print (dnode_t * node, struct vty *vty, char dynhost) +void +lsp_print (struct isis_lsp *lsp, struct vty *vty, char dynhost) { - struct isis_lsp *lsp = dnode_get (node); u_char LSPid[255]; + char age_out[8]; lspid_print (lsp->lsp_header->lsp_id, LSPid, dynhost, 1); - vty_out (vty, "%-21s%c ", LSPid, lsp->own_lsp ? '*' : ' '); - vty_out (vty, "0x%08x ", ntohl (lsp->lsp_header->seq_num)); - vty_out (vty, "0x%04x ", ntohs (lsp->lsp_header->checksum)); - + vty_out (vty, "%-21s%c ", LSPid, lsp->own_lsp ? '*' : ' '); + vty_out (vty, "%5u ", ntohs (lsp->lsp_header->pdu_len)); + vty_out (vty, "0x%08x ", ntohl (lsp->lsp_header->seq_num)); + vty_out (vty, "0x%04x ", ntohs (lsp->lsp_header->checksum)); if (ntohs (lsp->lsp_header->rem_lifetime) == 0) - vty_out (vty, " (%2u)", lsp->age_out); + { + snprintf (age_out, 8, "(%u)", lsp->age_out); + age_out[7] = '\0'; + vty_out (vty, "%7s ", age_out); + } else - vty_out (vty, "%5u", ntohs (lsp->lsp_header->rem_lifetime)); - - vty_out (vty, " %s%s", - lsp_bits2string (&lsp->lsp_header->lsp_bits), VTY_NEWLINE); + vty_out (vty, " %5u ", ntohs (lsp->lsp_header->rem_lifetime)); + vty_out (vty, "%s%s", + lsp_bits2string (&lsp->lsp_header->lsp_bits), VTY_NEWLINE); } -static void -lsp_print_detail (dnode_t * node, struct vty *vty, char dynhost) +void +lsp_print_detail (struct isis_lsp *lsp, struct vty *vty, char dynhost) { - struct isis_lsp *lsp = dnode_get (node); struct area_addr *area_addr; int i; struct listnode *lnode; @@ -751,7 +856,7 @@ lsp_print_detail (dnode_t * node, struct vty *vty, char dynhost) u_char ipv4_address[20]; lspid_print (lsp->lsp_header->lsp_id, LSPid, dynhost, 1); - lsp_print (node, vty, dynhost); + lsp_print (lsp, vty, dynhost); /* for all area address */ if (lsp->tlv_data.area_addrs) @@ -771,11 +876,11 @@ lsp_print_detail (dnode_t * node, struct vty *vty, char dynhost) { case NLPID_IP: case NLPID_IPV6: - vty_out (vty, " NLPID: 0x%X%s", + vty_out (vty, " NLPID : 0x%X%s", lsp->tlv_data.nlpids->nlpids[i], VTY_NEWLINE); break; default: - vty_out (vty, " NLPID: %s%s", "unknown", VTY_NEWLINE); + vty_out (vty, " NLPID : %s%s", "unknown", VTY_NEWLINE); break; } } @@ -784,33 +889,42 @@ lsp_print_detail (dnode_t * node, struct vty *vty, char dynhost) /* for the hostname tlv */ if (lsp->tlv_data.hostname) { - memset (hostname, 0, sizeof (hostname)); + bzero (hostname, sizeof (hostname)); memcpy (hostname, lsp->tlv_data.hostname->name, lsp->tlv_data.hostname->namelen); - vty_out (vty, " Hostname: %s%s", hostname, VTY_NEWLINE); + vty_out (vty, " Hostname : %s%s", hostname, VTY_NEWLINE); } - if (lsp->tlv_data.ipv4_addrs) - for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.ipv4_addrs, lnode, ipv4_addr)) - { - memcpy (ipv4_address, inet_ntoa (*ipv4_addr), sizeof (ipv4_address)); - vty_out (vty, " IP: %s%s", ipv4_address, VTY_NEWLINE); - } + /* authentication tlv */ + if (lsp->tlv_data.auth_info.type != ISIS_PASSWD_TYPE_UNUSED) + { + if (lsp->tlv_data.auth_info.type == ISIS_PASSWD_TYPE_HMAC_MD5) + vty_out (vty, " Auth type : md5%s", VTY_NEWLINE); + else if (lsp->tlv_data.auth_info.type == ISIS_PASSWD_TYPE_CLEARTXT) + vty_out (vty, " Auth type : clear text%s", VTY_NEWLINE); + } /* TE router id */ if (lsp->tlv_data.router_id) { memcpy (ipv4_address, inet_ntoa (lsp->tlv_data.router_id->id), sizeof (ipv4_address)); - vty_out (vty, " Router ID: %s%s", ipv4_address, VTY_NEWLINE); + vty_out (vty, " Router ID : %s%s", ipv4_address, VTY_NEWLINE); } + if (lsp->tlv_data.ipv4_addrs) + for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.ipv4_addrs, lnode, ipv4_addr)) + { + memcpy (ipv4_address, inet_ntoa (*ipv4_addr), sizeof (ipv4_address)); + vty_out (vty, " IPv4 Address: %s%s", ipv4_address, VTY_NEWLINE); + } + /* for the IS neighbor tlv */ if (lsp->tlv_data.is_neighs) for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.is_neighs, lnode, is_neigh)) { lspid_print (is_neigh->neigh_id, LSPid, dynhost, 0); - vty_out (vty, " Metric: %-10d IS %s%s", + vty_out (vty, " Metric : %-8d IS : %s%s", is_neigh->metrics.metric_default, LSPid, VTY_NEWLINE); } @@ -823,7 +937,7 @@ lsp_print_detail (dnode_t * node, struct vty *vty, char dynhost) sizeof (ipv4_reach_prefix)); memcpy (ipv4_reach_mask, inet_ntoa (ipv4_reach->mask), sizeof (ipv4_reach_mask)); - vty_out (vty, " Metric: %-10d IP-Internal %s %s%s", + vty_out (vty, " Metric : %-8d IPv4-Internal : %s %s%s", ipv4_reach->metrics.metric_default, ipv4_reach_prefix, ipv4_reach_mask, VTY_NEWLINE); } @@ -837,7 +951,7 @@ lsp_print_detail (dnode_t * node, struct vty *vty, char dynhost) sizeof (ipv4_reach_prefix)); memcpy (ipv4_reach_mask, inet_ntoa (ipv4_reach->mask), sizeof (ipv4_reach_mask)); - vty_out (vty, " Metric: %-10d IP-External %s %s%s", + vty_out (vty, " Metric : %-8d IPv4-External : %s %s%s", ipv4_reach->metrics.metric_default, ipv4_reach_prefix, ipv4_reach_mask, VTY_NEWLINE); } @@ -851,13 +965,13 @@ lsp_print_detail (dnode_t * node, struct vty *vty, char dynhost) memcpy (in6.s6_addr, ipv6_reach->prefix, PSIZE (ipv6_reach->prefix_len)); inet_ntop (AF_INET6, &in6, (char *)buff, BUFSIZ); - if ((ipv6_reach->control_info && + if ((ipv6_reach->control_info & CTRL_INFO_DISTRIBUTION) == DISTRIBUTION_INTERNAL) - vty_out (vty, " Metric: %-10d IPv6-Internal %s/%d%s", + vty_out (vty, " Metric : %-8d IPv6-Internal : %s/%d%s", ntohl (ipv6_reach->metric), buff, ipv6_reach->prefix_len, VTY_NEWLINE); else - vty_out (vty, " Metric: %-10d IPv6-External %s/%d%s", + vty_out (vty, " Metric : %-8d IPv6-External : %s/%d%s", ntohl (ipv6_reach->metric), buff, ipv6_reach->prefix_len, VTY_NEWLINE); } @@ -867,11 +981,11 @@ lsp_print_detail (dnode_t * node, struct vty *vty, char dynhost) if (lsp->tlv_data.te_is_neighs) for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.te_is_neighs, lnode, te_is_neigh)) { - uint32_t metric; - memcpy (&metric, te_is_neigh->te_metric, 3); lspid_print (te_is_neigh->neigh_id, LSPid, dynhost, 0); - vty_out (vty, " Metric: %-10d IS-Extended %s%s", - ntohl (metric << 8), LSPid, VTY_NEWLINE); + vty_out (vty, " Metric : %-8d IS-Extended : %s%s", + GET_TE_METRIC(te_is_neigh), LSPid, VTY_NEWLINE); + if (IS_MPLS_TE(isisMplsTE)) + mpls_te_print_detail(vty, te_is_neigh); } /* TE IPv4 tlv */ @@ -880,12 +994,13 @@ lsp_print_detail (dnode_t * node, struct vty *vty, char dynhost) te_ipv4_reach)) { /* FIXME: There should be better way to output this stuff. */ - vty_out (vty, " Metric: %-10d IP-Extended %s/%d%s", + vty_out (vty, " Metric : %-8d IPv4-Extended : %s/%d%s", ntohl (te_ipv4_reach->te_metric), inet_ntoa (newprefix2inaddr (&te_ipv4_reach->prefix_start, te_ipv4_reach->control)), te_ipv4_reach->control & 0x3F, VTY_NEWLINE); } + vty_out (vty, "%s", VTY_NEWLINE); return; } @@ -898,10 +1013,6 @@ lsp_print_all (struct vty *vty, dict_t * lspdb, char detail, char dynhost) dnode_t *node = dict_first (lspdb), *next; int lsp_count = 0; - /* print the title, for both modes */ - vty_out (vty, "LSP ID LSP Seq Num LSP Checksum " - "LSP Holdtime ATT/P/OL%s", VTY_NEWLINE); - if (detail == ISIS_UI_LEVEL_BRIEF) { while (node != NULL) @@ -909,7 +1020,7 @@ lsp_print_all (struct vty *vty, dict_t * lspdb, char detail, char dynhost) /* I think it is unnecessary, so I comment it out */ /* dict_contains (lspdb, node); */ next = dict_next (lspdb, node); - lsp_print (node, vty, dynhost); + lsp_print (dnode_get (node), vty, dynhost); node = next; lsp_count++; } @@ -919,7 +1030,7 @@ lsp_print_all (struct vty *vty, dict_t * lspdb, char detail, char dynhost) while (node != NULL) { next = dict_next (lspdb, node); - lsp_print_detail (node, vty, dynhost); + lsp_print_detail (dnode_get (node), vty, dynhost); node = next; lsp_count++; } @@ -929,7 +1040,7 @@ lsp_print_all (struct vty *vty, dict_t * lspdb, char detail, char dynhost) } #define FRAG_THOLD(S,T) \ -((STREAM_SIZE(S)*T)/100) + ((STREAM_SIZE(S)*T)/100) /* stream*, area->lsp_frag_threshold, increment */ #define FRAG_NEEDED(S,T,I) \ @@ -948,16 +1059,32 @@ lsp_tlv_fit (struct isis_lsp *lsp, struct list **from, struct list **to, if (!FRAG_NEEDED (lsp->pdu, frag_thold, listcount (*from) * tlvsize + 2)) { tlv_build_func (*from, lsp->pdu); - *to = *from; - *from = NULL; + if (listcount (*to) != 0) + { + struct listnode *node, *nextnode; + void *elem; + + for (ALL_LIST_ELEMENTS (*from, node, nextnode, elem)) + { + listnode_add (*to, elem); + list_delete_node (*from, node); + } + } + else + { + list_free (*to); + *to = *from; + *from = NULL; + } } else if (!FRAG_NEEDED (lsp->pdu, frag_thold, tlvsize + 2)) { /* fit all we can */ count = FRAG_THOLD (lsp->pdu, frag_thold) - 2 - (STREAM_SIZE (lsp->pdu) - STREAM_REMAIN (lsp->pdu)); - if (count) - count = count / tlvsize; + count = count / tlvsize; + if (count > (int)listcount (*from)) + count = listcount (*from); for (i = 0; i < count; i++) { listnode_add (*to, listgetdata (listhead (*from))); @@ -969,6 +1096,105 @@ lsp_tlv_fit (struct isis_lsp *lsp, struct list **from, struct list **to, return; } +/* Process IS_NEIGHBOURS TLV with TE subTLVs */ +static void +lsp_te_tlv_fit (struct isis_lsp *lsp, struct list **from, struct list **to, int frag_thold) +{ + int count, size = 0; + struct listnode *node, *nextnode; + struct te_is_neigh *elem; + + /* Start computing real size of TLVs */ + for (ALL_LIST_ELEMENTS (*from, node, nextnode, elem)) + size = size + elem->sub_tlvs_length + IS_NEIGHBOURS_LEN; + + /* can we fit all ? */ + if (!FRAG_NEEDED (lsp->pdu, frag_thold, size)) + { + tlv_add_te_is_neighs (*from, lsp->pdu); + if (listcount (*to) != 0) + { + for (ALL_LIST_ELEMENTS (*from, node, nextnode, elem)) + { + listnode_add (*to, elem); + list_delete_node (*from, node); + } + } + else + { + list_free (*to); + *to = *from; + *from = NULL; + } + } + else + { + /* fit all we can */ + /* Compute remaining place in LSP PDU */ + count = FRAG_THOLD (lsp->pdu, frag_thold) - 2 - + (STREAM_SIZE (lsp->pdu) - STREAM_REMAIN (lsp->pdu)); + /* Determine size of TE SubTLVs */ + elem = (struct te_is_neigh *)listgetdata ((struct listnode *)listhead (*from)); + count = count - elem->sub_tlvs_length - IS_NEIGHBOURS_LEN; + if (count > 0) + { + while (count > 0) + { + listnode_add (*to, listgetdata ((struct listnode *)listhead (*from))); + listnode_delete (*from, listgetdata ((struct listnode *)listhead (*from))); + + elem = (struct te_is_neigh *)listgetdata ((struct listnode *)listhead (*from)); + count = count - elem->sub_tlvs_length - IS_NEIGHBOURS_LEN; + } + + tlv_add_te_is_neighs (*to, lsp->pdu); + } + } + lsp->lsp_header->pdu_len = htons (stream_get_endp (lsp->pdu)); + return; +} + +static u_int16_t +lsp_rem_lifetime (struct isis_area *area, int level) +{ + u_int16_t rem_lifetime; + + /* Add jitter to configured LSP lifetime */ + rem_lifetime = isis_jitter (area->max_lsp_lifetime[level - 1], + MAX_AGE_JITTER); + + /* No jitter if the max refresh will be less than configure gen interval */ + /* N.B. this calucation is acceptable since rem_lifetime is in [332,65535] at + * this point */ + if (area->lsp_gen_interval[level - 1] > (rem_lifetime - 300)) + rem_lifetime = area->max_lsp_lifetime[level - 1]; + + return rem_lifetime; +} + +static u_int16_t +lsp_refresh_time (struct isis_lsp *lsp, u_int16_t rem_lifetime) +{ + struct isis_area *area = lsp->area; + int level = lsp->level; + u_int16_t refresh_time; + + /* Add jitter to LSP refresh time */ + refresh_time = isis_jitter (area->lsp_refresh[level - 1], + MAX_LSP_GEN_JITTER); + + /* RFC 4444 : make sure the refresh time is at least less than 300 + * of the remaining lifetime and more than gen interval */ + if (refresh_time <= area->lsp_gen_interval[level - 1] || + refresh_time > (rem_lifetime - 300)) + refresh_time = rem_lifetime - 300; + + /* In cornercases, refresh_time might be <= lsp_gen_interval, however + * we accept this violation to satisfy refresh_time <= rem_lifetime - 300 */ + + return refresh_time; +} + static struct isis_lsp * lsp_next_frag (u_char frag_num, struct isis_lsp *lsp0, struct isis_area *area, int level) @@ -978,41 +1204,136 @@ lsp_next_frag (u_char frag_num, struct isis_lsp *lsp0, struct isis_area *area, memcpy (frag_id, lsp0->lsp_header->lsp_id, ISIS_SYS_ID_LEN + 1); LSP_FRAGMENT (frag_id) = frag_num; + /* FIXME add authentication TLV for fragment LSPs */ lsp = lsp_search (frag_id, area->lspdb[level - 1]); if (lsp) { - /* - * Clear the TLVs, but inherit the authinfo - */ + /* Clear the TLVs */ lsp_clear_data (lsp); - if (lsp0->tlv_data.auth_info.type) - { - memcpy (&lsp->tlv_data.auth_info, &lsp->tlv_data.auth_info, - sizeof (struct isis_passwd)); - tlv_add_authinfo (lsp->tlv_data.auth_info.type, - lsp->tlv_data.auth_info.len, - lsp->tlv_data.auth_info.passwd, lsp->pdu); - } return lsp; } - lsp = lsp_new (frag_id, area->max_lsp_lifetime[level - 1], 0, area->is_type, - 0, level); + lsp = lsp_new (area, frag_id, ntohs(lsp0->lsp_header->rem_lifetime), 0, + lsp_bits_generate (level, area->overload_bit, + area->attached_bit), 0, level); + lsp->area = area; lsp->own_lsp = 1; lsp_insert (lsp, area->lspdb[level - 1]); listnode_add (lsp0->lspu.frags, lsp); lsp->lspu.zero_lsp = lsp0; - /* - * Copy the authinfo from zero LSP - */ - if (lsp0->tlv_data.auth_info.type) + return lsp; +} + +static void +lsp_build_ext_reach_ipv4(struct isis_lsp *lsp, struct isis_area *area, + struct tlvs *tlv_data) +{ + struct route_table *er_table; + struct route_node *rn; + struct prefix_ipv4 *ipv4; + struct isis_ext_info *info; + struct ipv4_reachability *ipreach; + struct te_ipv4_reachability *te_ipreach; + + er_table = get_ext_reach(area, AF_INET, lsp->level); + if (!er_table) + return; + + for (rn = route_top(er_table); rn; rn = route_next(rn)) { - memcpy (&lsp->tlv_data.auth_info, &lsp->tlv_data.auth_info, - sizeof (struct isis_passwd)); - tlv_add_authinfo (lsp->tlv_data.auth_info.type, - lsp->tlv_data.auth_info.len, - lsp->tlv_data.auth_info.passwd, lsp->pdu); + if (!rn->info) + continue; + + ipv4 = (struct prefix_ipv4*)&rn->p; + info = rn->info; + if (area->oldmetric) + { + if (tlv_data->ipv4_ext_reachs == NULL) + { + tlv_data->ipv4_ext_reachs = list_new(); + tlv_data->ipv4_ext_reachs->del = free_tlv; + } + ipreach = XMALLOC(MTYPE_ISIS_TLV, sizeof(*ipreach)); + + ipreach->prefix.s_addr = ipv4->prefix.s_addr; + masklen2ip(ipv4->prefixlen, &ipreach->mask); + ipreach->prefix.s_addr &= ipreach->mask.s_addr; + + if ((info->metric & 0x3f) != info->metric) + ipreach->metrics.metric_default = 0x3f; + else + ipreach->metrics.metric_default = info->metric; + ipreach->metrics.metric_expense = METRICS_UNSUPPORTED; + ipreach->metrics.metric_error = METRICS_UNSUPPORTED; + ipreach->metrics.metric_delay = METRICS_UNSUPPORTED; + listnode_add(tlv_data->ipv4_ext_reachs, ipreach); + } + if (area->newmetric) + { + if (tlv_data->te_ipv4_reachs == NULL) + { + tlv_data->te_ipv4_reachs = list_new(); + tlv_data->te_ipv4_reachs->del = free_tlv; + } + te_ipreach = + XCALLOC(MTYPE_ISIS_TLV, + sizeof(*te_ipreach) - 1 + PSIZE(ipv4->prefixlen)); + if (info->metric > MAX_WIDE_PATH_METRIC) + te_ipreach->te_metric = htonl(MAX_WIDE_PATH_METRIC); + else + te_ipreach->te_metric = htonl(info->metric); + te_ipreach->control = ipv4->prefixlen & 0x3f; + memcpy(&te_ipreach->prefix_start, &ipv4->prefix.s_addr, + PSIZE(ipv4->prefixlen)); + listnode_add(tlv_data->te_ipv4_reachs, te_ipreach); + } } - return lsp; +} + +static void +lsp_build_ext_reach_ipv6(struct isis_lsp *lsp, struct isis_area *area, + struct tlvs *tlv_data) +{ + struct route_table *er_table; + struct route_node *rn; + struct prefix_ipv6 *ipv6; + struct isis_ext_info *info; + struct ipv6_reachability *ip6reach; + + er_table = get_ext_reach(area, AF_INET6, lsp->level); + if (!er_table) + return; + + for (rn = route_top(er_table); rn; rn = route_next(rn)) + { + if (!rn->info) + continue; + + ipv6 = (struct prefix_ipv6*)&rn->p; + info = rn->info; + + if (tlv_data->ipv6_reachs == NULL) + { + tlv_data->ipv6_reachs = list_new(); + tlv_data->ipv6_reachs->del = free_tlv; + } + ip6reach = XCALLOC(MTYPE_ISIS_TLV, sizeof(*ip6reach)); + if (info->metric > MAX_WIDE_PATH_METRIC) + ip6reach->metric = htonl(MAX_WIDE_PATH_METRIC); + else + ip6reach->metric = htonl(info->metric); + ip6reach->control_info = DISTRIBUTION_EXTERNAL; + ip6reach->prefix_len = ipv6->prefixlen; + memcpy(ip6reach->prefix, ipv6->prefix.s6_addr, sizeof(ip6reach->prefix)); + listnode_add(tlv_data->ipv6_reachs, ip6reach); + } +} + +static void +lsp_build_ext_reach (struct isis_lsp *lsp, struct isis_area *area, + struct tlvs *tlv_data) +{ + lsp_build_ext_reach_ipv4(lsp, area, tlv_data); + lsp_build_ext_reach_ipv6(lsp, area, tlv_data); } /* @@ -1020,7 +1341,7 @@ lsp_next_frag (u_char frag_num, struct isis_lsp *lsp0, struct isis_area *area, * area->lsp_frag_threshold is exceeded. */ static void -lsp_build_nonpseudo (struct isis_lsp *lsp, struct isis_area *area) +lsp_build (struct isis_lsp *lsp, struct isis_area *area) { struct is_neigh *is_neigh; struct te_is_neigh *te_is_neigh; @@ -1032,13 +1353,35 @@ lsp_build_nonpseudo (struct isis_lsp *lsp, struct isis_area *area) struct te_ipv4_reachability *te_ipreach; struct isis_adjacency *nei; #ifdef HAVE_IPV6 - struct prefix_ipv6 *ipv6, *ip6prefix; + struct prefix_ipv6 *ipv6, ip6prefix; struct ipv6_reachability *ip6reach; #endif /* HAVE_IPV6 */ struct tlvs tlv_data; struct isis_lsp *lsp0 = lsp; - struct isis_passwd *passwd; struct in_addr *routerid; + uint32_t expected = 0, found = 0; + uint32_t metric; + u_char zero_id[ISIS_SYS_ID_LEN + 1]; + int retval = ISIS_OK; + char buf[BUFSIZ]; + + lsp_debug("ISIS (%s): Constructing local system LSP for level %d", area->area_tag, level); + + /* + * Building the zero lsp + */ + memset (zero_id, 0, ISIS_SYS_ID_LEN + 1); + + /* Reset stream endp. Stream is always there and on every LSP refresh only + * TLV part of it is overwritten. So we must seek past header we will not + * touch. */ + stream_reset (lsp->pdu); + stream_forward_endp (lsp->pdu, ISIS_FIXED_HDR_LEN + ISIS_LSP_HDR_LEN); + + /* + * Add the authentication info if its present + */ + lsp_auth_add (lsp); /* * First add the tlvs related to area @@ -1048,6 +1391,9 @@ lsp_build_nonpseudo (struct isis_lsp *lsp, struct isis_area *area) if (lsp->tlv_data.area_addrs == NULL) lsp->tlv_data.area_addrs = list_new (); list_add_list (lsp->tlv_data.area_addrs, area->area_addrs); + if (listcount (lsp->tlv_data.area_addrs) > 0) + tlv_add_area_addrs (lsp->tlv_data.area_addrs, lsp->pdu); + /* Protocols Supported */ if (area->ip_circuits > 0 #ifdef HAVE_IPV6 @@ -1059,61 +1405,54 @@ lsp_build_nonpseudo (struct isis_lsp *lsp, struct isis_area *area) lsp->tlv_data.nlpids->count = 0; if (area->ip_circuits > 0) { + lsp_debug("ISIS (%s): Found IPv4 circuit, adding IPv4 to NLPIDs", area->area_tag); lsp->tlv_data.nlpids->count++; lsp->tlv_data.nlpids->nlpids[0] = NLPID_IP; } #ifdef HAVE_IPV6 if (area->ipv6_circuits > 0) { + lsp_debug("ISIS (%s): Found IPv6 circuit, adding IPv6 to NLPIDs", area->area_tag); lsp->tlv_data.nlpids->count++; lsp->tlv_data.nlpids->nlpids[lsp->tlv_data.nlpids->count - 1] = NLPID_IPV6; } #endif /* HAVE_IPV6 */ + tlv_add_nlpid (lsp->tlv_data.nlpids, lsp->pdu); } + /* Dynamic Hostname */ if (area->dynhostname) { + const char *hostname = unix_hostname(); + size_t hostname_len = strlen(hostname); + lsp->tlv_data.hostname = XMALLOC (MTYPE_ISIS_TLV, sizeof (struct hostname)); - memcpy (lsp->tlv_data.hostname->name, unix_hostname (), - strlen (unix_hostname ())); - lsp->tlv_data.hostname->namelen = strlen (unix_hostname ()); - } - - /* - * Building the zero lsp - */ - - /* Reset stream endp. Stream is always there and on every LSP refresh only - * TLV part of it is overwritten. So we must seek past header we will not - * touch. */ - stream_reset (lsp->pdu); - stream_forward_endp (lsp->pdu, ISIS_FIXED_HDR_LEN + ISIS_LSP_HDR_LEN); + strncpy((char *)lsp->tlv_data.hostname->name, hostname, + sizeof(lsp->tlv_data.hostname->name)); + if (hostname_len <= MAX_TLV_LEN) + lsp->tlv_data.hostname->namelen = hostname_len; + else + lsp->tlv_data.hostname->namelen = MAX_TLV_LEN; - /* - * Add the authentication info if its present - */ - (level == 1) ? (passwd = &area->area_passwd) : - (passwd = &area->domain_passwd); - if (passwd->type) + lsp_debug("ISIS (%s): Adding dynamic hostname '%.*s'", area->area_tag, + lsp->tlv_data.hostname->namelen, lsp->tlv_data.hostname->name); + tlv_add_dynamic_hostname (lsp->tlv_data.hostname, lsp->pdu); + } + else { - memcpy (&lsp->tlv_data.auth_info, passwd, sizeof (struct isis_passwd)); - tlv_add_authinfo (passwd->type, passwd->len, passwd->passwd, lsp->pdu); + lsp_debug("ISIS (%s): Not adding dynamic hostname (disabled)", area->area_tag); } - if (lsp->tlv_data.nlpids) - tlv_add_nlpid (lsp->tlv_data.nlpids, lsp->pdu); - if (lsp->tlv_data.hostname) - tlv_add_dynamic_hostname (lsp->tlv_data.hostname, lsp->pdu); - if (lsp->tlv_data.area_addrs && listcount (lsp->tlv_data.area_addrs) > 0) - tlv_add_area_addrs (lsp->tlv_data.area_addrs, lsp->pdu); /* IPv4 address and TE router ID TLVs. In case of the first one we don't * follow "C" vendor, but "J" vendor behavior - one IPv4 address is put into * LSP and this address is same as router id. */ - if (router_id_zebra.s_addr != 0) + if (isis->router_id != 0) { + inet_ntop(AF_INET, &isis->router_id, buf, sizeof(buf)); + lsp_debug("ISIS (%s): Adding router ID %s as IPv4 tlv.", area->area_tag, buf); if (lsp->tlv_data.ipv4_addrs == NULL) { lsp->tlv_data.ipv4_addrs = list_new (); @@ -1121,7 +1460,7 @@ lsp_build_nonpseudo (struct isis_lsp *lsp, struct isis_area *area) } routerid = XMALLOC (MTYPE_ISIS_TLV, sizeof (struct in_addr)); - routerid->s_addr = router_id_zebra.s_addr; + routerid->s_addr = isis->router_id; listnode_add (lsp->tlv_data.ipv4_addrs, routerid); tlv_add_in_addr (routerid, lsp->pdu, IPV4_ADDR); @@ -1129,19 +1468,25 @@ lsp_build_nonpseudo (struct isis_lsp *lsp, struct isis_area *area) * TLV's are in use. */ if (area->newmetric) { + lsp_debug("ISIS (%s): Adding router ID also as TE router ID tlv.", area->area_tag); lsp->tlv_data.router_id = XMALLOC (MTYPE_ISIS_TLV, sizeof (struct in_addr)); - lsp->tlv_data.router_id->id.s_addr = router_id_zebra.s_addr; - tlv_add_in_addr (&lsp->tlv_data.router_id->id, lsp->pdu, TE_ROUTER_ID); + lsp->tlv_data.router_id->id.s_addr = isis->router_id; + tlv_add_in_addr (&lsp->tlv_data.router_id->id, lsp->pdu, + TE_ROUTER_ID); } } + else + { + lsp_debug("ISIS (%s): Router ID is unset. Not adding tlv.", area->area_tag); + } memset (&tlv_data, 0, sizeof (struct tlvs)); #ifdef TOPOLOGY_GENERATE /* If topology exists (and we create topology for level 1 only), create * (hardcoded) link to topology. */ - if (area->topology && level == 1) + if (area->topology && level == IS_LEVEL_1) { if (tlv_data.is_neighs == NULL) { @@ -1161,13 +1506,25 @@ lsp_build_nonpseudo (struct isis_lsp *lsp, struct isis_area *area) } #endif /* TOPOLOGY_GENERATE */ + lsp_debug("ISIS (%s): Adding circuit specific information.", area->area_tag); + /* * Then build lists of tlvs related to circuits */ for (ALL_LIST_ELEMENTS_RO (area->circuit_list, node, circuit)) { + if (!circuit->interface) + lsp_debug("ISIS (%s): Processing %s circuit %p with unknown interface", + area->area_tag, circuit_type2string(circuit->circ_type), circuit); + else + lsp_debug("ISIS (%s): Processing %s circuit %s", + area->area_tag, circuit_type2string(circuit->circ_type), circuit->interface->name); + if (circuit->state != C_STATE_UP) - continue; + { + lsp_debug("ISIS (%s): Circuit is not up, ignoring.", area->area_tag); + continue; + } /* * Add IPv4 internal reachability of this circuit @@ -1175,6 +1532,7 @@ lsp_build_nonpseudo (struct isis_lsp *lsp, struct isis_area *area) if (circuit->ip_router && circuit->ip_addrs && circuit->ip_addrs->count > 0) { + lsp_debug("ISIS (%s): Circuit has IPv4 active, adding respective TLVs.", area->area_tag); if (area->oldmetric) { if (tlv_data.ipv4_int_reachs == NULL) @@ -1186,13 +1544,18 @@ lsp_build_nonpseudo (struct isis_lsp *lsp, struct isis_area *area) { ipreach = XMALLOC (MTYPE_ISIS_TLV, sizeof (struct ipv4_reachability)); - ipreach->metrics = circuit->metrics[level - 1]; + ipreach->metrics.metric_default = circuit->metric[level - 1]; + ipreach->metrics.metric_expense = METRICS_UNSUPPORTED; + ipreach->metrics.metric_error = METRICS_UNSUPPORTED; + ipreach->metrics.metric_delay = METRICS_UNSUPPORTED; masklen2ip (ipv4->prefixlen, &ipreach->mask); ipreach->prefix.s_addr = ((ipreach->mask.s_addr) & (ipv4->prefix.s_addr)); + inet_ntop(AF_INET, &ipreach->prefix.s_addr, buf, sizeof(buf)); + lsp_debug("ISIS (%s): Adding old-style IP reachability for %s/%d", + area->area_tag, buf, ipv4->prefixlen); listnode_add (tlv_data.ipv4_int_reachs, ipreach); } - tlv_data.ipv4_int_reachs->del = free_tlv; } if (area->newmetric) { @@ -1209,17 +1572,21 @@ lsp_build_nonpseudo (struct isis_lsp *lsp, struct isis_area *area) ((ipv4->prefixlen + 7)/8) - 1); if (area->oldmetric) - te_ipreach->te_metric = htonl (circuit->metrics[level - 1].metric_default); + te_ipreach->te_metric = htonl (circuit->metric[level - 1]); else te_ipreach->te_metric = htonl (circuit->te_metric[level - 1]); te_ipreach->control = (ipv4->prefixlen & 0x3F); memcpy (&te_ipreach->prefix_start, &ipv4->prefix.s_addr, (ipv4->prefixlen + 7)/8); + inet_ntop(AF_INET, &ipv4->prefix.s_addr, buf, sizeof(buf)); + lsp_debug("ISIS (%s): Adding te-style IP reachability for %s/%d", + area->area_tag, buf, ipv4->prefixlen); listnode_add (tlv_data.te_ipv4_reachs, te_ipreach); } } } + #ifdef HAVE_IPV6 /* * Add IPv6 reachability of this circuit @@ -1240,15 +1607,20 @@ lsp_build_nonpseudo (struct isis_lsp *lsp, struct isis_area *area) if (area->oldmetric) ip6reach->metric = - htonl (circuit->metrics[level - 1].metric_default); + htonl (circuit->metric[level - 1]); else ip6reach->metric = htonl (circuit->te_metric[level - 1]); ip6reach->control_info = 0; ip6reach->prefix_len = ipv6->prefixlen; - memcpy (&ip6prefix, &ipv6, sizeof(ip6prefix)); - apply_mask_ipv6 (ip6prefix); - memcpy (ip6reach->prefix, ip6prefix->prefix.s6_addr, + memcpy(&ip6prefix, ipv6, sizeof(ip6prefix)); + apply_mask_ipv6(&ip6prefix); + + inet_ntop(AF_INET6, &ip6prefix.prefix.s6_addr, buf, sizeof(buf)); + lsp_debug("ISIS (%s): Adding IPv6 reachability for %s/%d", + area->area_tag, buf, ipv6->prefixlen); + + memcpy (ip6reach->prefix, ip6prefix.prefix.s6_addr, sizeof (ip6reach->prefix)); listnode_add (tlv_data.ipv6_reachs, ip6reach); } @@ -1258,7 +1630,7 @@ lsp_build_nonpseudo (struct isis_lsp *lsp, struct isis_area *area) switch (circuit->circ_type) { case CIRCUIT_T_BROADCAST: - if (level & circuit->circuit_is_type) + if (level & circuit->is_type) { if (area->oldmetric) { @@ -1268,20 +1640,33 @@ lsp_build_nonpseudo (struct isis_lsp *lsp, struct isis_area *area) tlv_data.is_neighs->del = free_tlv; } is_neigh = XCALLOC (MTYPE_ISIS_TLV, sizeof (struct is_neigh)); - if (level == 1) + if (level == IS_LEVEL_1) memcpy (is_neigh->neigh_id, circuit->u.bc.l1_desig_is, ISIS_SYS_ID_LEN + 1); else memcpy (is_neigh->neigh_id, circuit->u.bc.l2_desig_is, ISIS_SYS_ID_LEN + 1); - is_neigh->metrics = circuit->metrics[level - 1]; - listnode_add (tlv_data.is_neighs, is_neigh); - tlv_data.is_neighs->del = free_tlv; + is_neigh->metrics.metric_default = circuit->metric[level - 1]; + is_neigh->metrics.metric_expense = METRICS_UNSUPPORTED; + is_neigh->metrics.metric_error = METRICS_UNSUPPORTED; + is_neigh->metrics.metric_delay = METRICS_UNSUPPORTED; + if (!memcmp (is_neigh->neigh_id, zero_id, + ISIS_SYS_ID_LEN + 1)) + { + XFREE (MTYPE_ISIS_TLV, is_neigh); + lsp_debug("ISIS (%s): No DIS for circuit, not adding old-style IS neighbor.", + area->area_tag); + } + else + { + listnode_add (tlv_data.is_neighs, is_neigh); + lsp_debug("ISIS (%s): Adding DIS %s.%02x as old-style neighbor", + area->area_tag, sysid_print(is_neigh->neigh_id), + LSP_PSEUDO_ID(is_neigh->neigh_id)); + } } if (area->newmetric) { - uint32_t metric; - if (tlv_data.te_is_neighs == NULL) { tlv_data.te_is_neighs = list_new (); @@ -1289,23 +1674,46 @@ lsp_build_nonpseudo (struct isis_lsp *lsp, struct isis_area *area) } te_is_neigh = XCALLOC (MTYPE_ISIS_TLV, sizeof (struct te_is_neigh)); - if (level == 1) + if (level == IS_LEVEL_1) memcpy (te_is_neigh->neigh_id, circuit->u.bc.l1_desig_is, ISIS_SYS_ID_LEN + 1); else memcpy (te_is_neigh->neigh_id, circuit->u.bc.l2_desig_is, ISIS_SYS_ID_LEN + 1); if (area->oldmetric) - metric = - ((htonl(circuit->metrics[level - 1].metric_default) >> 8) - & 0xffffff); + metric = circuit->metric[level - 1]; else - metric = ((htonl(*circuit->te_metric) >> 8) & 0xffffff); - - memcpy (te_is_neigh->te_metric, &metric, 3); - listnode_add (tlv_data.te_is_neighs, te_is_neigh); + metric = circuit->te_metric[level - 1]; + SET_TE_METRIC(te_is_neigh, metric); + if (!memcmp (te_is_neigh->neigh_id, zero_id, + ISIS_SYS_ID_LEN + 1)) + { + XFREE (MTYPE_ISIS_TLV, te_is_neigh); + lsp_debug("ISIS (%s): No DIS for circuit, not adding te-style IS neighbor.", + area->area_tag); + } + else + { + /* Check if MPLS_TE is activate */ + if (IS_MPLS_TE(isisMplsTE) && HAS_LINK_PARAMS(circuit->interface)) + /* Add SubTLVs & Adjust real size of SubTLVs */ + te_is_neigh->sub_tlvs_length = add_te_subtlvs(te_is_neigh->sub_tlvs, circuit->mtc); + else + /* Or keep only TE metric with no SubTLVs if MPLS_TE is off */ + te_is_neigh->sub_tlvs_length = 0; + + listnode_add (tlv_data.te_is_neighs, te_is_neigh); + lsp_debug("ISIS (%s): Adding DIS %s.%02x as te-style neighbor", + area->area_tag, sysid_print(te_is_neigh->neigh_id), + LSP_PSEUDO_ID(te_is_neigh->neigh_id)); + } } } + else + { + lsp_debug("ISIS (%s): Circuit is not active for current level. Not adding IS neighbors", + area->area_tag); + } break; case CIRCUIT_T_P2P: nei = circuit->u.p2p.neighbor; @@ -1320,8 +1728,13 @@ lsp_build_nonpseudo (struct isis_lsp *lsp, struct isis_area *area) } is_neigh = XCALLOC (MTYPE_ISIS_TLV, sizeof (struct is_neigh)); memcpy (is_neigh->neigh_id, nei->sysid, ISIS_SYS_ID_LEN); - is_neigh->metrics = circuit->metrics[level - 1]; + is_neigh->metrics.metric_default = circuit->metric[level - 1]; + is_neigh->metrics.metric_expense = METRICS_UNSUPPORTED; + is_neigh->metrics.metric_error = METRICS_UNSUPPORTED; + is_neigh->metrics.metric_delay = METRICS_UNSUPPORTED; listnode_add (tlv_data.is_neighs, is_neigh); + lsp_debug("ISIS (%s): Adding old-style is reach for %s", area->area_tag, + sysid_print(is_neigh->neigh_id)); } if (area->newmetric) { @@ -1335,26 +1748,42 @@ lsp_build_nonpseudo (struct isis_lsp *lsp, struct isis_area *area) te_is_neigh = XCALLOC (MTYPE_ISIS_TLV, sizeof (struct te_is_neigh)); memcpy (te_is_neigh->neigh_id, nei->sysid, ISIS_SYS_ID_LEN); - metric = ((htonl(*circuit->te_metric) >> 8) & 0xffffff); - memcpy (te_is_neigh->te_metric, &metric, 3); + metric = circuit->te_metric[level - 1]; + SET_TE_METRIC(te_is_neigh, metric); + /* Check if MPLS_TE is activate */ + if (IS_MPLS_TE(isisMplsTE) && HAS_LINK_PARAMS(circuit->interface)) + /* Update Local and Remote IP address for MPLS TE circuit parameters */ + /* NOTE sure that it is the pertinent place for that updates */ + /* Local IP address could be updated in isis_circuit.c - isis_circuit_add_addr() */ + /* But, where update remote IP address ? in isis_pdu.c - process_p2p_hello() ? */ + + /* Add SubTLVs & Adjust real size of SubTLVs */ + te_is_neigh->sub_tlvs_length = add_te_subtlvs(te_is_neigh->sub_tlvs, circuit->mtc); + else + /* Or keep only TE metric with no SubTLVs if MPLS_TE is off */ + te_is_neigh->sub_tlvs_length = 0; listnode_add (tlv_data.te_is_neighs, te_is_neigh); + lsp_debug("ISIS (%s): Adding te-style is reach for %s", area->area_tag, + sysid_print(te_is_neigh->neigh_id)); } } + else + { + lsp_debug("ISIS (%s): No adjacency for given level on this circuit. Not adding IS neighbors", + area->area_tag); + } break; - case CIRCUIT_T_STATIC_IN: - zlog_warn ("lsp_area_create: unsupported circuit type"); - break; - case CIRCUIT_T_STATIC_OUT: - zlog_warn ("lsp_area_create: unsupported circuit type"); - break; - case CIRCUIT_T_DA: - zlog_warn ("lsp_area_create: unsupported circuit type"); - break; + case CIRCUIT_T_LOOPBACK: + break; default: zlog_warn ("lsp_area_create: unknown circuit type"); } } + lsp_build_ext_reach(lsp, area, &tlv_data); + + lsp_debug("ISIS (%s): LSP construction is complete. Serializing...", area->area_tag); + while (tlv_data.ipv4_int_reachs && listcount (tlv_data.ipv4_int_reachs)) { if (lsp->tlv_data.ipv4_int_reachs == NULL) @@ -1362,11 +1791,25 @@ lsp_build_nonpseudo (struct isis_lsp *lsp, struct isis_area *area) lsp_tlv_fit (lsp, &tlv_data.ipv4_int_reachs, &lsp->tlv_data.ipv4_int_reachs, IPV4_REACH_LEN, area->lsp_frag_threshold, - tlv_add_ipv4_reachs); + tlv_add_ipv4_int_reachs); if (tlv_data.ipv4_int_reachs && listcount (tlv_data.ipv4_int_reachs)) lsp = lsp_next_frag (LSP_FRAGMENT (lsp->lsp_header->lsp_id) + 1, lsp0, area, level); } + + while (tlv_data.ipv4_ext_reachs && listcount (tlv_data.ipv4_ext_reachs)) + { + if (lsp->tlv_data.ipv4_ext_reachs == NULL) + lsp->tlv_data.ipv4_ext_reachs = list_new (); + lsp_tlv_fit (lsp, &tlv_data.ipv4_ext_reachs, + &lsp->tlv_data.ipv4_ext_reachs, + IPV4_REACH_LEN, area->lsp_frag_threshold, + tlv_add_ipv4_ext_reachs); + if (tlv_data.ipv4_ext_reachs && listcount (tlv_data.ipv4_ext_reachs)) + lsp = lsp_next_frag (LSP_FRAGMENT (lsp->lsp_header->lsp_id) + 1, + lsp0, area, level); + } + /* FIXME: We pass maximum te_ipv4_reachability length to the lsp_tlv_fit() * for now. lsp_tlv_fit() needs to be fixed to deal with variable length * TLVs (sub TLVs!). */ @@ -1376,7 +1819,8 @@ lsp_build_nonpseudo (struct isis_lsp *lsp, struct isis_area *area) lsp->tlv_data.te_ipv4_reachs = list_new (); lsp_tlv_fit (lsp, &tlv_data.te_ipv4_reachs, &lsp->tlv_data.te_ipv4_reachs, - 9, area->lsp_frag_threshold, tlv_add_te_ipv4_reachs); + TE_IPV4_REACH_LEN, area->lsp_frag_threshold, + tlv_add_te_ipv4_reachs); if (tlv_data.te_ipv4_reachs && listcount (tlv_data.te_ipv4_reachs)) lsp = lsp_next_frag (LSP_FRAGMENT (lsp->lsp_header->lsp_id) + 1, lsp0, area, level); @@ -1421,87 +1865,107 @@ lsp_build_nonpseudo (struct isis_lsp *lsp, struct isis_area *area) lsp = lsp_next_frag (LSP_FRAGMENT (lsp->lsp_header->lsp_id) + 1, lsp0, area, level); } + lsp->lsp_header->pdu_len = htons (stream_get_endp (lsp->pdu)); free_tlvs (&tlv_data); + + /* Validate the LSP */ + retval = parse_tlvs (area->area_tag, STREAM_DATA (lsp->pdu) + + ISIS_FIXED_HDR_LEN + ISIS_LSP_HDR_LEN, + stream_get_endp (lsp->pdu) - + ISIS_FIXED_HDR_LEN - ISIS_LSP_HDR_LEN, + &expected, &found, &tlv_data, NULL); + assert (retval == ISIS_OK); + return; } /* - * 7.3.7 Generation on non-pseudonode LSPs + * 7.3.7 and 7.3.9 Generation on non-pseudonode LSPs */ -static int -lsp_generate_non_pseudo (struct isis_area *area, int level) +int +lsp_generate (struct isis_area *area, int level) { struct isis_lsp *oldlsp, *newlsp; u_int32_t seq_num = 0; u_char lspid[ISIS_SYS_ID_LEN + 2]; + u_int16_t rem_lifetime, refresh_time; + + if ((area == NULL) || (area->is_type & level) != level) + return ISIS_ERROR; memset (&lspid, 0, ISIS_SYS_ID_LEN + 2); memcpy (&lspid, isis->sysid, ISIS_SYS_ID_LEN); /* only builds the lsp if the area shares the level */ - if ((area->is_type & level) == level) - { - oldlsp = lsp_search (lspid, area->lspdb[level - 1]); - if (oldlsp) - { - seq_num = ntohl (oldlsp->lsp_header->seq_num); - lsp_search_and_destroy (oldlsp->lsp_header->lsp_id, - area->lspdb[level - 1]); - /* FIXME: we should actually initiate a purge */ - } - newlsp = lsp_new (lspid, area->max_lsp_lifetime[level - 1], seq_num, - area->is_type, 0, level); - newlsp->own_lsp = 1; - - lsp_insert (newlsp, area->lspdb[level - 1]); - /* build_lsp_data (newlsp, area); */ - lsp_build_nonpseudo (newlsp, area); - /* time to calculate our checksum */ - lsp_seqnum_update (newlsp); - } + oldlsp = lsp_search (lspid, area->lspdb[level - 1]); + if (oldlsp) + { + /* FIXME: we should actually initiate a purge */ + seq_num = ntohl (oldlsp->lsp_header->seq_num); + lsp_search_and_destroy (oldlsp->lsp_header->lsp_id, + area->lspdb[level - 1]); + } + rem_lifetime = lsp_rem_lifetime (area, level); + newlsp = lsp_new (area, lspid, rem_lifetime, seq_num, + area->is_type | area->overload_bit | area->attached_bit, + 0, level); + newlsp->area = area; + newlsp->own_lsp = 1; + + lsp_insert (newlsp, area->lspdb[level - 1]); + /* build_lsp_data (newlsp, area); */ + lsp_build (newlsp, area); + /* time to calculate our checksum */ + lsp_seqnum_update (newlsp); + newlsp->last_generated = time(NULL); + lsp_set_all_srmflags (newlsp); + + refresh_time = lsp_refresh_time (newlsp, rem_lifetime); + + THREAD_TIMER_OFF (area->t_lsp_refresh[level - 1]); + area->lsp_regenerate_pending[level - 1] = 0; + if (level == IS_LEVEL_1) + THREAD_TIMER_ON (master, area->t_lsp_refresh[level - 1], + lsp_l1_refresh, area, refresh_time); + else if (level == IS_LEVEL_2) + THREAD_TIMER_ON (master, area->t_lsp_refresh[level - 1], + lsp_l2_refresh, area, refresh_time); - /* DEBUG_ADJ_PACKETS */ - if (isis->debugs & DEBUG_ADJ_PACKETS) + if (isis->debugs & DEBUG_UPDATE_PACKETS) { - /* FIXME: is this place right? fix missing info */ - zlog_debug ("ISIS-Upd (%s): Building L%d LSP", area->area_tag, level); + zlog_debug ("ISIS-Upd (%s): Building L%d LSP %s, len %d, " + "seq 0x%08x, cksum 0x%04x, lifetime %us refresh %us", + area->area_tag, level, + rawlspid_print (newlsp->lsp_header->lsp_id), + ntohl (newlsp->lsp_header->pdu_len), + ntohl (newlsp->lsp_header->seq_num), + ntohs (newlsp->lsp_header->checksum), + ntohs (newlsp->lsp_header->rem_lifetime), + refresh_time); } + sched_debug("ISIS (%s): Built L%d LSP. Set triggered regenerate to non-pending.", + area->area_tag, level); return ISIS_OK; } /* - * 7.3.9 Generation of level 1 LSPs (non-pseudonode) + * Search own LSPs, update holding time and set SRM */ -int -lsp_l1_generate (struct isis_area *area) -{ - THREAD_TIMER_ON (master, area->t_lsp_refresh[0], lsp_refresh_l1, area, - MAX_LSP_GEN_INTERVAL); - - return lsp_generate_non_pseudo (area, 1); -} - -/* - * 7.3.9 Generation of level 2 LSPs (non-pseudonode) - */ -int -lsp_l2_generate (struct isis_area *area) -{ - THREAD_TIMER_ON (master, area->t_lsp_refresh[1], lsp_refresh_l2, area, - MAX_LSP_GEN_INTERVAL); - - return lsp_generate_non_pseudo (area, 2); -} - static int -lsp_non_pseudo_regenerate (struct isis_area *area, int level) +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]; + u_int16_t rem_lifetime, refresh_time; + + 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); @@ -1510,180 +1974,187 @@ lsp_non_pseudo_regenerate (struct isis_area *area, int level) if (!lsp) { - zlog_err - ("ISIS-Upd (%s): lsp_non_pseudo_regenerate(): no L%d LSP found!", - area->area_tag, level); - + zlog_err ("ISIS-Upd (%s): lsp_regenerate: no L%d LSP found!", + area->area_tag, level); return ISIS_ERROR; } lsp_clear_data (lsp); - lsp_build_nonpseudo (lsp, area); - lsp->lsp_header->rem_lifetime = htons (isis_jitter - (area->max_lsp_lifetime[level - 1], - MAX_AGE_JITTER)); + lsp_build (lsp, area); + lsp->lsp_header->lsp_bits = lsp_bits_generate (level, area->overload_bit, + area->attached_bit); + rem_lifetime = lsp_rem_lifetime (area, level); + lsp->lsp_header->rem_lifetime = htons (rem_lifetime); lsp_seqnum_update (lsp); - if (isis->debugs & DEBUG_UPDATE_PACKETS) + lsp->last_generated = time (NULL); + lsp_set_all_srmflags (lsp); + for (ALL_LIST_ELEMENTS_RO (lsp->lspu.frags, node, frag)) { - zlog_debug ("ISIS-Upd (%s): refreshing our L%d LSP %s, " - "seq 0x%08x, cksum 0x%04x lifetime %us", - area->area_tag, - level, - rawlspid_print (lsp->lsp_header->lsp_id), - ntohl (lsp->lsp_header->seq_num), - ntohs (lsp->lsp_header->checksum), - ntohs (lsp->lsp_header->rem_lifetime)); + frag->lsp_header->lsp_bits = lsp_bits_generate (level, + area->overload_bit, + area->attached_bit); + /* Set the lifetime values of all the fragments to the same value, + * so that no fragment expires before the lsp is refreshed. + */ + frag->lsp_header->rem_lifetime = htons (rem_lifetime); + lsp_set_all_srmflags (frag); } - lsp->last_generated = time (NULL); + refresh_time = lsp_refresh_time (lsp, rem_lifetime); + if (level == IS_LEVEL_1) + THREAD_TIMER_ON (master, area->t_lsp_refresh[level - 1], + lsp_l1_refresh, area, refresh_time); + else if (level == IS_LEVEL_2) + THREAD_TIMER_ON (master, area->t_lsp_refresh[level - 1], + lsp_l2_refresh, area, refresh_time); area->lsp_regenerate_pending[level - 1] = 0; - ISIS_FLAGS_SET_ALL (lsp->SRMflags); - for (ALL_LIST_ELEMENTS_RO (lsp->lspu.frags, node, frag)) + + if (isis->debugs & DEBUG_UPDATE_PACKETS) { - frag->lsp_header->rem_lifetime = htons (isis_jitter - (area-> - max_lsp_lifetime[level - 1], - MAX_AGE_JITTER)); - ISIS_FLAGS_SET_ALL (frag->SRMflags); + zlog_debug ("ISIS-Upd (%s): Refreshing our L%d LSP %s, len %d, " + "seq 0x%08x, cksum 0x%04x, lifetime %us refresh %us", + area->area_tag, level, + rawlspid_print (lsp->lsp_header->lsp_id), + ntohl (lsp->lsp_header->pdu_len), + ntohl (lsp->lsp_header->seq_num), + ntohs (lsp->lsp_header->checksum), + ntohs (lsp->lsp_header->rem_lifetime), + refresh_time); } + sched_debug("ISIS (%s): Rebuilt L%d LSP. Set triggered regenerate to non-pending.", + area->area_tag, level); - if (area->ip_circuits) - isis_spf_schedule (area, level); -#ifdef HAVE_IPV6 - if (area->ipv6_circuits) - isis_spf_schedule6 (area, level); -#endif return ISIS_OK; } /* - * Done at least every MAX_LSP_GEN_INTERVAL. Search own LSPs, update holding - * time and set SRM + * Something has changed or periodic refresh -> regenerate LSP */ -int -lsp_refresh_l1 (struct thread *thread) +static int +lsp_l1_refresh (struct thread *thread) { struct isis_area *area; - unsigned long ref_time; area = THREAD_ARG (thread); assert (area); area->t_lsp_refresh[0] = NULL; - if (area->is_type & IS_LEVEL_1) - lsp_non_pseudo_regenerate (area, 1); - - ref_time = area->lsp_refresh[0] > MAX_LSP_GEN_INTERVAL ? - MAX_LSP_GEN_INTERVAL : area->lsp_refresh[0]; + area->lsp_regenerate_pending[0] = 0; - THREAD_TIMER_ON (master, area->t_lsp_refresh[0], lsp_refresh_l1, area, - isis_jitter (ref_time, MAX_AGE_JITTER)); + if ((area->is_type & IS_LEVEL_1) == 0) + return ISIS_ERROR; - return ISIS_OK; + sched_debug("ISIS (%s): LSP L1 refresh timer expired. Refreshing LSP...", area->area_tag); + return lsp_regenerate (area, IS_LEVEL_1); } -int -lsp_refresh_l2 (struct thread *thread) +static int +lsp_l2_refresh (struct thread *thread) { struct isis_area *area; - unsigned long ref_time; area = THREAD_ARG (thread); assert (area); area->t_lsp_refresh[1] = NULL; - if (area->is_type & IS_LEVEL_2) - lsp_non_pseudo_regenerate (area, 2); - - ref_time = area->lsp_refresh[1] > MAX_LSP_GEN_INTERVAL ? - MAX_LSP_GEN_INTERVAL : area->lsp_refresh[1]; - - THREAD_TIMER_ON (master, area->t_lsp_refresh[1], lsp_refresh_l2, area, - isis_jitter (ref_time, MAX_AGE_JITTER)); - - return ISIS_OK; -} - -/* - * Something has changed -> regenerate LSP - */ - -static int -lsp_l1_regenerate (struct thread *thread) -{ - struct isis_area *area; - - area = THREAD_ARG (thread); - area->lsp_regenerate_pending[0] = 0; - - return lsp_non_pseudo_regenerate (area, 1); -} - -static int -lsp_l2_regenerate (struct thread *thread) -{ - struct isis_area *area; - - area = THREAD_ARG (thread); area->lsp_regenerate_pending[1] = 0; - return lsp_non_pseudo_regenerate (area, 2); + if ((area->is_type & IS_LEVEL_2) == 0) + return ISIS_ERROR; + + sched_debug("ISIS (%s): LSP L2 refresh timer expired. Refreshing LSP...", area->area_tag); + return lsp_regenerate (area, IS_LEVEL_2); } int -lsp_regenerate_schedule (struct isis_area *area) +lsp_regenerate_schedule (struct isis_area *area, int level, int all_pseudo) { struct isis_lsp *lsp; u_char id[ISIS_SYS_ID_LEN + 2]; time_t now, diff; + long timeout; + struct listnode *cnode; + struct isis_circuit *circuit; + int lvl; + + if (area == NULL) + return ISIS_ERROR; + + sched_debug("ISIS (%s): Scheduling regeneration of %s LSPs, %sincluding PSNs", + area->area_tag, circuit_t2string(level), all_pseudo ? "" : "not "); + memcpy (id, isis->sysid, ISIS_SYS_ID_LEN); LSP_PSEUDO_ID (id) = LSP_FRAGMENT (id) = 0; now = time (NULL); - /* - * First level 1 - */ - if (area->is_type & IS_LEVEL_1) - { - lsp = lsp_search (id, area->lspdb[0]); - if (!lsp || area->lsp_regenerate_pending[0]) - goto L2; - /* - * Throttle avoidance - */ - diff = now - lsp->last_generated; - if (diff < MIN_LSP_GEN_INTERVAL) - { - area->lsp_regenerate_pending[0] = 1; - area->t_lsp_l1_regenerate=thread_add_timer (master, lsp_l1_regenerate, area, - MIN_LSP_GEN_INTERVAL - diff); - goto L2; - } - else - lsp_non_pseudo_regenerate (area, 1); - } - /* - * then 2 - */ -L2: - if (area->is_type & IS_LEVEL_2) + + for (lvl = IS_LEVEL_1; lvl <= IS_LEVEL_2; lvl++) { - lsp = lsp_search (id, area->lspdb[1]); - if (!lsp || area->lsp_regenerate_pending[1]) - return ISIS_OK; + if (!((level & lvl) && (area->is_type & lvl))) + continue; + + sched_debug("ISIS (%s): Checking whether L%d needs to be scheduled", + area->area_tag, lvl); + + if (area->lsp_regenerate_pending[lvl - 1]) + { + struct timeval remain = thread_timer_remain(area->t_lsp_refresh[lvl - 1]); + sched_debug("ISIS (%s): Regeneration is already pending, nothing todo." + " (Due in %lld.%03lld seconds)", area->area_tag, + (long long)remain.tv_sec, (long long)remain.tv_usec / 1000); + continue; + } + + lsp = lsp_search (id, area->lspdb[lvl - 1]); + if (!lsp) + { + sched_debug("ISIS (%s): We do not have any LSPs to regenerate, nothing todo.", + area->area_tag); + continue; + } + /* * Throttle avoidance */ + sched_debug("ISIS (%s): Will schedule regen timer. Last run was: %lld, Now is: %lld", + area->area_tag, (long long)lsp->last_generated, (long long)now); + THREAD_TIMER_OFF (area->t_lsp_refresh[lvl - 1]); diff = now - lsp->last_generated; - if (diff < MIN_LSP_GEN_INTERVAL) - { - area->lsp_regenerate_pending[1] = 1; - area->t_lsp_l2_regenerate=thread_add_timer (master, lsp_l2_regenerate, area, - MIN_LSP_GEN_INTERVAL - diff); - return ISIS_OK; - } + if (diff < area->lsp_gen_interval[lvl - 1]) + { + timeout = 1000 * (area->lsp_gen_interval[lvl - 1] - diff); + sched_debug("ISIS (%s): Scheduling in %ld ms to match configured lsp_gen_interval", + area->area_tag, timeout); + } else - lsp_non_pseudo_regenerate (area, 2); + { + /* + * lsps are not regenerated if lsp_regenerate function is called + * directly. However if the lsp_regenerate call is queued for + * later execution it works. + */ + timeout = 100; + sched_debug("ISIS (%s): Last generation was more than lsp_gen_interval ago." + " Scheduling for execution in %ld ms.", area->area_tag, timeout); + } + + area->lsp_regenerate_pending[lvl - 1] = 1; + if (lvl == IS_LEVEL_1) + { + THREAD_TIMER_MSEC_ON(master, area->t_lsp_refresh[lvl - 1], + lsp_l1_refresh, area, timeout); + } + else if (lvl == IS_LEVEL_2) + { + THREAD_TIMER_MSEC_ON(master, area->t_lsp_refresh[lvl - 1], + lsp_l2_refresh, area, timeout); + } + } + + if (all_pseudo) + { + for (ALL_LIST_ELEMENTS_RO (area->circuit_list, cnode, circuit)) + lsp_regenerate_schedule_pseudo (circuit, level); } return ISIS_OK; @@ -1706,19 +2177,16 @@ lsp_build_pseudo (struct isis_lsp *lsp, struct isis_circuit *circuit, struct es_neigh *es_neigh; struct list *adj_list; struct listnode *node; - struct isis_passwd *passwd; - - assert (circuit); - assert (circuit->circ_type == CIRCUIT_T_BROADCAST); + struct isis_area *area = circuit->area; - if (!circuit->u.bc.is_dr[level - 1]) - return; /* we are not DIS on this circuit */ + lsp_debug("ISIS (%s): Constructing pseudo LSP %s for interface %s level %d", + area->area_tag, rawlspid_print(lsp->lsp_header->lsp_id), + circuit->interface->name, level); lsp->level = level; - if (level == 1) - lsp->lsp_header->lsp_bits |= IS_LEVEL_1; - else - lsp->lsp_header->lsp_bits |= IS_LEVEL_2; + /* RFC3787 section 4 SHOULD not set overload bit in pseudo LSPs */ + lsp->lsp_header->lsp_bits = lsp_bits_generate (level, 0, + circuit->area->attached_bit); /* * add self to IS neighbours @@ -1734,6 +2202,9 @@ lsp_build_pseudo (struct isis_lsp *lsp, struct isis_circuit *circuit, memcpy (&is_neigh->neigh_id, isis->sysid, ISIS_SYS_ID_LEN); listnode_add (lsp->tlv_data.is_neighs, is_neigh); + lsp_debug("ISIS (%s): Adding %s.%02x as old-style neighbor (self)", + area->area_tag, sysid_print(is_neigh->neigh_id), + LSP_PSEUDO_ID(is_neigh->neigh_id)); } if (circuit->area->newmetric) { @@ -1746,6 +2217,9 @@ lsp_build_pseudo (struct isis_lsp *lsp, struct isis_circuit *circuit, memcpy (&te_is_neigh->neigh_id, isis->sysid, ISIS_SYS_ID_LEN); listnode_add (lsp->tlv_data.te_is_neighs, te_is_neigh); + lsp_debug("ISIS (%s): Adding %s.%02x as te-style neighbor (self)", + area->area_tag, sysid_print(te_is_neigh->neigh_id), + LSP_PSEUDO_ID(te_is_neigh->neigh_id)); } adj_list = list_new (); @@ -1753,12 +2227,12 @@ lsp_build_pseudo (struct isis_lsp *lsp, struct isis_circuit *circuit, for (ALL_LIST_ELEMENTS_RO (adj_list, node, adj)) { - if (adj->circuit_t & level) + if (adj->level & level) { - if ((level == 1 && adj->sys_type == ISIS_SYSTYPE_L1_IS) || - (level == 1 && adj->sys_type == ISIS_SYSTYPE_L2_IS && + if ((level == IS_LEVEL_1 && adj->sys_type == ISIS_SYSTYPE_L1_IS) || + (level == IS_LEVEL_1 && adj->sys_type == ISIS_SYSTYPE_L2_IS && adj->adj_usage == ISIS_ADJ_LEVEL1AND2) || - (level == 2 && adj->sys_type == ISIS_SYSTYPE_L2_IS)) + (level == IS_LEVEL_2 && adj->sys_type == ISIS_SYSTYPE_L2_IS)) { /* an IS neighbour -> add it */ if (circuit->area->oldmetric) @@ -1767,6 +2241,9 @@ lsp_build_pseudo (struct isis_lsp *lsp, struct isis_circuit *circuit, memcpy (&is_neigh->neigh_id, adj->sysid, ISIS_SYS_ID_LEN); listnode_add (lsp->tlv_data.is_neighs, is_neigh); + lsp_debug("ISIS (%s): Adding %s.%02x as old-style neighbor (peer)", + area->area_tag, sysid_print(is_neigh->neigh_id), + LSP_PSEUDO_ID(is_neigh->neigh_id)); } if (circuit->area->newmetric) { @@ -1774,9 +2251,12 @@ lsp_build_pseudo (struct isis_lsp *lsp, struct isis_circuit *circuit, sizeof (struct te_is_neigh)); memcpy (&te_is_neigh->neigh_id, adj->sysid, ISIS_SYS_ID_LEN); listnode_add (lsp->tlv_data.te_is_neighs, te_is_neigh); + lsp_debug("ISIS (%s): Adding %s.%02x as te-style neighbor (peer)", + area->area_tag, sysid_print(te_is_neigh->neigh_id), + LSP_PSEUDO_ID(te_is_neigh->neigh_id)); } } - else if (level == 1 && adj->sys_type == ISIS_SYSTYPE_ES) + else if (level == IS_LEVEL_1 && adj->sys_type == ISIS_SYSTYPE_ES) { /* an ES neigbour add it, if we are building level 1 LSP */ /* FIXME: the tlv-format is hard to use here */ @@ -1789,9 +2269,24 @@ lsp_build_pseudo (struct isis_lsp *lsp, struct isis_circuit *circuit, memcpy (&es_neigh->first_es_neigh, adj->sysid, ISIS_SYS_ID_LEN); listnode_add (lsp->tlv_data.es_neighs, es_neigh); + lsp_debug("ISIS (%s): Adding %s as ES neighbor (peer)", + area->area_tag, sysid_print(es_neigh->first_es_neigh)); + } + else + { + lsp_debug("ISIS (%s): Ignoring neighbor %s, level does not match", + area->area_tag, sysid_print(adj->sysid)); } } + else + { + lsp_debug("ISIS (%s): Ignoring neighbor %s, level does not intersect", + area->area_tag, sysid_print(adj->sysid)); + } } + list_delete (adj_list); + + lsp_debug("ISIS (%s): Pseudo LSP construction is complete.", area->area_tag); /* Reset endp of stream to overwrite only TLV part of it. */ stream_reset (lsp->pdu); @@ -1800,13 +2295,7 @@ lsp_build_pseudo (struct isis_lsp *lsp, struct isis_circuit *circuit, /* * Add the authentication info if it's present */ - (level == 1) ? (passwd = &circuit->area->area_passwd) : - (passwd = &circuit->area->domain_passwd); - if (passwd->type) - { - memcpy (&lsp->tlv_data.auth_info, passwd, sizeof (struct isis_passwd)); - tlv_add_authinfo (passwd->type, passwd->len, passwd->passwd, lsp->pdu); - } + lsp_auth_add (lsp); if (lsp->tlv_data.is_neighs && listcount (lsp->tlv_data.is_neighs) > 0) tlv_add_is_neighs (lsp->tlv_data.is_neighs, lsp->pdu); @@ -1818,20 +2307,91 @@ lsp_build_pseudo (struct isis_lsp *lsp, struct isis_circuit *circuit, tlv_add_is_neighs (lsp->tlv_data.es_neighs, lsp->pdu); lsp->lsp_header->pdu_len = htons (stream_get_endp (lsp->pdu)); - fletcher_checksum (STREAM_DATA (lsp->pdu) + 12, - ntohs (lsp->lsp_header->pdu_len) - 12, 12); - list_delete (adj_list); + /* Recompute authentication and checksum information */ + lsp_auth_update (lsp); + fletcher_checksum(STREAM_DATA (lsp->pdu) + 12, + ntohs (lsp->lsp_header->pdu_len) - 12, 12); return; } +int +lsp_generate_pseudo (struct isis_circuit *circuit, int level) +{ + dict_t *lspdb = circuit->area->lspdb[level - 1]; + struct isis_lsp *lsp; + u_char lsp_id[ISIS_SYS_ID_LEN + 2]; + u_int16_t rem_lifetime, refresh_time; + + if ((circuit->is_type & level) != level || + (circuit->state != C_STATE_UP) || + (circuit->circ_type != CIRCUIT_T_BROADCAST) || + (circuit->u.bc.is_dr[level - 1] == 0)) + return ISIS_ERROR; + + memcpy (lsp_id, isis->sysid, ISIS_SYS_ID_LEN); + LSP_FRAGMENT (lsp_id) = 0; + LSP_PSEUDO_ID (lsp_id) = circuit->circuit_id; + + /* + * If for some reason have a pseudo LSP in the db already -> regenerate + */ + if (lsp_search (lsp_id, lspdb)) + return lsp_regenerate_schedule_pseudo (circuit, level); + + rem_lifetime = lsp_rem_lifetime (circuit->area, level); + /* RFC3787 section 4 SHOULD not set overload bit in pseudo LSPs */ + lsp = lsp_new (circuit->area, lsp_id, rem_lifetime, 1, + circuit->area->is_type | circuit->area->attached_bit, + 0, level); + lsp->area = circuit->area; + + lsp_build_pseudo (lsp, circuit, level); + + lsp->own_lsp = 1; + lsp_insert (lsp, lspdb); + lsp_set_all_srmflags (lsp); + + refresh_time = lsp_refresh_time (lsp, rem_lifetime); + THREAD_TIMER_OFF (circuit->u.bc.t_refresh_pseudo_lsp[level - 1]); + circuit->lsp_regenerate_pending[level - 1] = 0; + if (level == IS_LEVEL_1) + THREAD_TIMER_ON (master, circuit->u.bc.t_refresh_pseudo_lsp[level - 1], + lsp_l1_refresh_pseudo, circuit, refresh_time); + else if (level == IS_LEVEL_2) + THREAD_TIMER_ON (master, circuit->u.bc.t_refresh_pseudo_lsp[level - 1], + lsp_l2_refresh_pseudo, circuit, refresh_time); + + if (isis->debugs & DEBUG_UPDATE_PACKETS) + { + zlog_debug ("ISIS-Upd (%s): Building L%d Pseudo LSP %s, len %d, " + "seq 0x%08x, cksum 0x%04x, lifetime %us, refresh %us", + circuit->area->area_tag, level, + rawlspid_print (lsp->lsp_header->lsp_id), + ntohl (lsp->lsp_header->pdu_len), + ntohl (lsp->lsp_header->seq_num), + ntohs (lsp->lsp_header->checksum), + ntohs (lsp->lsp_header->rem_lifetime), + refresh_time); + } + + return ISIS_OK; +} + static int -lsp_pseudo_regenerate (struct isis_circuit *circuit, int level) +lsp_regenerate_pseudo (struct isis_circuit *circuit, int level) { dict_t *lspdb = circuit->area->lspdb[level - 1]; struct isis_lsp *lsp; u_char lsp_id[ISIS_SYS_ID_LEN + 2]; + u_int16_t rem_lifetime, refresh_time; + + if ((circuit->is_type & level) != level || + (circuit->state != C_STATE_UP) || + (circuit->circ_type != CIRCUIT_T_BROADCAST) || + (circuit->u.bc.is_dr[level - 1] == 0)) + return ISIS_ERROR; memcpy (lsp_id, isis->sysid, ISIS_SYS_ID_LEN); LSP_PSEUDO_ID (lsp_id) = circuit->circuit_id; @@ -1841,151 +2401,195 @@ lsp_pseudo_regenerate (struct isis_circuit *circuit, int level) if (!lsp) { - zlog_err ("lsp_pseudo_regenerate(): no l%d LSP %s found!", level, - rawlspid_print (lsp_id)); + zlog_err ("lsp_regenerate_pseudo: no l%d LSP %s found!", + level, rawlspid_print (lsp_id)); return ISIS_ERROR; } lsp_clear_data (lsp); lsp_build_pseudo (lsp, circuit, level); - lsp->lsp_header->rem_lifetime = - htons (isis_jitter (circuit->area->max_lsp_lifetime[level - 1], - MAX_AGE_JITTER)); - + /* RFC3787 section 4 SHOULD not set overload bit in pseudo LSPs */ + lsp->lsp_header->lsp_bits = lsp_bits_generate (level, 0, + circuit->area->attached_bit); + rem_lifetime = lsp_rem_lifetime (circuit->area, level); + lsp->lsp_header->rem_lifetime = htons (rem_lifetime); lsp_inc_seqnum (lsp, 0); + lsp->last_generated = time (NULL); + lsp_set_all_srmflags (lsp); + + refresh_time = lsp_refresh_time (lsp, rem_lifetime); + if (level == IS_LEVEL_1) + THREAD_TIMER_ON (master, circuit->u.bc.t_refresh_pseudo_lsp[level - 1], + lsp_l1_refresh_pseudo, circuit, refresh_time); + else if (level == IS_LEVEL_2) + THREAD_TIMER_ON (master, circuit->u.bc.t_refresh_pseudo_lsp[level - 1], + lsp_l2_refresh_pseudo, circuit, refresh_time); if (isis->debugs & DEBUG_UPDATE_PACKETS) { - zlog_debug ("ISIS-Upd (%s): refreshing pseudo LSP L%d %s", - circuit->area->area_tag, level, - rawlspid_print (lsp->lsp_header->lsp_id)); + zlog_debug ("ISIS-Upd (%s): Refreshing L%d Pseudo LSP %s, len %d, " + "seq 0x%08x, cksum 0x%04x, lifetime %us, refresh %us", + circuit->area->area_tag, level, + rawlspid_print (lsp->lsp_header->lsp_id), + ntohl (lsp->lsp_header->pdu_len), + ntohl (lsp->lsp_header->seq_num), + ntohs (lsp->lsp_header->checksum), + ntohs (lsp->lsp_header->rem_lifetime), + refresh_time); } - lsp->last_generated = time (NULL); - ISIS_FLAGS_SET_ALL (lsp->SRMflags); - return ISIS_OK; } -int +/* + * Something has changed or periodic refresh -> regenerate pseudo LSP + */ +static int lsp_l1_refresh_pseudo (struct thread *thread) { struct isis_circuit *circuit; - int retval; - unsigned long ref_time; + u_char id[ISIS_SYS_ID_LEN + 2]; circuit = THREAD_ARG (thread); - if (!circuit->u.bc.is_dr[0]) - return ISIS_ERROR; /* FIXME: purge and such */ - circuit->u.bc.t_refresh_pseudo_lsp[0] = NULL; + circuit->lsp_regenerate_pending[0] = 0; - retval = lsp_pseudo_regenerate (circuit, 1); - - ref_time = circuit->area->lsp_refresh[0] > MAX_LSP_GEN_INTERVAL ? - MAX_LSP_GEN_INTERVAL : circuit->area->lsp_refresh[0]; - - THREAD_TIMER_ON (master, circuit->u.bc.t_refresh_pseudo_lsp[0], - lsp_l1_refresh_pseudo, circuit, - isis_jitter (ref_time, MAX_AGE_JITTER)); - - return retval; -} - -int -lsp_l1_pseudo_generate (struct isis_circuit *circuit) -{ - struct isis_lsp *lsp; - u_char id[ISIS_SYS_ID_LEN + 2]; - unsigned long ref_time; - - memcpy (id, isis->sysid, ISIS_SYS_ID_LEN); - LSP_FRAGMENT (id) = 0; - LSP_PSEUDO_ID (id) = circuit->circuit_id; - - /* - * If for some reason have a pseudo LSP in the db already -> regenerate - */ - if (lsp_search (id, circuit->area->lspdb[0])) - return lsp_pseudo_regenerate (circuit, 1); - lsp = lsp_new (id, circuit->area->max_lsp_lifetime[0], - 1, circuit->area->is_type, 0, 1); - - lsp_build_pseudo (lsp, circuit, 1); - - lsp->own_lsp = 1; - lsp_insert (lsp, circuit->area->lspdb[0]); - ISIS_FLAGS_SET_ALL (lsp->SRMflags); - - ref_time = circuit->area->lsp_refresh[0] > MAX_LSP_GEN_INTERVAL ? - MAX_LSP_GEN_INTERVAL : circuit->area->lsp_refresh[0]; - - THREAD_TIMER_ON (master, circuit->u.bc.t_refresh_pseudo_lsp[0], - lsp_l1_refresh_pseudo, circuit, - isis_jitter (ref_time, MAX_AGE_JITTER)); + if ((circuit->u.bc.is_dr[0] == 0) || + (circuit->is_type & IS_LEVEL_1) == 0) + { + memcpy (id, isis->sysid, ISIS_SYS_ID_LEN); + LSP_PSEUDO_ID (id) = circuit->circuit_id; + LSP_FRAGMENT (id) = 0; + lsp_purge_pseudo (id, circuit, IS_LEVEL_1); + return ISIS_ERROR; + } - return lsp_regenerate_schedule (circuit->area); + return lsp_regenerate_pseudo (circuit, IS_LEVEL_1); } -int +static int lsp_l2_refresh_pseudo (struct thread *thread) { struct isis_circuit *circuit; - int retval; - unsigned long ref_time; - circuit = THREAD_ARG (thread); + u_char id[ISIS_SYS_ID_LEN + 2]; - if (!circuit->u.bc.is_dr[1]) - return ISIS_ERROR; /* FIXME: purge and such */ + circuit = THREAD_ARG (thread); circuit->u.bc.t_refresh_pseudo_lsp[1] = NULL; + circuit->lsp_regenerate_pending[1] = 0; - retval = lsp_pseudo_regenerate (circuit, 2); - - ref_time = circuit->area->lsp_refresh[1] > MAX_LSP_GEN_INTERVAL ? - MAX_LSP_GEN_INTERVAL : circuit->area->lsp_refresh[1]; - - THREAD_TIMER_ON (master, circuit->u.bc.t_refresh_pseudo_lsp[1], - lsp_l2_refresh_pseudo, circuit, - isis_jitter (ref_time, MAX_AGE_JITTER)); + if ((circuit->u.bc.is_dr[1] == 0) || + (circuit->is_type & IS_LEVEL_2) == 0) + { + memcpy (id, isis->sysid, ISIS_SYS_ID_LEN); + LSP_PSEUDO_ID (id) = circuit->circuit_id; + LSP_FRAGMENT (id) = 0; + lsp_purge_pseudo (id, circuit, IS_LEVEL_2); + return ISIS_ERROR; + } - return retval; + return lsp_regenerate_pseudo (circuit, IS_LEVEL_2); } int -lsp_l2_pseudo_generate (struct isis_circuit *circuit) +lsp_regenerate_schedule_pseudo (struct isis_circuit *circuit, int level) { struct isis_lsp *lsp; - u_char id[ISIS_SYS_ID_LEN + 2]; - unsigned long ref_time; - - memcpy (id, isis->sysid, ISIS_SYS_ID_LEN); - LSP_FRAGMENT (id) = 0; - LSP_PSEUDO_ID (id) = circuit->circuit_id; - - if (lsp_search (id, circuit->area->lspdb[1])) - return lsp_pseudo_regenerate (circuit, 2); - - lsp = lsp_new (id, circuit->area->max_lsp_lifetime[1], - 1, circuit->area->is_type, 0, 2); + u_char lsp_id[ISIS_SYS_ID_LEN + 2]; + time_t now, diff; + long timeout; + int lvl; + struct isis_area *area = circuit->area; - lsp_build_pseudo (lsp, circuit, 2); + if (circuit == NULL || + circuit->circ_type != CIRCUIT_T_BROADCAST || + circuit->state != C_STATE_UP) + return ISIS_OK; - ref_time = circuit->area->lsp_refresh[1] > MAX_LSP_GEN_INTERVAL ? - MAX_LSP_GEN_INTERVAL : circuit->area->lsp_refresh[1]; + sched_debug("ISIS (%s): Scheduling regeneration of %s pseudo LSP for interface %s", + area->area_tag, circuit_t2string(level), circuit->interface->name); + memcpy (lsp_id, isis->sysid, ISIS_SYS_ID_LEN); + LSP_PSEUDO_ID (lsp_id) = circuit->circuit_id; + LSP_FRAGMENT (lsp_id) = 0; + now = time (NULL); - lsp->own_lsp = 1; - lsp_insert (lsp, circuit->area->lspdb[1]); - ISIS_FLAGS_SET_ALL (lsp->SRMflags); + for (lvl = IS_LEVEL_1; lvl <= IS_LEVEL_2; lvl++) + { + sched_debug("ISIS (%s): Checking whether L%d pseudo LSP needs to be scheduled", + area->area_tag, lvl); + + if (!((level & lvl) && (circuit->is_type & lvl))) + { + sched_debug("ISIS (%s): Level is not active on circuit", + area->area_tag); + continue; + } + + if (circuit->u.bc.is_dr[lvl - 1] == 0) + { + sched_debug("ISIS (%s): This IS is not DR, nothing to do.", + area->area_tag); + continue; + } + + if (circuit->lsp_regenerate_pending[lvl - 1]) + { + struct timeval remain = + thread_timer_remain(circuit->u.bc.t_refresh_pseudo_lsp[lvl - 1]); + sched_debug("ISIS (%s): Regenerate is already pending, nothing todo." + " (Due in %lld.%03lld seconds)", area->area_tag, + (long long)remain.tv_sec, (long long)remain.tv_usec/1000); + continue; + } + + lsp = lsp_search (lsp_id, circuit->area->lspdb[lvl - 1]); + if (!lsp) + { + sched_debug("ISIS (%s): Pseudonode LSP does not exist yet, nothing to regenerate.", + area->area_tag); + continue; + } - THREAD_TIMER_ON (master, circuit->u.bc.t_refresh_pseudo_lsp[1], - lsp_l2_refresh_pseudo, circuit, - isis_jitter (ref_time, MAX_AGE_JITTER)); + /* + * Throttle avoidance + */ + sched_debug("ISIS (%s): Will schedule PSN regen timer. Last run was: %lld, Now is: %lld", + area->area_tag, (long long)lsp->last_generated, (long long) now); + THREAD_TIMER_OFF (circuit->u.bc.t_refresh_pseudo_lsp[lvl - 1]); + diff = now - lsp->last_generated; + if (diff < circuit->area->lsp_gen_interval[lvl - 1]) + { + timeout = 1000 * (circuit->area->lsp_gen_interval[lvl - 1] - diff); + sched_debug("ISIS (%s): Sechduling in %ld ms to match configured lsp_gen_interval", + area->area_tag, timeout); + } + else + { + timeout = 100; + sched_debug("ISIS (%s): Last generation was more than lsp_gen_interval ago." + " Scheduling for execution in %ld ms.", area->area_tag, timeout); + } + + circuit->lsp_regenerate_pending[lvl - 1] = 1; + + if (lvl == IS_LEVEL_1) + { + THREAD_TIMER_MSEC_ON(master, + circuit->u.bc.t_refresh_pseudo_lsp[lvl - 1], + lsp_l1_refresh_pseudo, circuit, timeout); + } + else if (lvl == IS_LEVEL_2) + { + THREAD_TIMER_MSEC_ON(master, + circuit->u.bc.t_refresh_pseudo_lsp[lvl - 1], + lsp_l2_refresh_pseudo, circuit, timeout); + } + } - return lsp_regenerate_schedule (circuit->area); + return ISIS_OK; } /* @@ -2003,6 +2607,7 @@ lsp_tick (struct thread *thread) struct listnode *lspnode, *cnode; dnode_t *dnode, *dnode_next; int level; + u_int16_t rem_lifetime; lsp_list = list_new (); @@ -2018,56 +2623,87 @@ lsp_tick (struct thread *thread) for (level = 0; level < ISIS_LEVELS; level++) { if (area->lspdb[level] && dict_count (area->lspdb[level]) > 0) - { - dnode = dict_first (area->lspdb[level]); - while (dnode != NULL) - { - dnode_next = dict_next (area->lspdb[level], dnode); - lsp = dnode_get (dnode); - lsp_set_time (lsp); - if (lsp->age_out == 0) - { - - zlog_debug ("ISIS-Upd (%s): L%u LSP %s seq 0x%08x aged out", - area->area_tag, - lsp->level, - rawlspid_print (lsp->lsp_header->lsp_id), - ntohl (lsp->lsp_header->seq_num)); + { + for (dnode = dict_first (area->lspdb[level]); + dnode != NULL; dnode = dnode_next) + { + dnode_next = dict_next (area->lspdb[level], dnode); + lsp = dnode_get (dnode); + + /* + * The lsp rem_lifetime is kept at 0 for MaxAge or + * ZeroAgeLifetime depending on explicit purge or + * natural age out. So schedule spf only once when + * the first time rem_lifetime becomes 0. + */ + rem_lifetime = ntohs(lsp->lsp_header->rem_lifetime); + lsp_set_time (lsp); + + /* + * Schedule may run spf which should be done only after + * the lsp rem_lifetime becomes 0 for the first time. + * ISO 10589 - 7.3.16.4 first paragraph. + */ + if (rem_lifetime == 1 && lsp->lsp_header->seq_num != 0) + { + /* 7.3.16.4 a) set SRM flags on all */ + lsp_set_all_srmflags (lsp); + /* 7.3.16.4 b) retain only the header FIXME */ + /* 7.3.16.4 c) record the time to purge FIXME */ + /* run/schedule spf */ + /* isis_spf_schedule is called inside lsp_destroy() below; + * so it is not needed here. */ + /* isis_spf_schedule (lsp->area, lsp->level); */ + } + + if (lsp->age_out == 0) + { + zlog_debug ("ISIS-Upd (%s): L%u LSP %s seq 0x%08x aged out", + area->area_tag, + lsp->level, + rawlspid_print (lsp->lsp_header->lsp_id), + ntohl (lsp->lsp_header->seq_num)); #ifdef TOPOLOGY_GENERATE - if (lsp->from_topology) - THREAD_TIMER_OFF (lsp->t_lsp_top_ref); + if (lsp->from_topology) + THREAD_TIMER_OFF (lsp->t_lsp_top_ref); #endif /* TOPOLOGY_GENERATE */ - lsp_destroy (lsp); - dict_delete (area->lspdb[level], dnode); - } - else if (flags_any_set (lsp->SRMflags)) - listnode_add (lsp_list, lsp); - dnode = dnode_next; - } - - /* - * Send LSPs on circuits indicated by the SRMflags - */ - if (listcount (lsp_list) > 0) - { + lsp_destroy (lsp); + lsp = NULL; + dict_delete_free (area->lspdb[level], dnode); + } + else if (flags_any_set (lsp->SRMflags)) + listnode_add (lsp_list, lsp); + } + + /* + * Send LSPs on circuits indicated by the SRMflags + */ + if (listcount (lsp_list) > 0) + { for (ALL_LIST_ELEMENTS_RO (area->circuit_list, cnode, circuit)) - { - if (circuit->state != C_STATE_UP) - continue; + { + int diff = time (NULL) - circuit->lsp_queue_last_cleared; + if (circuit->lsp_queue == NULL || + diff < MIN_LSP_TRANS_INTERVAL) + continue; for (ALL_LIST_ELEMENTS_RO (lsp_list, lspnode, lsp)) - { - if (ISIS_CHECK_FLAG (lsp->SRMflags, circuit)) - { - /* FIXME: if same or elder lsp is already in lsp - * queue */ - listnode_add (circuit->lsp_queue, lsp); - thread_add_event (master, send_lsp, circuit, 0); - } - } - } - } - list_delete_all_node (lsp_list); - } + { + if (circuit->upadjcount[lsp->level - 1] && + ISIS_CHECK_FLAG (lsp->SRMflags, circuit)) + { + /* Add the lsp only if it is not already in lsp + * queue */ + if (! listnode_lookup (circuit->lsp_queue, lsp)) + { + listnode_add (circuit->lsp_queue, lsp); + thread_add_event (master, send_lsp, circuit, 0); + } + } + } + } + list_delete_all_node (lsp_list); + } + } } list_delete (lsp_list); @@ -2076,22 +2712,45 @@ lsp_tick (struct thread *thread) } void -lsp_purge_dr (u_char * id, struct isis_circuit *circuit, int level) +lsp_purge_pseudo (u_char * id, struct isis_circuit *circuit, int level) { struct isis_lsp *lsp; + u_int16_t seq_num; + u_int8_t lsp_bits; lsp = lsp_search (id, circuit->area->lspdb[level - 1]); + if (!lsp) + return; - if (lsp && lsp->purged == 0) - { - lsp->lsp_header->rem_lifetime = htons (0); - lsp->lsp_header->pdu_len = - htons (ISIS_FIXED_HDR_LEN + ISIS_LSP_HDR_LEN); - lsp->purged = 0; - fletcher_checksum (STREAM_DATA (lsp->pdu) + 12, - ntohs (lsp->lsp_header->pdu_len) - 12, 12); - ISIS_FLAGS_SET_ALL (lsp->SRMflags); - } + /* store old values */ + seq_num = lsp->lsp_header->seq_num; + lsp_bits = lsp->lsp_header->lsp_bits; + + /* reset stream */ + lsp_clear_data (lsp); + stream_reset (lsp->pdu); + + /* update header */ + lsp->lsp_header->pdu_len = htons (ISIS_FIXED_HDR_LEN + ISIS_LSP_HDR_LEN); + memcpy (lsp->lsp_header->lsp_id, id, ISIS_SYS_ID_LEN + 2); + lsp->lsp_header->checksum = 0; + lsp->lsp_header->seq_num = seq_num; + lsp->lsp_header->rem_lifetime = 0; + lsp->lsp_header->lsp_bits = lsp_bits; + lsp->level = level; + lsp->age_out = lsp->area->max_lsp_lifetime[level-1]; + stream_forward_endp (lsp->pdu, ISIS_FIXED_HDR_LEN + ISIS_LSP_HDR_LEN); + + /* + * Add and update the authentication info if its present + */ + lsp_auth_add (lsp); + lsp->lsp_header->pdu_len = htons (stream_get_endp (lsp->pdu)); + lsp_auth_update (lsp); + fletcher_checksum(STREAM_DATA (lsp->pdu) + 12, + ntohs (lsp->lsp_header->pdu_len) - 12, 12); + + lsp_set_all_srmflags (lsp); return; } @@ -2101,7 +2760,8 @@ lsp_purge_dr (u_char * id, struct isis_circuit *circuit, int level) * -> Do as in 7.3.16.4 */ void -lsp_purge_non_exist (struct isis_link_state_hdr *lsp_hdr, +lsp_purge_non_exist (int level, + struct isis_link_state_hdr *lsp_hdr, struct isis_area *area) { struct isis_lsp *lsp; @@ -2109,27 +2769,34 @@ lsp_purge_non_exist (struct isis_link_state_hdr *lsp_hdr, /* * We need to create the LSP to be purged */ - zlog_debug ("LSP PURGE NON EXIST"); lsp = XCALLOC (MTYPE_ISIS_LSP, sizeof (struct isis_lsp)); - /*FIXME: BUG BUG BUG! the lsp doesn't exist here! */ - /*did smt here, maybe good probably not */ - lsp->level = ((lsp_hdr->lsp_bits & LSPBIT_IST) == IS_LEVEL_1) ? 1 : 2; - lsp->pdu = stream_new (ISIS_FIXED_HDR_LEN + ISIS_LSP_HDR_LEN); + lsp->area = area; + lsp->level = level; + lsp->pdu = stream_new(LLC_LEN + area->lsp_mtu); lsp->isis_header = (struct isis_fixed_hdr *) STREAM_DATA (lsp->pdu); - fill_fixed_hdr (lsp->isis_header, (lsp->level == 1) ? L1_LINK_STATE + fill_fixed_hdr (lsp->isis_header, (lsp->level == IS_LEVEL_1) ? L1_LINK_STATE : L2_LINK_STATE); lsp->lsp_header = (struct isis_link_state_hdr *) (STREAM_DATA (lsp->pdu) + ISIS_FIXED_HDR_LEN); memcpy (lsp->lsp_header, lsp_hdr, ISIS_LSP_HDR_LEN); + stream_forward_endp (lsp->pdu, ISIS_FIXED_HDR_LEN + ISIS_LSP_HDR_LEN); - /* - * Retain only LSP header - */ - lsp->lsp_header->pdu_len = htons (ISIS_FIXED_HDR_LEN + ISIS_LSP_HDR_LEN); /* * Set the remaining lifetime to 0 */ lsp->lsp_header->rem_lifetime = 0; + + /* + * Add and update the authentication info if its present + */ + lsp_auth_add (lsp); + lsp_auth_update (lsp); + + /* + * Update the PDU length to header plus any authentication TLV. + */ + lsp->lsp_header->pdu_len = htons (stream_get_endp (lsp->pdu)); + /* * Put the lsp into LSPdb */ @@ -2138,17 +2805,36 @@ lsp_purge_non_exist (struct isis_link_state_hdr *lsp_hdr, /* * Send in to whole area */ - ISIS_FLAGS_SET_ALL (lsp->SRMflags); + lsp_set_all_srmflags (lsp); return; } +void lsp_set_all_srmflags (struct isis_lsp *lsp) +{ + struct listnode *node; + struct isis_circuit *circuit; + + assert (lsp); + + ISIS_FLAGS_CLEAR_ALL(lsp->SRMflags); + + if (lsp->area) + { + struct list *circuit_list = lsp->area->circuit_list; + for (ALL_LIST_ELEMENTS_RO (circuit_list, node, circuit)) + { + ISIS_SET_FLAG(lsp->SRMflags, circuit); + } + } +} + #ifdef TOPOLOGY_GENERATE static int top_lsp_refresh (struct thread *thread) { struct isis_lsp *lsp; - unsigned long ref_time; + u_int16_t rem_lifetime; lsp = THREAD_ARG (thread); assert (lsp); @@ -2157,7 +2843,7 @@ top_lsp_refresh (struct thread *thread) lsp_seqnum_update (lsp); - ISIS_FLAGS_SET_ALL (lsp->SRMflags); + lsp_set_all_srmflags (lsp); if (isis->debugs & DEBUG_UPDATE_PACKETS) { zlog_debug ("ISIS-Upd (): refreshing Topology L1 %s", @@ -2167,14 +2853,15 @@ top_lsp_refresh (struct thread *thread) isis_dynhn_insert (lsp->lsp_header->lsp_id, lsp->tlv_data.hostname, IS_LEVEL_1); - lsp->lsp_header->rem_lifetime = - htons (isis_jitter (lsp->area->max_lsp_lifetime[0], MAX_AGE_JITTER)); - - ref_time = lsp->area->lsp_refresh[0] > MAX_LSP_GEN_INTERVAL ? - MAX_LSP_GEN_INTERVAL : lsp->area->lsp_refresh[0]; + lsp->lsp_header->lsp_bits = lsp_bits_generate (lsp->level, + lsp->area->overload_bit, + lsp->area->attached_bit); + rem_lifetime = lsp_rem_lifetime (lsp->area, IS_LEVEL_1); + lsp->lsp_header->rem_lifetime = htons (rem_lifetime); + /* refresh_time = lsp_refresh_time (lsp, rem_lifetime); */ THREAD_TIMER_ON (master, lsp->t_lsp_top_ref, top_lsp_refresh, lsp, - isis_jitter (ref_time, MAX_LSP_GEN_JITTER)); + lsp->area->lsp_refresh[0]); return ISIS_OK; } @@ -2187,16 +2874,16 @@ generate_topology_lsps (struct isis_area *area) struct arc *arc; u_char lspid[ISIS_SYS_ID_LEN + 2]; struct isis_lsp *lsp; - unsigned long ref_time; + u_int16_t rem_lifetime, refresh_time; /* first we find the maximal node */ for (ALL_LIST_ELEMENTS_RO (area->topology, node, arc)) - { - if (arc->from_node > max) - max = arc->from_node; - if (arc->to_node > max) - max = arc->to_node; - } + { + if (arc->from_node > max) + max = arc->from_node; + if (arc->to_node > max) + max = arc->to_node; + } for (i = 1; i < (max + 1); i++) { @@ -2206,12 +2893,13 @@ generate_topology_lsps (struct isis_area *area) lspid[ISIS_SYS_ID_LEN - 1] = (i & 0xFF); lspid[ISIS_SYS_ID_LEN - 2] = ((i >> 8) & 0xFF); - lsp = lsp_new (lspid, isis_jitter (area->max_lsp_lifetime[0], - MAX_AGE_JITTER), 1, IS_LEVEL_1, 0, 1); + rem_lifetime = lsp_rem_lifetime (area, IS_LEVEL_1); + lsp = lsp_new (area, lspid, rem_lifetime, 1, + IS_LEVEL_1 | area->overload_bit | area->attached_bit, + 0, 1); if (!lsp) return; lsp->from_topology = 1; - lsp->area = area; /* Creating LSP data based on topology info. */ build_topology_lsp_data (lsp, area, i); @@ -2220,12 +2908,10 @@ generate_topology_lsps (struct isis_area *area) /* Take care of inserting dynamic hostname into cache. */ isis_dynhn_insert (lspid, lsp->tlv_data.hostname, IS_LEVEL_1); - ref_time = area->lsp_refresh[0] > MAX_LSP_GEN_INTERVAL ? - MAX_LSP_GEN_INTERVAL : area->lsp_refresh[0]; - + refresh_time = lsp_refresh_time (lsp, rem_lifetime); THREAD_TIMER_ON (master, lsp->t_lsp_top_ref, top_lsp_refresh, lsp, - isis_jitter (ref_time, MAX_LSP_GEN_JITTER)); - ISIS_FLAGS_SET_ALL (lsp->SRMflags); + refresh_time); + lsp_set_all_srmflags (lsp); lsp_insert (lsp, area->lspdb[0]); } } @@ -2342,8 +3028,6 @@ build_topology_lsp_data (struct isis_lsp *lsp, struct isis_area *area, if (area->newmetric) { - uint32_t metric; - if (tlv_data.te_is_neighs == NULL) { tlv_data.te_is_neighs = list_new (); @@ -2354,8 +3038,7 @@ build_topology_lsp_data (struct isis_lsp *lsp, struct isis_area *area, ISIS_SYS_ID_LEN); te_is_neigh->neigh_id[ISIS_SYS_ID_LEN - 1] = (to_lsp & 0xFF); te_is_neigh->neigh_id[ISIS_SYS_ID_LEN - 2] = ((to_lsp >> 8) & 0xFF); - metric = ((htonl(arc->distance) >> 8) & 0xffffff); - memcpy (te_is_neigh->te_metric, &metric, 3); + SET_TE_METRIC(te_is_neigh, arc->distance); listnode_add (tlv_data.te_is_neighs, te_is_neigh); } } diff --git a/isisd/isis_lsp.h b/isisd/isis_lsp.h index 8d648d059..a35bfa762 100644 --- a/isisd/isis_lsp.h +++ b/isisd/isis_lsp.h @@ -24,10 +24,6 @@ #ifndef _ZEBRA_ISIS_LSP_H #define _ZEBRA_ISIS_LSP_H -/* The grand plan is to support 1024 circuits so we have 32*32 bit flags - * the support will be achived using the newest drafts */ -#define ISIS_MAX_CIRCUITS 32 /* = 1024 - FIXME:defined in flags.h as well */ - /* Structure for isis_lsp, this structure will only support the fixed * System ID (Currently 6) (atleast for now). In order to support more * We will have to split the header into two parts, and for readability @@ -42,15 +38,13 @@ struct isis_lsp struct list *frags; struct isis_lsp *zero_lsp; } lspu; + u_int32_t auth_tlv_offset; /* authentication TLV position in the pdu */ u_int32_t SRMflags[ISIS_MAX_CIRCUITS]; u_int32_t SSNflags[ISIS_MAX_CIRCUITS]; - u_int32_t rexmit_queue[ISIS_MAX_CIRCUITS]; int level; /* L1 or L2? */ - int purged; /* have purged this one */ int scheduled; /* scheduled for sending */ time_t installed; time_t last_generated; - time_t last_sent; int own_lsp; #ifdef TOPOLOGY_GENERATE int from_topology; @@ -58,7 +52,6 @@ struct isis_lsp #endif /* used for 60 second counting when rem_lifetime is zero */ int age_out; - /* FIXME: For now only topology LSP's use this. Is it helpful for others? */ struct isis_area *area; struct tlvs tlv_data; /* Simplifies TLV access */ }; @@ -67,38 +60,35 @@ dict_t *lsp_db_init (void); void lsp_db_destroy (dict_t * lspdb); int lsp_tick (struct thread *thread); -int lsp_l1_generate (struct isis_area *area); -int lsp_l2_generate (struct isis_area *area); -int lsp_refresh_l1 (struct thread *thread); -int lsp_refresh_l2 (struct thread *thread); -int lsp_regenerate_schedule (struct isis_area *area); +int lsp_generate (struct isis_area *area, int level); +int lsp_regenerate_schedule (struct isis_area *area, int level, + int all_pseudo); +int lsp_generate_pseudo (struct isis_circuit *circuit, int level); +int lsp_regenerate_schedule_pseudo (struct isis_circuit *circuit, int level); -int lsp_l1_pseudo_generate (struct isis_circuit *circuit); -int lsp_l2_pseudo_generate (struct isis_circuit *circuit); -int lsp_l1_refresh_pseudo (struct thread *thread); -int lsp_l2_refresh_pseudo (struct thread *thread); -int isis_lsp_authinfo_check (struct stream *stream, struct isis_area *area, - int pdulen, struct isis_passwd *passwd); -struct isis_lsp *lsp_new (u_char * lsp_id, u_int16_t rem_lifetime, +struct isis_lsp *lsp_new (struct isis_area *area, u_char * lsp_id, + u_int16_t rem_lifetime, u_int32_t seq_num, u_int8_t lsp_bits, u_int16_t checksum, int level); struct isis_lsp *lsp_new_from_stream_ptr (struct stream *stream, u_int16_t pdu_len, struct isis_lsp *lsp0, - struct isis_area *area); + struct isis_area *area, + int level); void lsp_insert (struct isis_lsp *lsp, dict_t * lspdb); struct isis_lsp *lsp_search (u_char * id, dict_t * lspdb); -void lsp_build_list (u_char * start_id, u_char * stop_id, +void lsp_build_list (u_char * start_id, u_char * stop_id, u_char num_lsps, struct list *list, dict_t * lspdb); void lsp_build_list_nonzero_ht (u_char * start_id, u_char * stop_id, struct list *list, dict_t * lspdb); -void lsp_build_list_ssn (struct isis_circuit *circuit, struct list *list, - dict_t * lspdb); +void lsp_build_list_ssn (struct isis_circuit *circuit, u_char num_lsps, + struct list *list, dict_t * lspdb); void lsp_search_and_destroy (u_char * id, dict_t * lspdb); -void lsp_purge_dr (u_char * id, struct isis_circuit *circuit, int level); -void lsp_purge_non_exist (struct isis_link_state_hdr *lsp_hdr, +void lsp_purge_pseudo (u_char * id, struct isis_circuit *circuit, int level); +void lsp_purge_non_exist (int level, + struct isis_link_state_hdr *lsp_hdr, struct isis_area *area); #define LSP_EQUAL 1 @@ -114,13 +104,18 @@ void lsp_purge_non_exist (struct isis_link_state_hdr *lsp_hdr, int lsp_id_cmp (u_char * id1, u_char * id2); int lsp_compare (char *areatag, struct isis_lsp *lsp, u_int32_t seq_num, u_int16_t checksum, u_int16_t rem_lifetime); -void lsp_update (struct isis_lsp *lsp, struct isis_link_state_hdr *lsp_hdr, - struct stream *stream, struct isis_area *area, int level); +void lsp_update (struct isis_lsp *lsp, struct stream *stream, + struct isis_area *area, int level); void lsp_inc_seqnum (struct isis_lsp *lsp, u_int32_t seq_num); +void lsp_print (struct isis_lsp *lsp, struct vty *vty, char dynhost); +void lsp_print_detail (struct isis_lsp *lsp, struct vty *vty, char dynhost); int lsp_print_all (struct vty *vty, dict_t * lspdb, char detail, char dynhost); const char *lsp_bits2string (u_char *); +/* sets SRMflags for all active circuits of an lsp */ +void lsp_set_all_srmflags (struct isis_lsp *lsp); + #ifdef TOPOLOGY_GENERATE void generate_topology_lsps (struct isis_area *area); void remove_topology_lsps (struct isis_area *area); diff --git a/isisd/isis_main.c b/isisd/isis_main.c index 15d85d6de..bbb1fb2a3 100644 --- a/isisd/isis_main.c +++ b/isisd/isis_main.c @@ -34,7 +34,9 @@ #include "privs.h" #include "sigevent.h" #include "filter.h" +#include "plist.h" #include "zclient.h" +#include "vrf.h" #include "isisd/dict.h" #include "include-netbsd/iso.h" @@ -44,6 +46,12 @@ #include "isisd/isis_circuit.h" #include "isisd/isisd.h" #include "isisd/isis_dynhn.h" +#include "isisd/isis_spf.h" +#include "isisd/isis_route.h" +#include "isisd/isis_routemap.h" +#include "isisd/isis_zebra.h" +#include "isisd/isis_tlv.h" +#include "isisd/isis_te.h" /* Default configuration file name */ #define ISISD_DEFAULT_CONFIG "isisd.conf" @@ -67,7 +75,7 @@ struct zebra_privs_t isisd_privs = { .vty_group = VTY_GROUP, #endif .caps_p = _caps_p, - .cap_num_p = 2, + .cap_num_p = sizeof (_caps_p) / sizeof (*_caps_p), .cap_num_i = 0 }; @@ -141,7 +149,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); @@ -154,7 +162,10 @@ reload () zlog_debug ("Reload"); /* FIXME: Clean up func call here */ vty_reset (); + (void) isisd_privs.change (ZPRIVS_RAISE); execve (_progpath, _argv, _envp); + zlog_err ("Reload failed: cannot exec %s: %s", _progpath, + safe_strerror (errno)); } static void @@ -313,26 +324,34 @@ main (int argc, char **argv, char **envp) master = thread_master_create (); /* random seed from time */ - srand (time (NULL)); + srandom (time (NULL)); /* * initializations */ zprivs_init (&isisd_privs); - signal_init (master, Q_SIGC (isisd_signals), isisd_signals); + signal_init (master, array_size (isisd_signals), isisd_signals); cmd_init (1); vty_init (master); memory_init (); access_list_init(); + vrf_init (); + prefix_list_init(); isis_init (); - dyn_cache_init (); - sort_node (); + isis_circuit_init (); + isis_spf_cmds_init (); + isis_redist_init (); + isis_route_map_init(); + isis_mpls_te_init(); + + /* create the global 'isis' instance */ + isis_new (1); + + isis_zebra_init (master); /* parse config file */ /* this is needed three times! because we have interfaces before the areas */ vty_read_config (config_file, config_default); - vty_read_config (config_file, config_default); - vty_read_config (config_file, config_default); /* Start execution only if not in dry-run mode */ if (dryrun) @@ -341,12 +360,13 @@ main (int argc, char **argv, char **envp) /* demonize */ if (daemon_mode && daemon (0, 0) < 0) { - zlog_err("ISISd daemon failed: %s", strerror(errno)); + zlog_err("IS-IS daemon failed: %s", strerror(errno)); exit (1); } /* Process ID file creation. */ - pid_output (pid_file); + if (pid_file[0] != '\0') + pid_output (pid_file); /* Make isis vty socket. */ vty_serv_sock (vty_addr, vty_port, ISIS_VTYSH_PATH); diff --git a/isisd/isis_misc.c b/isisd/isis_misc.c index 6b565bcbe..f19b44155 100644 --- a/isisd/isis_misc.c +++ b/isisd/isis_misc.c @@ -32,7 +32,9 @@ #include "isisd/dict.h" #include "isisd/isis_constants.h" #include "isisd/isis_common.h" +#include "isisd/isis_flags.h" #include "isisd/isis_circuit.h" +#include "isisd/isis_csm.h" #include "isisd/isisd.h" #include "isisd/isis_misc.h" @@ -40,6 +42,7 @@ #include "isisd/isis_lsp.h" #include "isisd/isis_constants.h" #include "isisd/isis_adjacency.h" +#include "isisd/isis_dynhn.h" /* staticly assigned vars for printing purposes */ struct in_addr new_prefix; @@ -60,7 +63,7 @@ char nlpidstring[30]; * This converts the isonet to its printable format */ const char * -isonet_print (u_char * from, int len) +isonet_print (const u_char * from, int len) { int i = 0; char *pos = isonet; @@ -99,10 +102,10 @@ isonet_print (u_char * from, int len) * extract dot from the dotted str, and insert all the number in a buff */ int -dotformat2buff (u_char * buff, const u_char * dotted) +dotformat2buff (u_char * buff, const char * dotted) { int dotlen, len = 0; - const u_char *pos = dotted; + const char *pos = dotted; u_char number[3]; int nextdotpos = 2; @@ -157,10 +160,10 @@ dotformat2buff (u_char * buff, const u_char * dotted) * conversion of XXXX.XXXX.XXXX to memory */ int -sysid2buff (u_char * buff, const u_char * dotted) +sysid2buff (u_char * buff, const char * dotted) { int len = 0; - const u_char *pos = dotted; + const char *pos = dotted; u_char number[3]; number[2] = '\0'; @@ -271,7 +274,7 @@ speaks (struct nlpids *nlpids, int family) * Returns 0 on error, IS-IS Circuit Type on ok */ int -string2circuit_t (const u_char * str) +string2circuit_t (const char * str) { if (!str) @@ -289,6 +292,42 @@ string2circuit_t (const u_char * str) return 0; } +const char * +circuit_state2string (int state) +{ + + switch (state) + { + case C_STATE_INIT: + return "Init"; + case C_STATE_CONF: + return "Config"; + case C_STATE_UP: + return "Up"; + default: + return "Unknown"; + } + return NULL; +} + +const char * +circuit_type2string (int type) +{ + + switch (type) + { + case CIRCUIT_T_P2P: + return "p2p"; + case CIRCUIT_T_BROADCAST: + return "lan"; + case CIRCUIT_T_LOOPBACK: + return "loopback"; + default: + return "Unknown"; + } + return NULL; +} + const char * circuit_t2string (int circuit_t) { @@ -331,7 +370,7 @@ syst2string (int type) * Print functions - we print to static vars */ const char * -snpa_print (u_char * from) +snpa_print (const u_char * from) { int i = 0; u_char *pos = (u_char *)snpa; @@ -363,7 +402,7 @@ snpa_print (u_char * from) } const char * -sysid_print (u_char * from) +sysid_print (const u_char * from) { int i = 0; char *pos = sysid; @@ -395,7 +434,7 @@ sysid_print (u_char * from) } const char * -rawlspid_print (u_char * from) +rawlspid_print (const u_char * from) { char *pos = lspid; if (!from) @@ -471,7 +510,7 @@ isis_jitter (unsigned long timer, unsigned long jitter) * most IS-IS timers are no longer than 16 bit */ - j = 1 + (int) ((RANDOM_SPREAD * rand ()) / (RAND_MAX + 1.0)); + j = 1 + (int) ((RANDOM_SPREAD * random ()) / (RAND_MAX + 1.0)); k = timer - (timer * (100 - jitter)) / 100; @@ -498,7 +537,6 @@ unix_hostname (void) { static struct utsname names; const char *hostname; - extern struct host host; hostname = host.name; if (!hostname) @@ -509,3 +547,87 @@ unix_hostname (void) return hostname; } + +/* + * Returns the dynamic hostname associated with the passed system ID. + * If no dynamic hostname found then returns formatted system ID. + */ +const char * +print_sys_hostname (const u_char *sysid) +{ + struct isis_dynhn *dyn; + + if (!sysid) + return "nullsysid"; + + /* For our system ID return our host name */ + if (memcmp(sysid, isis->sysid, ISIS_SYS_ID_LEN) == 0) + return unix_hostname(); + + dyn = dynhn_find_by_id (sysid); + if (dyn) + return (const char *)dyn->name.name; + + return sysid_print (sysid); +} + +/* + * This function is a generic utility that logs data of given length. + * Move this to a shared lib so that any protocol can use it. + */ +void +zlog_dump_data (void *data, int len) +{ + int i; + unsigned char *p; + unsigned char c; + char bytestr[4]; + char addrstr[10]; + char hexstr[ 16*3 + 5]; + char charstr[16*1 + 5]; + + p = data; + memset (bytestr, 0, sizeof(bytestr)); + memset (addrstr, 0, sizeof(addrstr)); + memset (hexstr, 0, sizeof(hexstr)); + memset (charstr, 0, sizeof(charstr)); + + for (i = 1; i <= len; i++) + { + c = *p; + if (isalnum (c) == 0) + c = '.'; + + /* store address for this line */ + if ((i % 16) == 1) + snprintf (addrstr, sizeof(addrstr), "%p", p); + + /* store hex str (for left side) */ + snprintf (bytestr, sizeof (bytestr), "%02X ", *p); + strncat (hexstr, bytestr, sizeof (hexstr) - strlen (hexstr) - 1); + + /* store char str (for right side) */ + snprintf (bytestr, sizeof (bytestr), "%c", c); + strncat (charstr, bytestr, sizeof (charstr) - strlen (charstr) - 1); + + if ((i % 16) == 0) + { + /* line completed */ + zlog_debug ("[%8.8s] %-50.50s %s", addrstr, hexstr, charstr); + hexstr[0] = 0; + charstr[0] = 0; + } + else if ((i % 8) == 0) + { + /* half line: add whitespaces */ + strncat (hexstr, " ", sizeof (hexstr) - strlen (hexstr) - 1); + strncat (charstr, " ", sizeof (charstr) - strlen (charstr) - 1); + } + p++; /* next byte */ + } + + /* print rest of buffer if not empty */ + if (strlen (hexstr) > 0) + zlog_debug ("[%8.8s] %-50.50s %s", addrstr, hexstr, charstr); + return; +} diff --git a/isisd/isis_misc.h b/isisd/isis_misc.h index d5003a8e6..37eaea154 100644 --- a/isisd/isis_misc.h +++ b/isisd/isis_misc.h @@ -24,8 +24,10 @@ #ifndef _ZEBRA_ISIS_MISC_H #define _ZEBRA_ISIS_MISC_H -int string2circuit_t (const u_char *); +int string2circuit_t (const char *); const char *circuit_t2string (int); +const char *circuit_state2string (int state); +const char *circuit_type2string (int type); const char *syst2string (int); struct in_addr newprefix2inaddr (u_char * prefix_start, u_char prefix_masklen); @@ -33,19 +35,21 @@ struct in_addr newprefix2inaddr (u_char * prefix_start, * Converting input to memory stored format * return value of 0 indicates wrong input */ -int dotformat2buff (u_char *, const u_char *); -int sysid2buff (u_char *, const u_char *); +int dotformat2buff (u_char *, const char *); +int sysid2buff (u_char *, const char *); /* * Printing functions */ -const char *isonet_print (u_char *, int len); -const char *sysid_print (u_char *); -const char *snpa_print (u_char *); -const char *rawlspid_print (u_char *); +const char *isonet_print (const u_char *, int len); +const char *sysid_print (const u_char *); +const char *snpa_print (const u_char *); +const char *rawlspid_print (const u_char *); const char *time2string (u_int32_t); /* typedef struct nlpids nlpids; */ char *nlpid2string (struct nlpids *); +const char *print_sys_hostname (const u_char *sysid); +void zlog_dump_data (void *data, int len); /* * misc functions @@ -57,7 +61,8 @@ const char *unix_hostname (void); /* * macros */ -#define GETSYSID(A,L) (A->area_addr + (A->addr_len - (L + 1))) +#define GETSYSID(A) (A->area_addr + (A->addr_len - \ + (ISIS_SYS_ID_LEN + ISIS_NSEL_LEN))) /* used for calculating nice string representation instead of plain seconds */ diff --git a/isisd/isis_pdu.c b/isisd/isis_pdu.c index d67df31ba..1dfb4623f 100644 --- a/isisd/isis_pdu.c +++ b/isisd/isis_pdu.c @@ -39,12 +39,12 @@ #include "isisd/include-netbsd/iso.h" #include "isisd/isis_constants.h" #include "isisd/isis_common.h" +#include "isisd/isis_flags.h" #include "isisd/isis_adjacency.h" #include "isisd/isis_circuit.h" #include "isisd/isis_network.h" #include "isisd/isis_misc.h" #include "isisd/isis_dr.h" -#include "isisd/isis_flags.h" #include "isisd/isis_tlv.h" #include "isisd/isisd.h" #include "isisd/isis_dynhn.h" @@ -53,9 +53,7 @@ #include "isisd/iso_checksum.h" #include "isisd/isis_csm.h" #include "isisd/isis_events.h" - -extern struct thread_master *master; -extern struct isis *isis; +#include "isisd/isis_te.h" #define ISIS_MINIMUM_FIXED_HDR_LEN 15 #define ISIS_MIN_PDU_LEN 13 /* partial seqnum pdu with id_len=2 */ @@ -169,44 +167,152 @@ accept_level (int level, int circuit_t) return retval; } - /* * Verify authentication information * Support cleartext and HMAC MD5 authentication */ -int -authentication_check (struct isis_passwd *remote, struct isis_passwd *local, struct isis_circuit* c) +static int +authentication_check (struct isis_passwd *remote, struct isis_passwd *local, + struct stream *stream, uint32_t auth_tlv_offset) { unsigned char digest[ISIS_AUTH_MD5_SIZE]; - if (c->passwd.type) - { - switch (c->passwd.type) - { - case ISIS_PASSWD_TYPE_HMAC_MD5: - /* HMAC MD5 (RFC 3567) */ - /* MD5 computation according to RFC 2104 */ - hmac_md5(c->rcv_stream->data, stream_get_endp(c->rcv_stream), (unsigned char *) &(local->passwd), c->passwd.len, (unsigned char *) &digest); - return memcmp (digest, remote->passwd, ISIS_AUTH_MD5_SIZE); - break; - case ISIS_PASSWD_TYPE_CLEARTXT: - /* Cleartext (ISO 10589) */ - if (local->len != remote->len) - return 1; /* Auth fail () - passwd len mismatch */ - return memcmp (local->passwd, remote->passwd, local->len); + /* Auth fail () - passwd type mismatch */ + if (local->type != remote->type) + return ISIS_ERROR; + + switch (local->type) + { + /* No authentication required */ + case ISIS_PASSWD_TYPE_UNUSED: break; + + /* Cleartext (ISO 10589) */ + case ISIS_PASSWD_TYPE_CLEARTXT: + /* Auth fail () - passwd len mismatch */ + if (remote->len != local->len) + return ISIS_ERROR; + return memcmp (local->passwd, remote->passwd, local->len); + + /* HMAC MD5 (RFC 3567) */ + case ISIS_PASSWD_TYPE_HMAC_MD5: + /* Auth fail () - passwd len mismatch */ + if (remote->len != ISIS_AUTH_MD5_SIZE) + return ISIS_ERROR; + /* Set the authentication value to 0 before the check */ + memset (STREAM_DATA (stream) + auth_tlv_offset + 3, 0, + ISIS_AUTH_MD5_SIZE); + /* Compute the digest */ + hmac_md5 (STREAM_DATA (stream), stream_get_endp (stream), + (unsigned char *) &(local->passwd), local->len, + (unsigned char *) &digest); + /* Copy back the authentication value after the check */ + memcpy (STREAM_DATA (stream) + auth_tlv_offset + 3, + remote->passwd, ISIS_AUTH_MD5_SIZE); + return memcmp (digest, remote->passwd, ISIS_AUTH_MD5_SIZE); + default: - zlog_warn ("Unsupported authentication type"); - break; + zlog_err ("Unsupported authentication type"); + return ISIS_ERROR; + } + + /* Authentication pass when no authentication is configured */ + return ISIS_OK; +} + +static int +lsp_authentication_check (struct stream *stream, struct isis_area *area, + int level, struct isis_passwd *passwd) +{ + struct isis_link_state_hdr *hdr; + uint32_t expected = 0, found = 0, auth_tlv_offset = 0; + uint16_t checksum, rem_lifetime, pdu_len; + struct tlvs tlvs; + int retval = ISIS_OK; + + hdr = (struct isis_link_state_hdr *) (STREAM_PNT (stream)); + pdu_len = ntohs (hdr->pdu_len); + expected |= TLVFLAG_AUTH_INFO; + auth_tlv_offset = stream_get_getp (stream) + ISIS_LSP_HDR_LEN; + retval = parse_tlvs (area->area_tag, STREAM_PNT (stream) + ISIS_LSP_HDR_LEN, + pdu_len - ISIS_FIXED_HDR_LEN - ISIS_LSP_HDR_LEN, + &expected, &found, &tlvs, &auth_tlv_offset); + + if (retval != ISIS_OK) + { + zlog_err ("ISIS-Upd (%s): Parse failed L%d LSP %s, seq 0x%08x, " + "cksum 0x%04x, lifetime %us, len %u", + area->area_tag, level, rawlspid_print (hdr->lsp_id), + ntohl (hdr->seq_num), ntohs (hdr->checksum), + ntohs (hdr->rem_lifetime), pdu_len); + if ((isis->debugs & DEBUG_UPDATE_PACKETS) && + (isis->debugs & DEBUG_PACKET_DUMP)) + zlog_dump_data (STREAM_DATA (stream), stream_get_endp (stream)); + return retval; + } + + if (!(found & TLVFLAG_AUTH_INFO)) + { + zlog_err ("No authentication tlv in LSP"); + return ISIS_ERROR; } + + if (tlvs.auth_info.type != ISIS_PASSWD_TYPE_CLEARTXT && + tlvs.auth_info.type != ISIS_PASSWD_TYPE_HMAC_MD5) + { + zlog_err ("Unknown authentication type in LSP"); + return ISIS_ERROR; } - return 0; /* Authentication pass when no authentication is configured */ + + /* + * RFC 5304 set checksum and remaining lifetime to zero before + * verification and reset to old values after verification. + */ + checksum = hdr->checksum; + rem_lifetime = hdr->rem_lifetime; + hdr->checksum = 0; + hdr->rem_lifetime = 0; + retval = authentication_check (&tlvs.auth_info, passwd, stream, + auth_tlv_offset); + hdr->checksum = checksum; + hdr->rem_lifetime = rem_lifetime; + + return retval; } /* * Processing helper functions */ static void +del_addr (void *val) +{ + XFREE (MTYPE_ISIS_TMP, val); +} + +static void +tlvs_to_adj_area_addrs (struct tlvs *tlvs, struct isis_adjacency *adj) +{ + struct listnode *node; + struct area_addr *area_addr, *malloced; + + if (adj->area_addrs) + { + adj->area_addrs->del = del_addr; + list_delete (adj->area_addrs); + } + adj->area_addrs = list_new (); + if (tlvs->area_addrs) + { + for (ALL_LIST_ELEMENTS_RO (tlvs->area_addrs, node, area_addr)) + { + malloced = XMALLOC (MTYPE_ISIS_TMP, sizeof (struct area_addr)); + memcpy (malloced, area_addr, sizeof (struct area_addr)); + listnode_add (adj->area_addrs, malloced); + } + } +} + +static int tlvs_to_adj_nlpids (struct tlvs *tlvs, struct isis_adjacency *adj) { int i; @@ -216,6 +322,8 @@ tlvs_to_adj_nlpids (struct tlvs *tlvs, struct isis_adjacency *adj) { tlv_nlpids = tlvs->nlpids; + if (tlv_nlpids->count > array_size (adj->nlpids.nlpids)) + return 1; adj->nlpids.count = tlv_nlpids->count; @@ -224,12 +332,7 @@ tlvs_to_adj_nlpids (struct tlvs *tlvs, struct isis_adjacency *adj) adj->nlpids.nlpids[i] = tlv_nlpids->nlpids[i]; } } -} - -static void -del_ip_addr (void *val) -{ - XFREE (MTYPE_ISIS_TMP, val); + return 0; } static void @@ -240,7 +343,7 @@ tlvs_to_adj_ipv4_addrs (struct tlvs *tlvs, struct isis_adjacency *adj) if (adj->ipv4_addrs) { - adj->ipv4_addrs->del = del_ip_addr; + adj->ipv4_addrs->del = del_addr; list_delete (adj->ipv4_addrs); } adj->ipv4_addrs = list_new (); @@ -264,7 +367,7 @@ tlvs_to_adj_ipv6_addrs (struct tlvs *tlvs, struct isis_adjacency *adj) if (adj->ipv6_addrs) { - adj->ipv6_addrs->del = del_ip_addr; + adj->ipv6_addrs->del = del_addr; list_delete (adj->ipv6_addrs); } adj->ipv6_addrs = list_new (); @@ -297,8 +400,26 @@ process_p2p_hello (struct isis_circuit *circuit) int retval = ISIS_OK; struct isis_p2p_hello_hdr *hdr; struct isis_adjacency *adj; - u_int32_t expected = 0, found; + 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) + { + zlog_debug ("ISIS-Adj (%s): Rcvd P2P IIH on %s, cirType %s, cirID %u", + circuit->area->area_tag, circuit->interface->name, + circuit_t2string (circuit->is_type), circuit->circuit_id); + if (isis->debugs & DEBUG_PACKET_DUMP) + zlog_dump_data (STREAM_DATA (circuit->rcv_stream), + stream_get_endp (circuit->rcv_stream)); + } + + if (circuit->circ_type != CIRCUIT_T_P2P) + { + zlog_warn ("p2p hello on non p2p circuit"); + return ISIS_WARNING; + } if ((stream_get_endp (circuit->rcv_stream) - stream_get_getp (circuit->rcv_stream)) < ISIS_P2PHELLO_HDRLEN) @@ -324,42 +445,26 @@ process_p2p_hello (struct isis_circuit *circuit) * Get the header */ hdr = (struct isis_p2p_hello_hdr *) STREAM_PNT (circuit->rcv_stream); - circuit->rcv_stream->getp += ISIS_P2PHELLO_HDRLEN; - - /* hdr.circuit_t = stream_getc (stream); - stream_get (hdr.source_id, stream, ISIS_SYS_ID_LEN); - hdr.hold_time = stream_getw (stream); - hdr.pdu_len = stream_getw (stream); - hdr.local_id = stream_getc (stream); */ - - /* - * My interpertation of the ISO, if no adj exists we will create one for - * the circuit - */ + pdu_len = ntohs (hdr->pdu_len); - if (isis->debugs & DEBUG_ADJ_PACKETS) + if (pdu_len < (ISIS_FIXED_HDR_LEN + ISIS_P2PHELLO_HDRLEN) || + pdu_len > ISO_MTU(circuit) || + pdu_len > stream_get_endp (circuit->rcv_stream)) { - zlog_debug ("ISIS-Adj (%s): Rcvd P2P IIH from (%s), cir type %s," - " cir id %02d, length %d", - circuit->area->area_tag, circuit->interface->name, - circuit_t2string (circuit->circuit_is_type), - circuit->circuit_id, ntohs (hdr->pdu_len)); + zlog_warn ("ISIS-Adj (%s): Rcvd P2P IIH from (%s) with " + "invalid pdu length %d", + circuit->area->area_tag, circuit->interface->name, pdu_len); + return ISIS_WARNING; } - adj = circuit->u.p2p.neighbor; - if (!adj) - { - adj = isis_new_adj (hdr->source_id, NULL, 0, circuit); - if (adj == NULL) - return ISIS_ERROR; - circuit->u.p2p.neighbor = adj; - isis_adj_state_change (adj, ISIS_ADJ_INITIALIZING, NULL); - adj->sys_type = ISIS_SYSTYPE_UNKNOWN; - } + /* + * Set the stream endp to PDU length, ignoring additional padding + * introduced by transport chips. + */ + if (pdu_len < stream_get_endp (circuit->rcv_stream)) + stream_set_endp (circuit->rcv_stream, pdu_len); - /* 8.2.6 Monitoring point-to-point adjacencies */ - adj->hold_time = ntohs (hdr->hold_time); - adj->last_upd = time (NULL); + stream_forward_getp (circuit->rcv_stream, ISIS_P2PHELLO_HDRLEN); /* * Lets get the TLVS now @@ -370,37 +475,174 @@ process_p2p_hello (struct isis_circuit *circuit) expected |= TLVFLAG_IPV4_ADDR; expected |= TLVFLAG_IPV6_ADDR; + auth_tlv_offset = stream_get_getp (circuit->rcv_stream); retval = parse_tlvs (circuit->area->area_tag, STREAM_PNT (circuit->rcv_stream), - ntohs (hdr->pdu_len) - ISIS_P2PHELLO_HDRLEN - - ISIS_FIXED_HDR_LEN, &expected, &found, &tlvs); + pdu_len - ISIS_P2PHELLO_HDRLEN - ISIS_FIXED_HDR_LEN, + &expected, &found, &tlvs, &auth_tlv_offset); if (retval > ISIS_WARNING) { + zlog_warn ("parse_tlvs() failed"); free_tlvs (&tlvs); return retval; }; + if (!(found & TLVFLAG_AREA_ADDRS)) + { + zlog_warn ("No Area addresses TLV in P2P IS to IS hello"); + free_tlvs (&tlvs); + 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) { if (!(found & TLVFLAG_AUTH_INFO) || - authentication_check (&tlvs.auth_info, &circuit->passwd, circuit)) + authentication_check (&tlvs.auth_info, &circuit->passwd, + circuit->rcv_stream, auth_tlv_offset)) + { + isis_event_auth_failure (circuit->area->area_tag, + "P2P hello authentication failure", + hdr->source_id); + free_tlvs (&tlvs); + return ISIS_OK; + } + } + + /* + * check if it's own interface ip match iih ip addrs + */ + 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) + { + free_tlvs (&tlvs); + return ISIS_WARNING; + } + + /* + * it's own p2p IIH PDU - discard + */ + if (!memcmp (hdr->source_id, isis->sysid, ISIS_SYS_ID_LEN)) + { + zlog_warn ("ISIS-Adj (%s): it's own IIH PDU - discarded", + circuit->area->area_tag); + free_tlvs (&tlvs); + return ISIS_WARNING; + } + + /* + * My interpertation of the ISO, if no adj exists we will create one for + * 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)) { - isis_event_auth_failure (circuit->area->area_tag, - "P2P hello authentication failure", - hdr->source_id); - return ISIS_OK; - } + 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) + { + adj = isis_new_adj (hdr->source_id, NULL, hdr->circuit_t, circuit); + if (adj == NULL) + return ISIS_ERROR; + } + else + { + adj->level = hdr->circuit_t; + } + circuit->u.p2p.neighbor = adj; + /* Build lsp with the new neighbor entry when a new + * adjacency is formed. Set adjacency circuit type to + * IIH PDU header circuit type before lsp is regenerated + * when an adjacency is up. This will result in the new + * adjacency entry getting added to the lsp tlv neighbor list. + */ + adj->circuit_t = hdr->circuit_t; + isis_adj_state_change (adj, ISIS_ADJ_INITIALIZING, NULL); + adj->sys_type = ISIS_SYSTYPE_UNKNOWN; } + /* 8.2.6 Monitoring point-to-point adjacencies */ + adj->hold_time = ntohs (hdr->hold_time); + adj->last_upd = time (NULL); + /* we do this now because the adj may not survive till the end... */ + tlvs_to_adj_area_addrs (&tlvs, adj); + + /* which protocol are spoken ??? */ + if (tlvs_to_adj_nlpids (&tlvs, adj)) + { + free_tlvs (&tlvs); + return ISIS_WARNING; + } /* we need to copy addresses to the adj */ - tlvs_to_adj_ipv4_addrs (&tlvs, adj); + if (found & TLVFLAG_IPV4_ADDR) + tlvs_to_adj_ipv4_addrs (&tlvs, adj); + + /* Update MPLS TE Remote IP address parameter if possible */ + if (IS_MPLS_TE(isisMplsTE) && circuit->mtc && IS_CIRCUIT_TE(circuit->mtc)) + if (adj->ipv4_addrs != NULL && listcount(adj->ipv4_addrs) != 0) + { + struct in_addr *ip_addr; + ip_addr = (struct in_addr *)listgetdata ((struct listnode *)listhead (adj->ipv4_addrs)); + set_circuitparams_rmt_ipaddr (circuit->mtc, *ip_addr); + } #ifdef HAVE_IPV6 - tlvs_to_adj_ipv6_addrs (&tlvs, adj); + if (found & TLVFLAG_IPV6_ADDR) + tlvs_to_adj_ipv6_addrs (&tlvs, adj); #endif /* HAVE_IPV6 */ /* lets take care of the expiry */ @@ -435,6 +677,7 @@ process_p2p_hello (struct isis_circuit *circuit) { /* (7) reject - wrong system type event */ zlog_warn ("wrongSystemType"); + free_tlvs (&tlvs); return ISIS_WARNING; /* Reject */ } else if (adj->adj_usage == ISIS_ADJ_LEVEL1) @@ -521,6 +764,7 @@ process_p2p_hello (struct isis_circuit *circuit) { /* (5) reject - wrong system type event */ zlog_warn ("wrongSystemType"); + free_tlvs (&tlvs); return ISIS_WARNING; /* Reject */ } else if ((adj->adj_usage == ISIS_ADJ_LEVEL1AND2) || @@ -553,7 +797,7 @@ process_p2p_hello (struct isis_circuit *circuit) } } /* 8.2.5.2 b) if no match was detected */ - else + else if (listcount (circuit->area->area_addrs) > 0) { if (circuit->area->is_type == IS_LEVEL_1) { @@ -579,6 +823,7 @@ process_p2p_hello (struct isis_circuit *circuit) { /* (6) reject - Area Mismatch event */ zlog_warn ("AreaMismatch"); + free_tlvs (&tlvs); return ISIS_WARNING; /* Reject */ } else if (adj->adj_usage == ISIS_ADJ_LEVEL1) @@ -631,6 +876,11 @@ process_p2p_hello (struct isis_circuit *circuit) } } } + else + { + /* down - area mismatch */ + isis_adj_state_change (adj, ISIS_ADJ_DOWN, "Area Mismatch"); + } /* 8.2.5.2 c) if the action was up - comparing circuit IDs */ /* FIXME - Missing parts */ @@ -653,8 +903,15 @@ process_p2p_hello (struct isis_circuit *circuit) break; } - adj->circuit_t = hdr->circuit_t; - adj->level = hdr->circuit_t; + + if (isis->debugs & DEBUG_ADJ_PACKETS) + { + zlog_debug ("ISIS-Adj (%s): Rcvd P2P IIH from (%s), cir type %s," + " cir id %02d, length %d", + circuit->area->area_tag, circuit->interface->name, + circuit_t2string (circuit->is_type), + circuit->circuit_id, pdu_len); + } free_tlvs (&tlvs); @@ -665,15 +922,33 @@ process_p2p_hello (struct isis_circuit *circuit) * Process IS-IS LAN Level 1/2 Hello PDU */ static int -process_lan_hello (int level, struct isis_circuit *circuit, u_char * ssnpa) +process_lan_hello (int level, struct isis_circuit *circuit, const u_char *ssnpa) { int retval = ISIS_OK; struct isis_lan_hello_hdr hdr; struct isis_adjacency *adj; - u_int32_t expected = 0, found; + u_int32_t expected = 0, found = 0, auth_tlv_offset = 0; struct tlvs tlvs; u_char *snpa; struct listnode *node; + int v4_usable = 0, v6_usable = 0; + + if (isis->debugs & DEBUG_ADJ_PACKETS) + { + zlog_debug ("ISIS-Adj (%s): Rcvd L%d LAN IIH on %s, cirType %s, " + "cirID %u", + circuit->area->area_tag, level, circuit->interface->name, + circuit_t2string (circuit->is_type), circuit->circuit_id); + if (isis->debugs & DEBUG_PACKET_DUMP) + zlog_dump_data (STREAM_DATA (circuit->rcv_stream), + stream_get_endp (circuit->rcv_stream)); + } + + if (circuit->circ_type != CIRCUIT_T_BROADCAST) + { + zlog_warn ("lan hello on non broadcast circuit"); + return ISIS_WARNING; + } if ((stream_get_endp (circuit->rcv_stream) - stream_get_getp (circuit->rcv_stream)) < ISIS_LANHELLO_HDRLEN) @@ -689,7 +964,7 @@ process_lan_hello (int level, struct isis_circuit *circuit, u_char * ssnpa) return ISIS_WARNING; } - if (!accept_level (level, circuit->circuit_is_type)) + if (!accept_level (level, circuit->is_type)) { if (isis->debugs & DEBUG_ADJ_PACKETS) { @@ -721,13 +996,34 @@ process_lan_hello (int level, struct isis_circuit *circuit, u_char * ssnpa) hdr.prio = stream_getc (circuit->rcv_stream); stream_get (hdr.lan_id, circuit->rcv_stream, ISIS_SYS_ID_LEN + 1); - if (hdr.circuit_t != IS_LEVEL_1 && hdr.circuit_t != IS_LEVEL_2 && - hdr.circuit_t != IS_LEVEL_1_AND_2) + if (hdr.pdu_len < (ISIS_FIXED_HDR_LEN + ISIS_LANHELLO_HDRLEN) || + hdr.pdu_len > ISO_MTU(circuit) || + hdr.pdu_len > stream_get_endp (circuit->rcv_stream)) + { + zlog_warn ("ISIS-Adj (%s): Rcvd LAN IIH from (%s) with " + "invalid pdu length %d", + circuit->area->area_tag, circuit->interface->name, + hdr.pdu_len); + return ISIS_WARNING; + } + + /* + * Set the stream endp to PDU length, ignoring additional padding + * introduced by transport chips. + */ + if (hdr.pdu_len < stream_get_endp (circuit->rcv_stream)) + stream_set_endp (circuit->rcv_stream, hdr.pdu_len); + + if (hdr.circuit_t != IS_LEVEL_1 && + hdr.circuit_t != IS_LEVEL_2 && + hdr.circuit_t != IS_LEVEL_1_AND_2 && + (level & hdr.circuit_t) == 0) { - zlog_warn ("Level %d LAN Hello with Circuit Type %d", level, - hdr.circuit_t); + zlog_err ("Level %d LAN Hello with Circuit Type %d", level, + hdr.circuit_t); return ISIS_ERROR; } + /* * Then get the tlvs */ @@ -738,10 +1034,12 @@ process_lan_hello (int level, struct isis_circuit *circuit, u_char * ssnpa) expected |= TLVFLAG_IPV4_ADDR; expected |= TLVFLAG_IPV6_ADDR; + auth_tlv_offset = stream_get_getp (circuit->rcv_stream); retval = parse_tlvs (circuit->area->area_tag, - STREAM_PNT (circuit->rcv_stream), - hdr.pdu_len - ISIS_LANHELLO_HDRLEN - - ISIS_FIXED_HDR_LEN, &expected, &found, &tlvs); + STREAM_PNT (circuit->rcv_stream), + hdr.pdu_len - ISIS_LANHELLO_HDRLEN - ISIS_FIXED_HDR_LEN, + &expected, &found, &tlvs, + &auth_tlv_offset); if (retval > ISIS_WARNING) { @@ -757,25 +1055,43 @@ 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) { if (!(found & TLVFLAG_AUTH_INFO) || - authentication_check (&tlvs.auth_info, &circuit->passwd, circuit)) - { - isis_event_auth_failure (circuit->area->area_tag, - "LAN hello authentication failure", - hdr.source_id); - retval = ISIS_WARNING; - goto out; - } + authentication_check (&tlvs.auth_info, &circuit->passwd, + circuit->rcv_stream, auth_tlv_offset)) + { + isis_event_auth_failure (circuit->area->area_tag, + "LAN hello authentication failure", + hdr.source_id); + retval = ISIS_WARNING; + goto out; + } + } + + 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 */ - if (level == 1 && !area_match (circuit->area->area_addrs, tlvs.area_addrs)) + if (listcount (circuit->area->area_addrs) == 0 || + (level == IS_LEVEL_1 && + area_match (circuit->area->area_addrs, tlvs.area_addrs) == 0)) { if (isis->debugs & DEBUG_ADJ_PACKETS) { @@ -802,43 +1118,83 @@ 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) - || !ip_match (circuit->ip_addrs, tlvs.ipv4_addrs)) + 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); - adj = isis_adj_lookup (hdr.source_id, circuit->u.bc.adjdb[level - 1]); - if (!adj) +#else + if (found & TLVFLAG_IPV6_ADDR) { - /* - * Do as in 8.4.2.5 - */ - adj = isis_new_adj (hdr.source_id, ssnpa, level, circuit); - if (adj == NULL) - { - retval = ISIS_ERROR; - goto out; - } + /* 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 - adj->level = level; + 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)) || + (adj->level != level)) + { + if (!adj) + { + /* + * Do as in 8.4.2.5 + */ + adj = isis_new_adj (hdr.source_id, ssnpa, level, circuit); + if (adj == NULL) + { + retval = ISIS_ERROR; + goto out; + } + } + else + { + if (ssnpa) { + memcpy (adj->snpa, ssnpa, 6); + } else { + memset (adj->snpa, ' ', 6); + } + adj->level = level; + } isis_adj_state_change (adj, ISIS_ADJ_INITIALIZING, NULL); - if (level == 1) - { - adj->sys_type = ISIS_SYSTYPE_L1_IS; - } + if (level == IS_LEVEL_1) + adj->sys_type = ISIS_SYSTYPE_L1_IS; else - { - adj->sys_type = ISIS_SYSTYPE_L2_IS; - } + adj->sys_type = ISIS_SYSTYPE_L2_IS; list_delete_all_node (circuit->u.bc.lan_neighs[level - 1]); isis_adj_build_neigh_list (circuit->u.bc.adjdb[level - 1], - circuit->u.bc.lan_neighs[level - 1]); + circuit->u.bc.lan_neighs[level - 1]); } if(adj->dis_record[level-1].dis==ISIS_IS_DIS) @@ -847,7 +1203,7 @@ process_lan_hello (int level, struct isis_circuit *circuit, u_char * ssnpa) case 1: if (memcmp (circuit->u.bc.l1_desig_is, hdr.lan_id, ISIS_SYS_ID_LEN + 1)) { - thread_add_event (master, isis_event_dis_status_change, circuit, 0); + thread_add_event (master, isis_event_dis_status_change, circuit, 0); memcpy (&circuit->u.bc.l1_desig_is, hdr.lan_id, ISIS_SYS_ID_LEN + 1); } @@ -855,7 +1211,7 @@ process_lan_hello (int level, struct isis_circuit *circuit, u_char * ssnpa) case 2: if (memcmp (circuit->u.bc.l2_desig_is, hdr.lan_id, ISIS_SYS_ID_LEN + 1)) { - thread_add_event (master, isis_event_dis_status_change, circuit, 0); + thread_add_event (master, isis_event_dis_status_change, circuit, 0); memcpy (&circuit->u.bc.l2_desig_is, hdr.lan_id, ISIS_SYS_ID_LEN + 1); } @@ -868,9 +1224,14 @@ process_lan_hello (int level, struct isis_circuit *circuit, u_char * ssnpa) memcpy (adj->lanid, hdr.lan_id, ISIS_SYS_ID_LEN + 1); + 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) @@ -886,7 +1247,7 @@ process_lan_hello (int level, struct isis_circuit *circuit, u_char * ssnpa) /* lets take care of the expiry */ THREAD_TIMER_OFF (adj->t_expire); THREAD_TIMER_ON (master, adj->t_expire, isis_adj_expire, adj, - (long) adj->hold_time); + (long) adj->hold_time); /* * If the snpa for this circuit is found from LAN Neighbours TLV @@ -894,31 +1255,48 @@ process_lan_hello (int level, struct isis_circuit *circuit, u_char * ssnpa) */ if (found & TLVFLAG_LAN_NEIGHS) + { + if (adj->adj_state != ISIS_ADJ_UP) { - if (adj->adj_state != ISIS_ADJ_UP) - { - for (ALL_LIST_ELEMENTS_RO (tlvs.lan_neighs, node, snpa)) - if (!memcmp (snpa, circuit->u.bc.snpa, ETH_ALEN)) - { - isis_adj_state_change (adj, ISIS_ADJ_UP, - "own SNPA found in LAN Neighbours TLV"); - } - } + for (ALL_LIST_ELEMENTS_RO (tlvs.lan_neighs, node, snpa)) + { + if (!memcmp (snpa, circuit->u.bc.snpa, ETH_ALEN)) + { + isis_adj_state_change (adj, ISIS_ADJ_UP, + "own SNPA found in LAN Neighbours TLV"); + } + } + } + else + { + int found = 0; + for (ALL_LIST_ELEMENTS_RO (tlvs.lan_neighs, node, snpa)) + if (!memcmp (snpa, circuit->u.bc.snpa, ETH_ALEN)) + { + found = 1; + break; + } + if (found == 0) + isis_adj_state_change (adj, ISIS_ADJ_INITIALIZING, + "own SNPA not found in LAN Neighbours TLV"); } + } + else if (adj->adj_state == ISIS_ADJ_UP) + { + isis_adj_state_change (adj, ISIS_ADJ_INITIALIZING, + "no LAN Neighbours TLV found"); + } out: - /* DEBUG_ADJ_PACKETS */ if (isis->debugs & DEBUG_ADJ_PACKETS) { - /* FIXME: is this place right? fix missing info */ zlog_debug ("ISIS-Adj (%s): Rcvd L%d LAN IIH from %s on %s, cirType %s, " - "cirID %u, length %ld", + "cirID %u, length %zd", circuit->area->area_tag, level, snpa_print (ssnpa), circuit->interface->name, - circuit_t2string (circuit->circuit_is_type), + circuit_t2string (circuit->is_type), circuit->circuit_id, - /* FIXME: use %z when we stop supporting old compilers. */ - (unsigned long) stream_get_endp (circuit->rcv_stream)); + stream_get_endp (circuit->rcv_stream)); } free_tlvs (&tlvs); @@ -932,7 +1310,7 @@ process_lan_hello (int level, struct isis_circuit *circuit, u_char * ssnpa) * Section 7.3.15.1 - Action on receipt of a link state PDU */ static int -process_lsp (int level, struct isis_circuit *circuit, u_char * ssnpa) +process_lsp (int level, struct isis_circuit *circuit, const u_char *ssnpa) { struct isis_link_state_hdr *hdr; struct isis_adjacency *adj = NULL; @@ -940,8 +1318,19 @@ process_lsp (int level, struct isis_circuit *circuit, u_char * ssnpa) int retval = ISIS_OK, comp = 0; u_char lspid[ISIS_SYS_ID_LEN + 2]; struct isis_passwd *passwd; + uint16_t pdu_len; + int lsp_confusion; + + if (isis->debugs & DEBUG_UPDATE_PACKETS) + { + zlog_debug ("ISIS-Upd (%s): Rcvd L%d LSP on %s, cirType %s, cirID %u", + circuit->area->area_tag, level, circuit->interface->name, + circuit_t2string (circuit->is_type), circuit->circuit_id); + if (isis->debugs & DEBUG_PACKET_DUMP) + zlog_dump_data (STREAM_DATA (circuit->rcv_stream), + stream_get_endp (circuit->rcv_stream)); + } - /* Sanity check - FIXME: move to correct place */ if ((stream_get_endp (circuit->rcv_stream) - stream_get_getp (circuit->rcv_stream)) < ISIS_LSP_HDR_LEN) { @@ -951,28 +1340,56 @@ process_lsp (int level, struct isis_circuit *circuit, u_char * ssnpa) /* Reference the header */ hdr = (struct isis_link_state_hdr *) STREAM_PNT (circuit->rcv_stream); + pdu_len = ntohs (hdr->pdu_len); + + /* lsp length check */ + if (pdu_len < (ISIS_FIXED_HDR_LEN + ISIS_LSP_HDR_LEN) || + pdu_len > ISO_MTU(circuit) || + pdu_len > stream_get_endp (circuit->rcv_stream)) + { + zlog_debug ("ISIS-Upd (%s): LSP %s invalid LSP length %d", + circuit->area->area_tag, + rawlspid_print (hdr->lsp_id), pdu_len); + + return ISIS_WARNING; + } + + /* + * Set the stream endp to PDU length, ignoring additional padding + * introduced by transport chips. + */ + if (pdu_len < stream_get_endp (circuit->rcv_stream)) + stream_set_endp (circuit->rcv_stream, pdu_len); if (isis->debugs & DEBUG_UPDATE_PACKETS) { zlog_debug ("ISIS-Upd (%s): Rcvd L%d LSP %s, seq 0x%08x, cksum 0x%04x, " - "lifetime %us, len %lu, on %s", + "lifetime %us, len %u, on %s", circuit->area->area_tag, level, rawlspid_print (hdr->lsp_id), ntohl (hdr->seq_num), ntohs (hdr->checksum), ntohs (hdr->rem_lifetime), - /* FIXME: use %z when we stop supporting old compilers. */ - (unsigned long) stream_get_endp (circuit->rcv_stream), + pdu_len, circuit->interface->name); } - assert (ntohs (hdr->pdu_len) > ISIS_LSP_HDR_LEN); + /* lsp is_type check */ + if ((hdr->lsp_bits & IS_LEVEL_1_AND_2) != IS_LEVEL_1 && + (hdr->lsp_bits & IS_LEVEL_1_AND_2) != IS_LEVEL_1_AND_2) + { + zlog_debug ("ISIS-Upd (%s): LSP %s invalid LSP is type %x", + circuit->area->area_tag, + rawlspid_print (hdr->lsp_id), hdr->lsp_bits); + /* continue as per RFC1122 Be liberal in what you accept, and + * conservative in what you send */ + } /* Checksum sanity check - FIXME: move to correct place */ /* 12 = sysid+pdu+remtime */ if (iso_csum_verify (STREAM_PNT (circuit->rcv_stream) + 4, - ntohs (hdr->pdu_len) - 12, &hdr->checksum)) + pdu_len - 12, &hdr->checksum)) { zlog_debug ("ISIS-Upd (%s): LSP %s invalid LSP checksum 0x%04x", circuit->area->area_tag, @@ -993,13 +1410,13 @@ process_lsp (int level, struct isis_circuit *circuit, u_char * ssnpa) } /* 7.3.15.1 a) 2,3 - manualL2OnlyMode not implemented */ - if (!accept_level (level, circuit->circuit_is_type)) + if (!accept_level (level, circuit->is_type)) { zlog_debug ("ISIS-Upd (%s): LSP %s received at level %d over circuit of" " type %s", circuit->area->area_tag, rawlspid_print (hdr->lsp_id), - level, circuit_t2string (circuit->circuit_is_type)); + level, circuit_t2string (circuit->is_type)); return ISIS_WARNING; } @@ -1009,12 +1426,12 @@ process_lsp (int level, struct isis_circuit *circuit, u_char * ssnpa) /* 7.3.15.1 a) 5 - maximum area match, can be ommited since we only use 3 */ /* 7.3.15.1 a) 7 - password check */ - (level == ISIS_LEVEL1) ? (passwd = &circuit->area->area_passwd) : - (passwd = &circuit->area->domain_passwd); + (level == IS_LEVEL_1) ? (passwd = &circuit->area->area_passwd) : + (passwd = &circuit->area->domain_passwd); if (passwd->type) { - if (isis_lsp_authinfo_check (circuit->rcv_stream, circuit->area, - ntohs (hdr->pdu_len), passwd)) + if (lsp_authentication_check (circuit->rcv_stream, circuit->area, + level, passwd)) { isis_event_auth_failure (circuit->area->area_tag, "LSP authentication failure", hdr->lsp_id); @@ -1035,7 +1452,6 @@ process_lsp (int level, struct isis_circuit *circuit, u_char * ssnpa) /* 7.3.15.1 a) 6 - Must check that we have an adjacency of the same level */ /* for broadcast circuits, snpa should be compared */ - /* FIXME : Point To Point */ if (circuit->circ_type == CIRCUIT_T_BROADCAST) { @@ -1052,7 +1468,6 @@ process_lsp (int level, struct isis_circuit *circuit, u_char * ssnpa) return ISIS_WARNING; /* Silently discard */ } } - /* for non broadcast, we just need to find same level adj */ else { @@ -1063,13 +1478,15 @@ process_lsp (int level, struct isis_circuit *circuit, u_char * ssnpa) } else { - if (((level == 1) && + if (((level == IS_LEVEL_1) && (circuit->u.p2p.neighbor->adj_usage == ISIS_ADJ_LEVEL2)) || - ((level == 2) && + ((level == IS_LEVEL_2) && (circuit->u.p2p.neighbor->adj_usage == ISIS_ADJ_LEVEL1))) return ISIS_WARNING; /* Silently discard */ + adj = circuit->u.p2p.neighbor; } } + dontcheckadj: /* 7.3.15.1 a) 7 - Passwords for level 1 - not implemented */ @@ -1077,6 +1494,21 @@ process_lsp (int level, struct isis_circuit *circuit, u_char * ssnpa) /* 7.3.15.1 a) 9 - OriginatingLSPBufferSize - not implemented FIXME: do it */ + /* 7.3.16.2 - If this is an LSP from another IS with identical seq_num but + * wrong checksum, initiate a purge. */ + if (lsp + && (lsp->lsp_header->seq_num == hdr->seq_num) + && (lsp->lsp_header->checksum != hdr->checksum)) + { + zlog_warn("ISIS-Upd (%s): LSP %s seq 0x%08x with confused checksum received.", + circuit->area->area_tag, rawlspid_print(hdr->lsp_id), + ntohl(hdr->seq_num)); + hdr->rem_lifetime = 0; + lsp_confusion = 1; + } + else + lsp_confusion = 0; + /* 7.3.15.1 b) - If the remaining life time is 0, we perform 7.3.16.4 */ if (hdr->rem_lifetime == 0) { @@ -1096,18 +1528,23 @@ process_lsp (int level, struct isis_circuit *circuit, u_char * ssnpa) /* 7.3.16.4 b) 1) */ if (comp == LSP_NEWER) { - lsp_update (lsp, hdr, circuit->rcv_stream, circuit->area, - level); + lsp_update (lsp, circuit->rcv_stream, circuit->area, level); /* ii */ - ISIS_FLAGS_SET_ALL (lsp->SRMflags); - /* iii */ - ISIS_CLEAR_FLAG (lsp->SRMflags, circuit); + lsp_set_all_srmflags (lsp); /* v */ ISIS_FLAGS_CLEAR_ALL (lsp->SSNflags); /* FIXME: OTHER than c */ - /* iv */ - if (circuit->circ_type != CIRCUIT_T_BROADCAST) - ISIS_SET_FLAG (lsp->SSNflags, circuit); + /* For the case of lsp confusion, flood the purge back to its + * originator so that it can react. Otherwise, don't reflood + * through incoming circuit as usual */ + if (!lsp_confusion) + { + /* iii */ + ISIS_CLEAR_FLAG (lsp->SRMflags, circuit); + /* iv */ + if (circuit->circ_type != CIRCUIT_T_BROADCAST) + ISIS_SET_FLAG (lsp->SSNflags, circuit); + } } /* 7.3.16.4 b) 2) */ else if (comp == LSP_EQUAL) { @@ -1123,38 +1560,25 @@ process_lsp (int level, struct isis_circuit *circuit, u_char * ssnpa) ISIS_CLEAR_FLAG (lsp->SSNflags, circuit); } } - else - { - /* our own LSP -> 7.3.16.4 c) */ - if (LSP_PSEUDO_ID (lsp->lsp_header->lsp_id) != - circuit->circuit_id - || (LSP_PSEUDO_ID (lsp->lsp_header->lsp_id) == - circuit->circuit_id - && circuit->u.bc.is_dr[level - 1] == 1)) - { - lsp->lsp_header->seq_num = htonl (ntohl (hdr->seq_num) + 1); - if (isis->debugs & DEBUG_UPDATE_PACKETS) - zlog_debug ("LSP LEN: %d", - ntohs (lsp->lsp_header->pdu_len)); - fletcher_checksum (STREAM_DATA (lsp->pdu) + 12, - ntohs (lsp->lsp_header->pdu_len) - 12, 12); - ISIS_FLAGS_SET_ALL (lsp->SRMflags); - if (isis->debugs & DEBUG_UPDATE_PACKETS) - zlog_debug ("ISIS-Upd (%s): (1) re-originating LSP %s new " - "seq 0x%08x", circuit->area->area_tag, - rawlspid_print (hdr->lsp_id), - ntohl (lsp->lsp_header->seq_num)); - lsp->lsp_header->rem_lifetime = - htons (isis_jitter - (circuit->area->max_lsp_lifetime[level - 1], - MAX_AGE_JITTER)); - } - else - { - /* Got purge for own pseudo-lsp, and we are not DR */ - lsp_purge_dr (lsp->lsp_header->lsp_id, circuit, level); - } - } + else if (lsp->lsp_header->rem_lifetime != 0) + { + /* our own LSP -> 7.3.16.4 c) */ + if (comp == LSP_NEWER) + { + lsp_inc_seqnum (lsp, ntohl (hdr->seq_num)); + lsp_set_all_srmflags (lsp); + } + else + { + ISIS_SET_FLAG (lsp->SRMflags, circuit); + ISIS_CLEAR_FLAG (lsp->SSNflags, circuit); + } + if (isis->debugs & DEBUG_UPDATE_PACKETS) + zlog_debug ("ISIS-Upd (%s): (1) re-originating LSP %s new " + "seq 0x%08x", circuit->area->area_tag, + rawlspid_print (hdr->lsp_id), + ntohl (lsp->lsp_header->seq_num)); + } } return retval; } @@ -1165,7 +1589,7 @@ process_lsp (int level, struct isis_circuit *circuit, u_char * ssnpa) if (!lsp) { /* 7.3.16.4: initiate a purge */ - lsp_purge_non_exist (hdr, circuit->area); + lsp_purge_non_exist(level, hdr, circuit->area); return ISIS_OK; } /* 7.3.15.1 d) - If this is our own lsp and we have it */ @@ -1174,25 +1598,19 @@ process_lsp (int level, struct isis_circuit *circuit, u_char * ssnpa) * has information that the current sequence number for source S is * "greater" than that held by S, ... */ - else if (ntohl (hdr->seq_num) > ntohl (lsp->lsp_header->seq_num)) + if (ntohl (hdr->seq_num) > ntohl (lsp->lsp_header->seq_num)) { /* 7.3.16.1 */ - lsp->lsp_header->seq_num = htonl (ntohl (hdr->seq_num) + 1); - - fletcher_checksum (STREAM_DATA (lsp->pdu) + 12, - ntohs (lsp->lsp_header->pdu_len) - 12, 12); - - ISIS_FLAGS_SET_ALL (lsp->SRMflags); + lsp_inc_seqnum (lsp, ntohl (hdr->seq_num)); if (isis->debugs & DEBUG_UPDATE_PACKETS) zlog_debug ("ISIS-Upd (%s): (2) re-originating LSP %s new seq " "0x%08x", circuit->area->area_tag, rawlspid_print (hdr->lsp_id), ntohl (lsp->lsp_header->seq_num)); - lsp->lsp_header->rem_lifetime = - htons (isis_jitter - (circuit->area->max_lsp_lifetime[level - 1], - MAX_AGE_JITTER)); } + /* If the received LSP is older or equal, + * resend the LSP which will act as ACK */ + lsp_set_all_srmflags (lsp); } else { @@ -1201,22 +1619,6 @@ process_lsp (int level, struct isis_circuit *circuit, u_char * ssnpa) /* 7.3.15.1 e) 1) LSP newer than the one in db or no LSP in db */ if ((!lsp || comp == LSP_NEWER)) { - int regenerate = (lsp == NULL); - /* i */ - if (lsp) - { -#ifdef EXTREME_DEBUG - zlog_debug ("level %d number is - %ld", level, - circuit->area->lspdb[level - 1]->dict_nodecount); -#endif /* EXTREME DEBUG */ - lsp_search_and_destroy (hdr->lsp_id, - circuit->area->lspdb[level - 1]); - /* exists, so we overwrite */ -#ifdef EXTREME_DEBUG - zlog_debug ("level %d number is - %ld", level, - circuit->area->lspdb[level - 1]->dict_nodecount); -#endif /* EXTREME DEBUG */ - } /* * If this lsp is a frag, need to see if we have zero lsp present */ @@ -1227,18 +1629,24 @@ process_lsp (int level, struct isis_circuit *circuit, u_char * ssnpa) lsp0 = lsp_search (lspid, circuit->area->lspdb[level - 1]); if (!lsp0) { - zlog_debug ("Got lsp frag, while zero lsp not database"); + zlog_debug ("Got lsp frag, while zero lsp not in database"); return ISIS_OK; } } - lsp = - lsp_new_from_stream_ptr (circuit->rcv_stream, - ntohs (hdr->pdu_len), lsp0, - circuit->area); - lsp->level = level; - lsp_insert (lsp, circuit->area->lspdb[level - 1]); + /* i */ + if (!lsp) + { + lsp = lsp_new_from_stream_ptr (circuit->rcv_stream, + pdu_len, lsp0, + circuit->area, level); + lsp_insert (lsp, circuit->area->lspdb[level - 1]); + } + else /* exists, so we overwrite */ + { + lsp_update (lsp, circuit->rcv_stream, circuit->area, level); + } /* ii */ - ISIS_FLAGS_SET_ALL (lsp->SRMflags); + lsp_set_all_srmflags (lsp); /* iii */ ISIS_CLEAR_FLAG (lsp->SRMflags, circuit); @@ -1246,19 +1654,14 @@ process_lsp (int level, struct isis_circuit *circuit, u_char * ssnpa) if (circuit->circ_type != CIRCUIT_T_BROADCAST) ISIS_SET_FLAG (lsp->SSNflags, circuit); /* FIXME: v) */ - if (regenerate && circuit->u.bc.is_dr[level - 1]) { - lsp_l1_pseudo_generate (circuit); - } } /* 7.3.15.1 e) 2) LSP equal to the one in db */ else if (comp == LSP_EQUAL) { ISIS_CLEAR_FLAG (lsp->SRMflags, circuit); - lsp_update (lsp, hdr, circuit->rcv_stream, circuit->area, level); + lsp_update (lsp, circuit->rcv_stream, circuit->area, level); if (circuit->circ_type != CIRCUIT_T_BROADCAST) - { - ISIS_SET_FLAG (lsp->SSNflags, circuit); - } + ISIS_SET_FLAG (lsp->SSNflags, circuit); } /* 7.3.15.1 e) 3) LSP older than the one in db */ else @@ -1267,7 +1670,6 @@ process_lsp (int level, struct isis_circuit *circuit, u_char * ssnpa) ISIS_CLEAR_FLAG (lsp->SSNflags, circuit); } } - return retval; } @@ -1279,16 +1681,16 @@ process_lsp (int level, struct isis_circuit *circuit, u_char * ssnpa) static int process_snp (int snp_type, int level, struct isis_circuit *circuit, - u_char * ssnpa) + const u_char *ssnpa) { int retval = ISIS_OK; int cmp, own_lsp; char typechar = ' '; - int len; + uint16_t pdu_len; struct isis_adjacency *adj; struct isis_complete_seqnum_hdr *chdr = NULL; struct isis_partial_seqnum_hdr *phdr = NULL; - uint32_t found = 0, expected = 0; + uint32_t found = 0, expected = 0, auth_tlv_offset = 0; struct isis_lsp *lsp; struct lsp_entry *entry; struct listnode *node, *nnode; @@ -1303,12 +1705,14 @@ process_snp (int snp_type, int level, struct isis_circuit *circuit, typechar = 'C'; chdr = (struct isis_complete_seqnum_hdr *) STREAM_PNT (circuit->rcv_stream); - circuit->rcv_stream->getp += ISIS_CSNP_HDRLEN; - len = ntohs (chdr->pdu_len); - if (len < ISIS_CSNP_HDRLEN) + stream_forward_getp (circuit->rcv_stream, ISIS_CSNP_HDRLEN); + pdu_len = ntohs (chdr->pdu_len); + if (pdu_len < (ISIS_FIXED_HDR_LEN + ISIS_CSNP_HDRLEN) || + pdu_len > ISO_MTU(circuit) || + pdu_len > stream_get_endp (circuit->rcv_stream)) { - zlog_warn ("Received a CSNP with bogus length!"); - return ISIS_OK; + zlog_warn ("Received a CSNP with bogus length %d", pdu_len); + return ISIS_WARNING; } } else @@ -1316,15 +1720,24 @@ process_snp (int snp_type, int level, struct isis_circuit *circuit, typechar = 'P'; phdr = (struct isis_partial_seqnum_hdr *) STREAM_PNT (circuit->rcv_stream); - circuit->rcv_stream->getp += ISIS_PSNP_HDRLEN; - len = ntohs (phdr->pdu_len); - if (len < ISIS_PSNP_HDRLEN) + stream_forward_getp (circuit->rcv_stream, ISIS_PSNP_HDRLEN); + pdu_len = ntohs (phdr->pdu_len); + if (pdu_len < (ISIS_FIXED_HDR_LEN + ISIS_PSNP_HDRLEN) || + pdu_len > ISO_MTU(circuit) || + pdu_len > stream_get_endp (circuit->rcv_stream)) { - zlog_warn ("Received a CSNP with bogus length!"); - return ISIS_OK; + zlog_warn ("Received a PSNP with bogus length %d", pdu_len); + return ISIS_WARNING; } } + /* + * Set the stream endp to PDU length, ignoring additional padding + * introduced by transport chips. + */ + if (pdu_len < stream_get_endp (circuit->rcv_stream)) + stream_set_endp (circuit->rcv_stream, pdu_len); + /* 7.3.15.2 a) 1 - external domain circuit will discard snp pdu */ if (circuit->ext_domain) { @@ -1338,7 +1751,7 @@ process_snp (int snp_type, int level, struct isis_circuit *circuit, } /* 7.3.15.2 a) 2,3 - manualL2OnlyMode not implemented */ - if (!accept_level (level, circuit->circuit_is_type)) + if (!accept_level (level, circuit->is_type)) { zlog_debug ("ISIS-Snp (%s): Rcvd L%d %cSNP on %s, " @@ -1347,26 +1760,23 @@ process_snp (int snp_type, int level, struct isis_circuit *circuit, level, typechar, circuit->interface->name, - circuit_t2string (circuit->circuit_is_type), level); + circuit_t2string (circuit->is_type), level); return ISIS_OK; } /* 7.3.15.2 a) 4 - not applicable for CSNP only PSNPs on broadcast */ if ((snp_type == ISIS_SNP_PSNP_FLAG) && - (circuit->circ_type == CIRCUIT_T_BROADCAST)) + (circuit->circ_type == CIRCUIT_T_BROADCAST) && + (!circuit->u.bc.is_dr[level - 1])) { - if (!circuit->u.bc.is_dr[level - 1]) - { + zlog_debug ("ISIS-Snp (%s): Rcvd L%d %cSNP from %s on %s, " + "skipping: we are not the DIS", + circuit->area->area_tag, + level, + typechar, snpa_print (ssnpa), circuit->interface->name); - zlog_debug ("ISIS-Snp (%s): Rcvd L%d %cSNP from %s on %s, " - "skipping: we are not the DIS", - circuit->area->area_tag, - level, - typechar, snpa_print (ssnpa), circuit->interface->name); - - return ISIS_OK; - } + return ISIS_OK; } /* 7.3.15.2 a) 5 - need to make sure IDLength matches - already checked */ @@ -1396,7 +1806,10 @@ process_snp (int snp_type, int level, struct isis_circuit *circuit, else { if (!circuit->u.p2p.neighbor) - return ISIS_OK; /* Silently discard */ + { + zlog_warn ("no p2p neighbor on circuit %s", circuit->interface->name); + return ISIS_OK; /* Silently discard */ + } } /* 7.3.15.2 a) 8 - Passwords for level 1 - not implemented */ @@ -1408,10 +1821,12 @@ process_snp (int snp_type, int level, struct isis_circuit *circuit, /* parse the SNP */ expected |= TLVFLAG_LSP_ENTRIES; expected |= TLVFLAG_AUTH_INFO; + + auth_tlv_offset = stream_get_getp (circuit->rcv_stream); retval = parse_tlvs (circuit->area->area_tag, STREAM_PNT (circuit->rcv_stream), - len - circuit->rcv_stream->getp, - &expected, &found, &tlvs); + pdu_len - stream_get_getp (circuit->rcv_stream), + &expected, &found, &tlvs, &auth_tlv_offset); if (retval > ISIS_WARNING) { @@ -1420,7 +1835,7 @@ process_snp (int snp_type, int level, struct isis_circuit *circuit, return retval; } - if (level == 1) + if (level == IS_LEVEL_1) passwd = &circuit->area->area_passwd; else passwd = &circuit->area->domain_passwd; @@ -1428,16 +1843,19 @@ process_snp (int snp_type, int level, struct isis_circuit *circuit, if (CHECK_FLAG(passwd->snp_auth, SNP_AUTH_RECV)) { if (passwd->type) - { - if (!(found & TLVFLAG_AUTH_INFO) || - authentication_check (&tlvs.auth_info, passwd, circuit)) - { - isis_event_auth_failure (circuit->area->area_tag, - "SNP authentication" " failure", - phdr ? phdr->source_id : chdr->source_id); - return ISIS_OK; - } - } + { + if (!(found & TLVFLAG_AUTH_INFO) || + authentication_check (&tlvs.auth_info, passwd, + circuit->rcv_stream, auth_tlv_offset)) + { + isis_event_auth_failure (circuit->area->area_tag, + "SNP authentication" " failure", + phdr ? phdr->source_id : + chdr->source_id); + free_tlvs (&tlvs); + return ISIS_OK; + } + } } /* debug isis snp-packets */ @@ -1477,19 +1895,18 @@ process_snp (int snp_type, int level, struct isis_circuit *circuit, /* 7.3.15.2 b) 2) if it equals, clear SRM on p2p */ if (cmp == LSP_EQUAL) { - if (circuit->circ_type != CIRCUIT_T_BROADCAST) - ISIS_CLEAR_FLAG (lsp->SRMflags, circuit); - /* 7.3.15.2 b) 3) if it is older, clear SSN and set SRM */ + /* if (circuit->circ_type != CIRCUIT_T_BROADCAST) */ + ISIS_CLEAR_FLAG (lsp->SRMflags, circuit); } + /* 7.3.15.2 b) 3) if it is older, clear SSN and set SRM */ else if (cmp == LSP_OLDER) { ISIS_CLEAR_FLAG (lsp->SSNflags, circuit); ISIS_SET_FLAG (lsp->SRMflags, circuit); } + /* 7.3.15.2 b) 4) if it is newer, set SSN and clear SRM on p2p */ else { - /* 7.3.15.2 b) 4) if it is newer, set SSN and clear SRM - * on p2p */ if (own_lsp) { lsp_inc_seqnum (lsp, ntohl (entry->seq_num)); @@ -1498,8 +1915,8 @@ process_snp (int snp_type, int level, struct isis_circuit *circuit, else { ISIS_SET_FLAG (lsp->SSNflags, circuit); - if (circuit->circ_type != CIRCUIT_T_BROADCAST) - ISIS_CLEAR_FLAG (lsp->SRMflags, circuit); + /* if (circuit->circ_type != CIRCUIT_T_BROADCAST) */ + ISIS_CLEAR_FLAG (lsp->SRMflags, circuit); } } } @@ -1510,9 +1927,11 @@ process_snp (int snp_type, int level, struct isis_circuit *circuit, if (entry->rem_lifetime && entry->checksum && entry->seq_num && memcmp (entry->lsp_id, isis->sysid, ISIS_SYS_ID_LEN)) { - lsp = lsp_new (entry->lsp_id, ntohs (entry->rem_lifetime), - 0, 0, entry->checksum, level); + lsp = lsp_new(circuit->area, entry->lsp_id, + ntohs(entry->rem_lifetime), + 0, 0, entry->checksum, level); lsp_insert (lsp, circuit->area->lspdb[level - 1]); + ISIS_FLAGS_CLEAR_ALL (lsp->SRMflags); ISIS_SET_FLAG (lsp->SSNflags, circuit); } } @@ -1523,7 +1942,8 @@ process_snp (int snp_type, int level, struct isis_circuit *circuit, if (snp_type == ISIS_SNP_CSNP_FLAG) { /* - * Build a list from our own LSP db bounded with start_ and stop_lsp_id + * Build a list from our own LSP db bounded with + * start_lsp_id and stop_lsp_id */ lsp_list = list_new (); lsp_build_list_nonzero_ht (chdr->start_lsp_id, chdr->stop_lsp_id, @@ -1546,11 +1966,10 @@ process_snp (int snp_type, int level, struct isis_circuit *circuit, } /* on remaining LSPs we set SRM (neighbor knew not of) */ for (ALL_LIST_ELEMENTS_RO (lsp_list, node, lsp)) - { ISIS_SET_FLAG (lsp->SRMflags, circuit); - } /* lets free it */ - list_free (lsp_list); + list_delete (lsp_list); + } free_tlvs (&tlvs); @@ -1558,8 +1977,18 @@ process_snp (int snp_type, int level, struct isis_circuit *circuit, } static int -process_csnp (int level, struct isis_circuit *circuit, u_char * ssnpa) +process_csnp (int level, struct isis_circuit *circuit, const u_char *ssnpa) { + if (isis->debugs & DEBUG_SNP_PACKETS) + { + zlog_debug ("ISIS-Snp (%s): Rcvd L%d CSNP on %s, cirType %s, cirID %u", + circuit->area->area_tag, level, circuit->interface->name, + circuit_t2string (circuit->is_type), circuit->circuit_id); + if (isis->debugs & DEBUG_PACKET_DUMP) + zlog_dump_data (STREAM_DATA (circuit->rcv_stream), + stream_get_endp (circuit->rcv_stream)); + } + /* Sanity check - FIXME: move to correct place */ if ((stream_get_endp (circuit->rcv_stream) - stream_get_getp (circuit->rcv_stream)) < ISIS_CSNP_HDRLEN) @@ -1572,91 +2001,28 @@ process_csnp (int level, struct isis_circuit *circuit, u_char * ssnpa) } static int -process_psnp (int level, struct isis_circuit *circuit, u_char * ssnpa) +process_psnp (int level, struct isis_circuit *circuit, const u_char *ssnpa) { + if (isis->debugs & DEBUG_SNP_PACKETS) + { + zlog_debug ("ISIS-Snp (%s): Rcvd L%d PSNP on %s, cirType %s, cirID %u", + circuit->area->area_tag, level, circuit->interface->name, + circuit_t2string (circuit->is_type), circuit->circuit_id); + if (isis->debugs & DEBUG_PACKET_DUMP) + zlog_dump_data (STREAM_DATA (circuit->rcv_stream), + stream_get_endp (circuit->rcv_stream)); + } + if ((stream_get_endp (circuit->rcv_stream) - stream_get_getp (circuit->rcv_stream)) < ISIS_PSNP_HDRLEN) { - zlog_warn ("Packet too short"); + zlog_warn ("Packet too short ( < %d)", ISIS_PSNP_HDRLEN); return ISIS_WARNING; } return process_snp (ISIS_SNP_PSNP_FLAG, level, circuit, ssnpa); } -/* - * Process ISH - * ISO - 10589 - * Section 8.2.2 - Receiving ISH PDUs by an intermediate system - * FIXME: sample packet dump, need to figure 0x81 - looks like NLPid - * 0x82 0x15 0x01 0x00 0x04 0x01 0x2c 0x59 - * 0x38 0x08 0x47 0x00 0x01 0x00 0x02 0x00 - * 0x03 0x00 0x81 0x01 0xcc - */ -static int -process_is_hello (struct isis_circuit *circuit) -{ - struct isis_adjacency *adj; - int retval = ISIS_OK; - u_char neigh_len; - u_char *sysid; - - /* In this point in time we are not yet able to handle is_hellos - * on lan - Sorry juniper... - */ - if (circuit->circ_type == CIRCUIT_T_BROADCAST) - return retval; - - neigh_len = stream_getc (circuit->rcv_stream); - sysid = STREAM_PNT (circuit->rcv_stream) + neigh_len - 1 - ISIS_SYS_ID_LEN; - adj = circuit->u.p2p.neighbor; - if (!adj) - { - /* 8.2.2 */ - adj = isis_new_adj (sysid, NULL, 0, circuit); - if (adj == NULL) - return ISIS_ERROR; - - isis_adj_state_change (adj, ISIS_ADJ_INITIALIZING, NULL); - adj->sys_type = ISIS_SYSTYPE_UNKNOWN; - circuit->u.p2p.neighbor = adj; - } - /* 8.2.2 a) */ - if ((adj->adj_state == ISIS_ADJ_UP) && memcmp (adj->sysid, sysid, - ISIS_SYS_ID_LEN)) - { - /* 8.2.2 a) 1) FIXME: adjStateChange(down) event */ - /* 8.2.2 a) 2) delete the adj */ - XFREE (MTYPE_ISIS_ADJACENCY, adj); - /* 8.2.2 a) 3) create a new adj */ - adj = isis_new_adj (sysid, NULL, 0, circuit); - if (adj == NULL) - return ISIS_ERROR; - - /* 8.2.2 a) 3) i */ - isis_adj_state_change (adj, ISIS_ADJ_INITIALIZING, NULL); - /* 8.2.2 a) 3) ii */ - adj->sys_type = ISIS_SYSTYPE_UNKNOWN; - /* 8.2.2 a) 4) quite meaningless */ - } - /* 8.2.2 b) ignore on condition */ - if ((adj->adj_state == ISIS_ADJ_INITIALIZING) && - (adj->sys_type == ISIS_SYSTYPE_IS)) - { - /* do nothing */ - } - else - { - /* 8.2.2 c) respond with a p2p IIH */ - send_hello (circuit, 1); - } - /* 8.2.2 d) type is IS */ - adj->sys_type = ISIS_SYSTYPE_IS; - /* 8.2.2 e) FIXME: Circuit type of? */ - - return retval; -} - /* * PDU Dispatcher */ @@ -1665,7 +2031,6 @@ static int isis_handle_pdu (struct isis_circuit *circuit, u_char * ssnpa) { struct isis_fixed_hdr *hdr; - struct esis_fixed_hdr *esis_hdr; int retval = ISIS_OK; @@ -1676,7 +2041,7 @@ isis_handle_pdu (struct isis_circuit *circuit, u_char * ssnpa) if ((hdr->idrp != ISO10589_ISIS) && (hdr->idrp != ISO9542_ESIS)) { - zlog_warn ("Not an IS-IS or ES-IS packet IDRP=%02x", hdr->idrp); + zlog_err ("Not an IS-IS or ES-IS packet IDRP=%02x", hdr->idrp); return ISIS_ERROR; } @@ -1685,28 +2050,11 @@ isis_handle_pdu (struct isis_circuit *circuit, u_char * ssnpa) */ if (hdr->idrp == ISO9542_ESIS) { - esis_hdr = (struct esis_fixed_hdr *) STREAM_DATA (circuit->rcv_stream); - stream_set_getp (circuit->rcv_stream, ESIS_FIXED_HDR_LEN); - /* FIXME: Need to do some acceptence tests */ - /* example length... */ - switch (esis_hdr->pdu_type) - { - case ESH_PDU: - /* FIXME */ - break; - case ISH_PDU: - zlog_debug ("AN ISH PDU!!"); - retval = process_is_hello (circuit); - break; - default: - return ISIS_ERROR; - } - return retval; - } - else - { - stream_set_getp (circuit->rcv_stream, ISIS_FIXED_HDR_LEN); + zlog_err ("No support for ES-IS packet IDRP=%02x", hdr->idrp); + return ISIS_ERROR; } + stream_set_getp (circuit->rcv_stream, ISIS_FIXED_HDR_LEN); + /* * and then process it */ @@ -1737,6 +2085,14 @@ isis_handle_pdu (struct isis_circuit *circuit, u_char * ssnpa) zlog_warn ("Unsupported ISIS version %u", hdr->version2); return ISIS_WARNING; } + + if (circuit->is_passive) + { + zlog_warn ("Received ISIS PDU on passive circuit %s", + circuit->interface->name); + return ISIS_WARNING; + } + /* either 3 or 0 */ if ((hdr->max_area_addrs != 0) && (hdr->max_area_addrs != isis->max_area_addrs)) @@ -1797,13 +2153,7 @@ isis_receive (struct thread *thread) circuit = THREAD_ARG (thread); assert (circuit); - if (!circuit->area) - return ISIS_OK; - - if (circuit->rcv_stream == NULL) - circuit->rcv_stream = stream_new (ISO_MTU (circuit)); - else - stream_reset (circuit->rcv_stream); + isis_circuit_stream(circuit, &circuit->rcv_stream); retval = circuit->rx (circuit, ssnpa); circuit->t_read = NULL; @@ -1814,8 +2164,11 @@ isis_receive (struct thread *thread) /* * prepare for next packet. */ - THREAD_READ_ON (master, circuit->t_read, isis_receive, circuit, - circuit->fd); + if (!circuit->is_passive) + { + THREAD_READ_ON (master, circuit->t_read, isis_receive, circuit, + circuit->fd); + } return retval; } @@ -1836,10 +2189,7 @@ isis_receive (struct thread *thread) circuit->t_read = NULL; - if (circuit->rcv_stream == NULL) - circuit->rcv_stream = stream_new (ISO_MTU (circuit)); - else - stream_reset (circuit->rcv_stream); + isis_circuit_stream(circuit, &circuit->rcv_stream); retval = circuit->rx (circuit, ssnpa); @@ -1849,10 +2199,13 @@ isis_receive (struct thread *thread) /* * prepare for next packet. */ - circuit->t_read = thread_add_timer_msec (master, isis_receive, circuit, - listcount - (circuit->area->circuit_list) * - 100); + if (!circuit->is_passive) + { + circuit->t_read = thread_add_timer_msec (master, isis_receive, circuit, + listcount + (circuit->area->circuit_list) * + 100); + } return retval; } @@ -1927,14 +2280,13 @@ send_hello (struct isis_circuit *circuit, int level) struct isis_fixed_hdr fixed_hdr; struct isis_lan_hello_hdr hello_hdr; struct isis_p2p_hello_hdr p2p_hello_hdr; - char hmac_md5_hash[ISIS_AUTH_MD5_SIZE]; - + unsigned char hmac_md5_hash[ISIS_AUTH_MD5_SIZE]; + size_t len_pointer, length, auth_tlv_offset = 0; u_int32_t interval; - unsigned long len_pointer, length, auth_tlv; int retval; - if (circuit->state != C_STATE_UP || circuit->interface == NULL) - return ISIS_WARNING; + if (circuit->is_passive) + return ISIS_OK; if (circuit->interface->mtu == 0) { @@ -1942,13 +2294,10 @@ send_hello (struct isis_circuit *circuit, int level) return ISIS_WARNING; } - if (!circuit->snd_stream) - circuit->snd_stream = stream_new (ISO_MTU (circuit)); - else - stream_reset (circuit->snd_stream); + isis_circuit_stream(circuit, &circuit->snd_stream); if (circuit->circ_type == CIRCUIT_T_BROADCAST) - if (level == 1) + if (level == IS_LEVEL_1) fill_fixed_hdr_andstream (&fixed_hdr, L1_LAN_HELLO, circuit->snd_stream); else @@ -1963,12 +2312,9 @@ send_hello (struct isis_circuit *circuit, int level) memset (&hello_hdr, 0, sizeof (struct isis_lan_hello_hdr)); interval = circuit->hello_multiplier[level - 1] * circuit->hello_interval[level - 1]; - /* If we are the DIS then hello interval is divided by three, as is the hold-timer */ - if (circuit->u.bc.is_dr[level - 1]) - interval=interval/3; if (interval > USHRT_MAX) interval = USHRT_MAX; - hello_hdr.circuit_t = circuit->circuit_is_type; + hello_hdr.circuit_t = circuit->is_type; memcpy (hello_hdr.source_id, isis->sysid, ISIS_SYS_ID_LEN); hello_hdr.hold_time = htons ((u_int16_t) interval); @@ -1985,13 +2331,13 @@ send_hello (struct isis_circuit *circuit, int level) } else { - hello_hdr.prio = circuit->u.bc.priority[level - 1]; - if (level == 1 && circuit->u.bc.l1_desig_is) + hello_hdr.prio = circuit->priority[level - 1]; + if (level == IS_LEVEL_1) { memcpy (hello_hdr.lan_id, circuit->u.bc.l1_desig_is, ISIS_SYS_ID_LEN + 1); } - else if (level == 2 && circuit->u.bc.l2_desig_is) + else if (level == IS_LEVEL_2) { memcpy (hello_hdr.lan_id, circuit->u.bc.l2_desig_is, ISIS_SYS_ID_LEN + 1); @@ -2000,68 +2346,73 @@ send_hello (struct isis_circuit *circuit, int level) } /* - * Then the variable length part + * Then the variable length part. */ /* add circuit password */ - /* Cleartext */ - if (circuit->passwd.type == ISIS_PASSWD_TYPE_CLEARTXT) - if (tlv_add_authinfo (ISIS_PASSWD_TYPE_CLEARTXT, circuit->passwd.len, - circuit->passwd.passwd, circuit->snd_stream)) - return ISIS_WARNING; + switch (circuit->passwd.type) + { + /* Cleartext */ + case ISIS_PASSWD_TYPE_CLEARTXT: + if (tlv_add_authinfo (circuit->passwd.type, circuit->passwd.len, + circuit->passwd.passwd, circuit->snd_stream)) + return ISIS_WARNING; + break; - /* or HMAC MD5 */ - if (circuit->passwd.type == ISIS_PASSWD_TYPE_HMAC_MD5) - { + /* HMAC MD5 */ + case ISIS_PASSWD_TYPE_HMAC_MD5: /* Remember where TLV is written so we can later overwrite the MD5 hash */ - auth_tlv = stream_get_endp (circuit->snd_stream); + auth_tlv_offset = stream_get_endp (circuit->snd_stream); memset(&hmac_md5_hash, 0, ISIS_AUTH_MD5_SIZE); - if (tlv_add_authinfo (ISIS_PASSWD_TYPE_HMAC_MD5, ISIS_AUTH_MD5_SIZE, - hmac_md5_hash, circuit->snd_stream)) - return ISIS_WARNING; + if (tlv_add_authinfo (circuit->passwd.type, ISIS_AUTH_MD5_SIZE, + hmac_md5_hash, circuit->snd_stream)) + return ISIS_WARNING; + break; + + default: + break; + } + + /* Area Addresses TLV */ + if (listcount (circuit->area->area_addrs) == 0) + return ISIS_WARNING; + if (tlv_add_area_addrs (circuit->area->area_addrs, circuit->snd_stream)) + return ISIS_WARNING; + + /* LAN Neighbors TLV */ + if (circuit->circ_type == CIRCUIT_T_BROADCAST) + { + if (level == IS_LEVEL_1 && circuit->u.bc.lan_neighs[0] && + listcount (circuit->u.bc.lan_neighs[0]) > 0) + if (tlv_add_lan_neighs (circuit->u.bc.lan_neighs[0], + circuit->snd_stream)) + return ISIS_WARNING; + if (level == IS_LEVEL_2 && circuit->u.bc.lan_neighs[1] && + listcount (circuit->u.bc.lan_neighs[1]) > 0) + if (tlv_add_lan_neighs (circuit->u.bc.lan_neighs[1], + circuit->snd_stream)) + return ISIS_WARNING; } /* Protocols Supported TLV */ if (circuit->nlpids.count > 0) if (tlv_add_nlpid (&circuit->nlpids, circuit->snd_stream)) return ISIS_WARNING; - - /* Area Addresses TLV */ - assert (circuit->area); - if (circuit->area->area_addrs && circuit->area->area_addrs->count > 0) - if (tlv_add_area_addrs (circuit->area->area_addrs, circuit->snd_stream)) - return ISIS_WARNING; - /* IP interface Address TLV */ - if (circuit->ip_router && circuit->ip_addrs && circuit->ip_addrs->count > 0) + if (circuit->ip_router && circuit->ip_addrs && + listcount (circuit->ip_addrs) > 0) if (tlv_add_ip_addrs (circuit->ip_addrs, circuit->snd_stream)) return ISIS_WARNING; #ifdef HAVE_IPV6 /* IPv6 Interface Address TLV */ if (circuit->ipv6_router && circuit->ipv6_link && - circuit->ipv6_link->count > 0) + listcount (circuit->ipv6_link) > 0) if (tlv_add_ipv6_addrs (circuit->ipv6_link, circuit->snd_stream)) return ISIS_WARNING; #endif /* HAVE_IPV6 */ - /* Restart signaling, vendor C sends it too */ - retval = add_tlv (211, 3, 0, circuit->snd_stream); - - /* LAN Neighbors TLV */ - if (circuit->circ_type == CIRCUIT_T_BROADCAST) - { - if (level == 1 && circuit->u.bc.lan_neighs[0]->count > 0) - if (tlv_add_lan_neighs (circuit->u.bc.lan_neighs[0], - circuit->snd_stream)) - return ISIS_WARNING; - if (level == 2 && circuit->u.bc.lan_neighs[1]->count > 0) - if (tlv_add_lan_neighs (circuit->u.bc.lan_neighs[1], - circuit->snd_stream)) - return ISIS_WARNING; - } - - if (circuit->u.bc.pad_hellos) + if (circuit->pad_hellos) if (tlv_add_padding (circuit->snd_stream)) return ISIS_WARNING; @@ -2072,41 +2423,40 @@ send_hello (struct isis_circuit *circuit, int level) /* For HMAC MD5 we need to compute the md5 hash and store it */ if (circuit->passwd.type == ISIS_PASSWD_TYPE_HMAC_MD5) { - hmac_md5(circuit->snd_stream->data, stream_get_endp(circuit->snd_stream), (unsigned char *) &circuit->passwd.passwd, circuit->passwd.len, (unsigned char *) &hmac_md5_hash); + hmac_md5 (STREAM_DATA (circuit->snd_stream), + stream_get_endp (circuit->snd_stream), + (unsigned char *) &circuit->passwd.passwd, circuit->passwd.len, + (unsigned char *) &hmac_md5_hash); /* Copy the hash into the stream */ - memcpy(circuit->snd_stream->data+auth_tlv+3,hmac_md5_hash,ISIS_AUTH_MD5_SIZE); + memcpy (STREAM_DATA (circuit->snd_stream) + auth_tlv_offset + 3, + hmac_md5_hash, ISIS_AUTH_MD5_SIZE); } - retval = circuit->tx (circuit, level); - if (retval) - zlog_warn ("sending of LAN Level %d Hello failed", level); - - /* DEBUG_ADJ_PACKETS */ if (isis->debugs & DEBUG_ADJ_PACKETS) { if (circuit->circ_type == CIRCUIT_T_BROADCAST) { - zlog_debug ("ISIS-Adj (%s): Sent L%d LAN IIH on %s, length %ld", + zlog_debug ("ISIS-Adj (%s): Sending L%d LAN IIH on %s, length %zd", circuit->area->area_tag, level, circuit->interface->name, - /* FIXME: use %z when we stop supporting old compilers. */ - (unsigned long) STREAM_SIZE (circuit->snd_stream)); + length); } else { - zlog_debug ("ISIS-Adj (%s): Sent P2P IIH on %s, length %ld", + zlog_debug ("ISIS-Adj (%s): Sending P2P IIH on %s, length %zd", circuit->area->area_tag, circuit->interface->name, - /* FIXME: use %z when we stop supporting old compilers. */ - (unsigned long) STREAM_SIZE (circuit->snd_stream)); + length); } + if (isis->debugs & DEBUG_PACKET_DUMP) + zlog_dump_data (STREAM_DATA (circuit->snd_stream), + stream_get_endp (circuit->snd_stream)); } - return retval; -} + retval = circuit->tx (circuit, level); + if (retval != ISIS_OK) + zlog_err ("ISIS-Adj (%s): Send L%d IIH on %s failed", + circuit->area->area_tag, level, circuit->interface->name); -static int -send_lan_hello (struct isis_circuit *circuit, int level) -{ - return send_hello (circuit, level); + return retval; } int @@ -2114,32 +2464,27 @@ send_lan_l1_hello (struct thread *thread) { struct isis_circuit *circuit; int retval; - unsigned long next_hello; circuit = THREAD_ARG (thread); assert (circuit); - - if (!circuit->area) { - return ISIS_OK; - } - - /* Pseudonode sends hellos three times more than the other nodes */ - if (circuit->u.bc.is_dr[0]) - next_hello=circuit->hello_interval[0]/3+1; - else - next_hello=circuit->hello_interval[0]; - circuit->u.bc.t_send_lan_hello[0] = NULL; + if (!(circuit->area->is_type & IS_LEVEL_1)) + { + zlog_warn ("ISIS-Hello (%s): Trying to send L1 IIH in L2-only area", + circuit->area->area_tag); + return 1; + } + if (circuit->u.bc.run_dr_elect[0]) retval = isis_dr_elect (circuit, 1); - retval = send_lan_hello (circuit, 1); + retval = send_hello (circuit, 1); /* set next timer thread */ THREAD_TIMER_ON (master, circuit->u.bc.t_send_lan_hello[0], send_lan_l1_hello, circuit, - isis_jitter (next_hello, IIH_JITTER)); + isis_jitter (circuit->hello_interval[0], IIH_JITTER)); return retval; } @@ -2149,32 +2494,27 @@ send_lan_l2_hello (struct thread *thread) { struct isis_circuit *circuit; int retval; - unsigned long next_hello; circuit = THREAD_ARG (thread); assert (circuit); - - if (!circuit->area) { - return ISIS_OK; - } - - /* Pseudonode sends hellos three times more than the other nodes */ - if (circuit->u.bc.is_dr[1]) - next_hello=circuit->hello_interval[1]/3+1; - else - next_hello=circuit->hello_interval[1]; - circuit->u.bc.t_send_lan_hello[1] = NULL; + if (!(circuit->area->is_type & IS_LEVEL_2)) + { + zlog_warn ("ISIS-Hello (%s): Trying to send L2 IIH in L1 area", + circuit->area->area_tag); + return 1; + } + if (circuit->u.bc.run_dr_elect[1]) retval = isis_dr_elect (circuit, 2); - retval = send_lan_hello (circuit, 2); + retval = send_hello (circuit, 2); /* set next timer thread */ THREAD_TIMER_ON (master, circuit->u.bc.t_send_lan_hello[1], send_lan_l2_hello, circuit, - isis_jitter (next_hello, IIH_JITTER)); + isis_jitter (circuit->hello_interval[1], IIH_JITTER)); return retval; } @@ -2204,11 +2544,15 @@ build_csnp (int level, u_char * start, u_char * stop, struct list *lsps, { struct isis_fixed_hdr fixed_hdr; struct isis_passwd *passwd; - int retval = ISIS_OK; unsigned long lenp; u_int16_t length; + unsigned char hmac_md5_hash[ISIS_AUTH_MD5_SIZE]; + unsigned long auth_tlv_offset = 0; + int retval = ISIS_OK; + + isis_circuit_stream(circuit, &circuit->snd_stream); - if (level == 1) + if (level == IS_LEVEL_1) fill_fixed_hdr_andstream (&fixed_hdr, L1_COMPLETE_SEQ_NUM, circuit->snd_stream); else @@ -2232,28 +2576,143 @@ build_csnp (int level, u_char * start, u_char * stop, struct list *lsps, /* * And TLVs */ - if (level == 1) + if (level == IS_LEVEL_1) passwd = &circuit->area->area_passwd; else passwd = &circuit->area->domain_passwd; if (CHECK_FLAG(passwd->snp_auth, SNP_AUTH_SEND)) - if (passwd->type) - retval = tlv_add_authinfo (passwd->type, passwd->len, - passwd->passwd, circuit->snd_stream); - - if (!retval && lsps) - { - retval = tlv_add_lsp_entries (lsps, circuit->snd_stream); + { + switch (passwd->type) + { + /* Cleartext */ + case ISIS_PASSWD_TYPE_CLEARTXT: + if (tlv_add_authinfo (ISIS_PASSWD_TYPE_CLEARTXT, passwd->len, + passwd->passwd, circuit->snd_stream)) + return ISIS_WARNING; + break; + + /* HMAC MD5 */ + case ISIS_PASSWD_TYPE_HMAC_MD5: + /* Remember where TLV is written so we can later overwrite the MD5 hash */ + auth_tlv_offset = stream_get_endp (circuit->snd_stream); + memset(&hmac_md5_hash, 0, ISIS_AUTH_MD5_SIZE); + if (tlv_add_authinfo (ISIS_PASSWD_TYPE_HMAC_MD5, ISIS_AUTH_MD5_SIZE, + hmac_md5_hash, circuit->snd_stream)) + return ISIS_WARNING; + break; + + default: + break; } + } + + retval = tlv_add_lsp_entries (lsps, circuit->snd_stream); + if (retval != ISIS_OK) + return retval; + length = (u_int16_t) stream_get_endp (circuit->snd_stream); - assert (length >= ISIS_CSNP_HDRLEN); /* Update PU length */ stream_putw_at (circuit->snd_stream, lenp, length); + /* For HMAC MD5 we need to compute the md5 hash and store it */ + if (CHECK_FLAG(passwd->snp_auth, SNP_AUTH_SEND) && + passwd->type == ISIS_PASSWD_TYPE_HMAC_MD5) + { + hmac_md5 (STREAM_DATA (circuit->snd_stream), + stream_get_endp(circuit->snd_stream), + (unsigned char *) &passwd->passwd, passwd->len, + (unsigned char *) &hmac_md5_hash); + /* Copy the hash into the stream */ + memcpy (STREAM_DATA (circuit->snd_stream) + auth_tlv_offset + 3, + hmac_md5_hash, ISIS_AUTH_MD5_SIZE); + } + return retval; } +/* + * Count the maximum number of lsps that can be accomodated by a given size. + */ +static uint16_t +get_max_lsp_count (uint16_t size) +{ + uint16_t tlv_count; + uint16_t lsp_count; + uint16_t remaining_size; + + /* First count the full size TLVs */ + tlv_count = size / MAX_LSP_ENTRIES_TLV_SIZE; + lsp_count = tlv_count * (MAX_LSP_ENTRIES_TLV_SIZE / LSP_ENTRIES_LEN); + + /* The last TLV, if any */ + remaining_size = size % MAX_LSP_ENTRIES_TLV_SIZE; + if (remaining_size - 2 >= LSP_ENTRIES_LEN) + lsp_count += (remaining_size - 2) / LSP_ENTRIES_LEN; + + return lsp_count; +} + +/* + * Calculate the length of Authentication Info. TLV. + */ +static uint16_t +auth_tlv_length (int level, struct isis_circuit *circuit) +{ + struct isis_passwd *passwd; + uint16_t length; + + if (level == IS_LEVEL_1) + passwd = &circuit->area->area_passwd; + else + passwd = &circuit->area->domain_passwd; + + /* Also include the length of TLV header */ + length = AUTH_INFO_HDRLEN; + if (CHECK_FLAG(passwd->snp_auth, SNP_AUTH_SEND)) + { + switch (passwd->type) + { + /* Cleartext */ + case ISIS_PASSWD_TYPE_CLEARTXT: + length += passwd->len; + break; + + /* HMAC MD5 */ + case ISIS_PASSWD_TYPE_HMAC_MD5: + length += ISIS_AUTH_MD5_SIZE; + break; + + default: + break; + } + } + + return length; +} + +/* + * Calculate the maximum number of lsps that can be accomodated in a CSNP/PSNP. + */ +static uint16_t +max_lsps_per_snp (int snp_type, int level, struct isis_circuit *circuit) +{ + int snp_hdr_len; + int auth_tlv_len; + uint16_t lsp_count; + + snp_hdr_len = ISIS_FIXED_HDR_LEN; + if (snp_type == ISIS_SNP_CSNP_FLAG) + snp_hdr_len += ISIS_CSNP_HDRLEN; + else + snp_hdr_len += ISIS_PSNP_HDRLEN; + + auth_tlv_len = auth_tlv_length (level, circuit); + lsp_count = get_max_lsp_count ( + stream_get_size (circuit->snd_stream) - snp_hdr_len - auth_tlv_len); + return lsp_count; +} + /* * FIXME: support multiple CSNPs */ @@ -2261,55 +2720,100 @@ build_csnp (int level, u_char * start, u_char * stop, struct list *lsps, int send_csnp (struct isis_circuit *circuit, int level) { - int retval = ISIS_OK; u_char start[ISIS_SYS_ID_LEN + 2]; u_char stop[ISIS_SYS_ID_LEN + 2]; struct list *list = NULL; struct listnode *node; struct isis_lsp *lsp; + u_char num_lsps, loop = 1; + int i, retval = ISIS_OK; - if (circuit->state != C_STATE_UP || circuit->interface == NULL) - return ISIS_WARNING; + if (circuit->area->lspdb[level - 1] == NULL || + dict_count (circuit->area->lspdb[level - 1]) == 0) + return retval; memset (start, 0x00, ISIS_SYS_ID_LEN + 2); memset (stop, 0xff, ISIS_SYS_ID_LEN + 2); - if (circuit->area->lspdb[level - 1] && - dict_count (circuit->area->lspdb[level - 1]) > 0) + num_lsps = max_lsps_per_snp (ISIS_SNP_CSNP_FLAG, level, circuit); + + while (loop) { list = list_new (); - lsp_build_list (start, stop, list, circuit->area->lspdb[level - 1]); - - if (circuit->snd_stream == NULL) - circuit->snd_stream = stream_new (ISO_MTU (circuit)); + lsp_build_list (start, stop, num_lsps, list, + circuit->area->lspdb[level - 1]); + /* + * Update the stop lsp_id before encoding this CSNP. + */ + if (listcount (list) < num_lsps) + { + memset (stop, 0xff, ISIS_SYS_ID_LEN + 2); + } else - stream_reset (circuit->snd_stream); + { + node = listtail (list); + lsp = listgetdata (node); + memcpy (stop, lsp->lsp_header->lsp_id, ISIS_SYS_ID_LEN + 2); + } retval = build_csnp (level, start, stop, list, circuit); + if (retval != ISIS_OK) + { + zlog_err ("ISIS-Snp (%s): Build L%d CSNP on %s failed", + circuit->area->area_tag, level, circuit->interface->name); + list_delete (list); + return retval; + } if (isis->debugs & DEBUG_SNP_PACKETS) - { - zlog_debug ("ISIS-Snp (%s): Sent L%d CSNP on %s, length %ld", - circuit->area->area_tag, level, circuit->interface->name, - /* FIXME: use %z when we stop supporting old compilers. */ - (unsigned long) STREAM_SIZE (circuit->snd_stream)); - for (ALL_LIST_ELEMENTS_RO (list, node, lsp)) - { - zlog_debug ("ISIS-Snp (%s): CSNP entry %s, seq 0x%08x," - " cksum 0x%04x, lifetime %us", - circuit->area->area_tag, - rawlspid_print (lsp->lsp_header->lsp_id), - ntohl (lsp->lsp_header->seq_num), - ntohs (lsp->lsp_header->checksum), - ntohs (lsp->lsp_header->rem_lifetime)); - } - } + { + zlog_debug ("ISIS-Snp (%s): Sending L%d CSNP on %s, length %zd", + circuit->area->area_tag, level, circuit->interface->name, + stream_get_endp (circuit->snd_stream)); + for (ALL_LIST_ELEMENTS_RO (list, node, lsp)) + { + zlog_debug ("ISIS-Snp (%s): CSNP entry %s, seq 0x%08x," + " cksum 0x%04x, lifetime %us", + circuit->area->area_tag, + rawlspid_print (lsp->lsp_header->lsp_id), + ntohl (lsp->lsp_header->seq_num), + ntohs (lsp->lsp_header->checksum), + ntohs (lsp->lsp_header->rem_lifetime)); + } + if (isis->debugs & DEBUG_PACKET_DUMP) + zlog_dump_data (STREAM_DATA (circuit->snd_stream), + stream_get_endp (circuit->snd_stream)); + } + + retval = circuit->tx (circuit, level); + if (retval != ISIS_OK) + { + zlog_err ("ISIS-Snp (%s): Send L%d CSNP on %s failed", + circuit->area->area_tag, level, + circuit->interface->name); + list_delete (list); + return retval; + } + /* + * Start lsp_id of the next CSNP should be one plus the + * stop lsp_id in this current CSNP. + */ + memcpy (start, stop, ISIS_SYS_ID_LEN + 2); + loop = 0; + for (i = ISIS_SYS_ID_LEN + 1; i >= 0; --i) + { + if (start[i] < (u_char)0xff) + { + start[i] += 1; + loop = 1; + break; + } + } + memset (stop, 0xff, ISIS_SYS_ID_LEN + 2); list_delete (list); - - if (retval == ISIS_OK) - retval = circuit->tx (circuit, level); } + return retval; } @@ -2325,7 +2829,9 @@ send_l1_csnp (struct thread *thread) circuit->t_send_csnp[0] = NULL; if (circuit->circ_type == CIRCUIT_T_BROADCAST && circuit->u.bc.is_dr[0]) + { send_csnp (circuit, 1); + } /* set next timer thread */ THREAD_TIMER_ON (master, circuit->t_send_csnp[0], send_l1_csnp, circuit, isis_jitter (circuit->csnp_interval[0], CSNP_JITTER)); @@ -2345,7 +2851,9 @@ send_l2_csnp (struct thread *thread) circuit->t_send_csnp[1] = NULL; if (circuit->circ_type == CIRCUIT_T_BROADCAST && circuit->u.bc.is_dr[1]) + { send_csnp (circuit, 2); + } /* set next timer thread */ THREAD_TIMER_ON (master, circuit->t_send_csnp[1], send_l2_csnp, circuit, isis_jitter (circuit->csnp_interval[1], CSNP_JITTER)); @@ -2359,12 +2867,16 @@ build_psnp (int level, struct isis_circuit *circuit, struct list *lsps) struct isis_fixed_hdr fixed_hdr; unsigned long lenp; u_int16_t length; - int retval = 0; struct isis_lsp *lsp; struct isis_passwd *passwd; struct listnode *node; + unsigned char hmac_md5_hash[ISIS_AUTH_MD5_SIZE]; + unsigned long auth_tlv_offset = 0; + int retval = ISIS_OK; - if (level == 1) + isis_circuit_stream(circuit, &circuit->snd_stream); + + if (level == IS_LEVEL_1) fill_fixed_hdr_andstream (&fixed_hdr, L1_PARTIAL_SEQ_NUM, circuit->snd_stream); else @@ -2383,20 +2895,40 @@ build_psnp (int level, struct isis_circuit *circuit, struct list *lsps) * And TLVs */ - if (level == 1) + if (level == IS_LEVEL_1) passwd = &circuit->area->area_passwd; else passwd = &circuit->area->domain_passwd; if (CHECK_FLAG(passwd->snp_auth, SNP_AUTH_SEND)) - if (passwd->type) - retval = tlv_add_authinfo (passwd->type, passwd->len, - passwd->passwd, circuit->snd_stream); - - if (!retval && lsps) - { - retval = tlv_add_lsp_entries (lsps, circuit->snd_stream); + { + switch (passwd->type) + { + /* Cleartext */ + case ISIS_PASSWD_TYPE_CLEARTXT: + if (tlv_add_authinfo (ISIS_PASSWD_TYPE_CLEARTXT, passwd->len, + passwd->passwd, circuit->snd_stream)) + return ISIS_WARNING; + break; + + /* HMAC MD5 */ + case ISIS_PASSWD_TYPE_HMAC_MD5: + /* Remember where TLV is written so we can later overwrite the MD5 hash */ + auth_tlv_offset = stream_get_endp (circuit->snd_stream); + memset(&hmac_md5_hash, 0, ISIS_AUTH_MD5_SIZE); + if (tlv_add_authinfo (ISIS_PASSWD_TYPE_HMAC_MD5, ISIS_AUTH_MD5_SIZE, + hmac_md5_hash, circuit->snd_stream)) + return ISIS_WARNING; + break; + + default: + break; } + } + + retval = tlv_add_lsp_entries (lsps, circuit->snd_stream); + if (retval != ISIS_OK) + return retval; if (isis->debugs & DEBUG_SNP_PACKETS) { @@ -2413,10 +2945,22 @@ build_psnp (int level, struct isis_circuit *circuit, struct list *lsps) } length = (u_int16_t) stream_get_endp (circuit->snd_stream); - assert (length >= ISIS_PSNP_HDRLEN); /* Update PDU length */ stream_putw_at (circuit->snd_stream, lenp, length); + /* For HMAC MD5 we need to compute the md5 hash and store it */ + if (CHECK_FLAG(passwd->snp_auth, SNP_AUTH_SEND) && + passwd->type == ISIS_PASSWD_TYPE_HMAC_MD5) + { + hmac_md5 (STREAM_DATA (circuit->snd_stream), + stream_get_endp(circuit->snd_stream), + (unsigned char *) &passwd->passwd, passwd->len, + (unsigned char *) &hmac_md5_hash); + /* Copy the hash into the stream */ + memcpy (STREAM_DATA (circuit->snd_stream) + auth_tlv_offset + 3, + hmac_md5_hash, ISIS_AUTH_MD5_SIZE); + } + return ISIS_OK; } @@ -2427,57 +2971,74 @@ build_psnp (int level, struct isis_circuit *circuit, struct list *lsps) static int send_psnp (int level, struct isis_circuit *circuit) { - int retval = ISIS_OK; struct isis_lsp *lsp; struct list *list = NULL; struct listnode *node; + u_char num_lsps; + int retval = ISIS_OK; - if (circuit->state != C_STATE_UP || circuit->interface == NULL) - return ISIS_WARNING; + if (circuit->circ_type == CIRCUIT_T_BROADCAST && + circuit->u.bc.is_dr[level - 1]) + return ISIS_OK; - if ((circuit->circ_type == CIRCUIT_T_BROADCAST && - !circuit->u.bc.is_dr[level - 1]) || - circuit->circ_type != CIRCUIT_T_BROADCAST) - { + if (circuit->area->lspdb[level - 1] == NULL || + dict_count (circuit->area->lspdb[level - 1]) == 0) + return ISIS_OK; - if (circuit->area->lspdb[level - 1] && - dict_count (circuit->area->lspdb[level - 1]) > 0) - { - list = list_new (); - lsp_build_list_ssn (circuit, list, circuit->area->lspdb[level - 1]); - - if (listcount (list) > 0) - { - if (circuit->snd_stream == NULL) - circuit->snd_stream = stream_new (ISO_MTU (circuit)); - else - stream_reset (circuit->snd_stream); + if (! circuit->snd_stream) + return ISIS_ERROR; + num_lsps = max_lsps_per_snp (ISIS_SNP_PSNP_FLAG, level, circuit); - if (isis->debugs & DEBUG_SNP_PACKETS) - zlog_debug ("ISIS-Snp (%s): Sent L%d PSNP on %s, length %ld", - circuit->area->area_tag, level, - circuit->interface->name, - /* FIXME: use %z when we stop supporting old - * compilers. */ - (unsigned long) STREAM_SIZE (circuit->snd_stream)); + while (1) + { + list = list_new (); + lsp_build_list_ssn (circuit, num_lsps, list, + circuit->area->lspdb[level - 1]); + + if (listcount (list) == 0) + { + list_delete (list); + return ISIS_OK; + } + + retval = build_psnp (level, circuit, list); + if (retval != ISIS_OK) + { + zlog_err ("ISIS-Snp (%s): Build L%d PSNP on %s failed", + circuit->area->area_tag, level, circuit->interface->name); + list_delete (list); + return retval; + } - retval = build_psnp (level, circuit, list); - if (retval == ISIS_OK) - retval = circuit->tx (circuit, level); + if (isis->debugs & DEBUG_SNP_PACKETS) + { + zlog_debug ("ISIS-Snp (%s): Sending L%d PSNP on %s, length %zd", + circuit->area->area_tag, level, + circuit->interface->name, + stream_get_endp (circuit->snd_stream)); + if (isis->debugs & DEBUG_PACKET_DUMP) + zlog_dump_data (STREAM_DATA (circuit->snd_stream), + stream_get_endp (circuit->snd_stream)); + } + + retval = circuit->tx (circuit, level); + if (retval != ISIS_OK) + { + zlog_err ("ISIS-Snp (%s): Send L%d PSNP on %s failed", + circuit->area->area_tag, level, + circuit->interface->name); + list_delete (list); + return retval; + } - if (retval == ISIS_OK) - { - /* - * sending succeeded, we can clear SSN flags of this circuit - * for the LSPs in list - */ - for (ALL_LIST_ELEMENTS_RO (list, node, lsp)) - ISIS_CLEAR_FLAG (lsp->SSNflags, circuit); - } - } - list_delete (list); - } + /* + * sending succeeded, we can clear SSN flags of this circuit + * for the LSPs in list + */ + for (ALL_LIST_ELEMENTS_RO (list, node, lsp)) + ISIS_CLEAR_FLAG (lsp->SSNflags, circuit); + list_delete (list); } return retval; @@ -2536,90 +3097,116 @@ send_lsp (struct thread *thread) struct isis_circuit *circuit; struct isis_lsp *lsp; struct listnode *node; - int retval = 0; + int clear_srm = 1; + int retval = ISIS_OK; circuit = THREAD_ARG (thread); assert (circuit); - if (circuit->state != C_STATE_UP || circuit->interface == NULL) - return ISIS_WARNING; + if (!circuit->lsp_queue) + return ISIS_OK; - lsp = listgetdata ((node = listhead (circuit->lsp_queue))); + node = listhead (circuit->lsp_queue); /* - * Do not send if levels do not match + * Handle case where there are no LSPs on the queue. This can + * happen, for instance, if an adjacency goes down before this + * thread gets a chance to run. */ - if (!(lsp->level & circuit->circuit_is_type)) - goto dontsend; + if (!node) + return ISIS_OK; /* - * Do not send if we do not have adjacencies in state up on the circuit + * Delete LSP from lsp_queue. If it's still in queue, it is assumed + * as 'transmit pending', but send_lsp may never be called again. + * Retry will happen because SRM flag will not be cleared. */ - if (circuit->upadjcount[lsp->level - 1] == 0) - goto dontsend; - /* only send if it needs sending */ - if ((time (NULL) - lsp->last_sent) >= - circuit->area->lsp_gen_interval[lsp->level - 1]) - { + lsp = listgetdata(node); + list_delete_node (circuit->lsp_queue, node); - if (isis->debugs & DEBUG_UPDATE_PACKETS) - { - zlog_debug - ("ISIS-Upd (%s): Sent L%d LSP %s, seq 0x%08x, cksum 0x%04x," - " lifetime %us on %s", circuit->area->area_tag, lsp->level, - rawlspid_print (lsp->lsp_header->lsp_id), - ntohl (lsp->lsp_header->seq_num), - ntohs (lsp->lsp_header->checksum), - ntohs (lsp->lsp_header->rem_lifetime), - circuit->interface->name); - } - /* copy our lsp to the send buffer */ - stream_copy (circuit->snd_stream, lsp->pdu); + /* Set the last-cleared time if the queue is empty. */ + /* TODO: Is is possible that new lsps keep being added to the queue + * that the queue is never empty? */ + if (list_isempty (circuit->lsp_queue)) + circuit->lsp_queue_last_cleared = time (NULL); - retval = circuit->tx (circuit, lsp->level); + if (circuit->state != C_STATE_UP || circuit->is_passive == 1) + goto out; - /* - * If the sending succeeded, we can del the lsp from circuits - * lsp_queue - */ - if (retval == ISIS_OK) - { - list_delete_node (circuit->lsp_queue, node); + /* + * Do not send if levels do not match + */ + if (!(lsp->level & circuit->is_type)) + goto out; - /* - * On broadcast circuits also the SRMflag can be cleared - */ - if (circuit->circ_type == CIRCUIT_T_BROADCAST) - ISIS_CLEAR_FLAG (lsp->SRMflags, circuit); + /* + * Do not send if we do not have adjacencies in state up on the circuit + */ + if (circuit->upadjcount[lsp->level - 1] == 0) + goto out; + + /* stream_copy will assert and stop program execution if LSP is larger than + * the circuit's MTU. So handle and log this case here. */ + if (stream_get_endp(lsp->pdu) > stream_get_size(circuit->snd_stream)) + { + zlog_err("ISIS-Upd (%s): Can't send L%d LSP %s, seq 0x%08x," + " cksum 0x%04x, lifetime %us on %s. LSP Size is %zu" + " while interface stream size is %zu.", + circuit->area->area_tag, lsp->level, + rawlspid_print(lsp->lsp_header->lsp_id), + ntohl(lsp->lsp_header->seq_num), + ntohs(lsp->lsp_header->checksum), + ntohs(lsp->lsp_header->rem_lifetime), + circuit->interface->name, + stream_get_endp(lsp->pdu), + stream_get_size(circuit->snd_stream)); + if (isis->debugs & DEBUG_PACKET_DUMP) + zlog_dump_data(STREAM_DATA(lsp->pdu), stream_get_endp(lsp->pdu)); + retval = ISIS_ERROR; + goto out; + } - if (flags_any_set (lsp->SRMflags) == 0) - { - /* - * need to remember when we were last sent - */ - lsp->last_sent = time (NULL); - } - } - else - { - zlog_debug ("sending of level %d link state failed", lsp->level); - } + /* copy our lsp to the send buffer */ + stream_copy (circuit->snd_stream, lsp->pdu); + + if (isis->debugs & DEBUG_UPDATE_PACKETS) + { + zlog_debug + ("ISIS-Upd (%s): Sending L%d LSP %s, seq 0x%08x, cksum 0x%04x," + " lifetime %us on %s", circuit->area->area_tag, lsp->level, + rawlspid_print (lsp->lsp_header->lsp_id), + ntohl (lsp->lsp_header->seq_num), + ntohs (lsp->lsp_header->checksum), + ntohs (lsp->lsp_header->rem_lifetime), + circuit->interface->name); + if (isis->debugs & DEBUG_PACKET_DUMP) + zlog_dump_data (STREAM_DATA (circuit->snd_stream), + stream_get_endp (circuit->snd_stream)); } - else + + clear_srm = 0; + retval = circuit->tx (circuit, lsp->level); + if (retval != ISIS_OK) { - /* my belief is that if it wasn't his time, the lsp can be removed - * from the queue + zlog_err ("ISIS-Upd (%s): Send L%d LSP on %s failed %s", + circuit->area->area_tag, lsp->level, + circuit->interface->name, + (retval == ISIS_WARNING) ? "temporarily" : "permanently"); + } + +out: + if (clear_srm + || (retval == ISIS_OK && circuit->circ_type == CIRCUIT_T_BROADCAST) + || (retval != ISIS_OK && retval != ISIS_WARNING)) + { + /* SRM flag will trigger retransmission. We will not retransmit if we + * encountered a fatal error. + * On success, they should only be cleared if it's a broadcast circuit. + * On a P2P circuit, we will wait for the ack from the neighbor to clear + * the fag. */ - dontsend: - list_delete_node (circuit->lsp_queue, node); + ISIS_CLEAR_FLAG (lsp->SRMflags, circuit); } -#if 0 - /* - * If there are still LSPs send next one after lsp-interval (33 msecs) - */ - if (listcount (circuit->lsp_queue) > 0) - thread_add_timer (master, send_lsp, circuit, 1); -#endif return retval; } @@ -2633,13 +3220,10 @@ ack_lsp (struct isis_link_state_hdr *hdr, struct isis_circuit *circuit, u_int16_t length; struct isis_fixed_hdr fixed_hdr; - if (!circuit->snd_stream) - circuit->snd_stream = stream_new (ISO_MTU (circuit)); - else - stream_reset (circuit->snd_stream); + isis_circuit_stream(circuit, &circuit->snd_stream); -// fill_llc_hdr (stream); - if (level == 1) + // fill_llc_hdr (stream); + if (level == IS_LEVEL_1) fill_fixed_hdr_andstream (&fixed_hdr, L1_PARTIAL_SEQ_NUM, circuit->snd_stream); else @@ -2664,7 +3248,10 @@ ack_lsp (struct isis_link_state_hdr *hdr, struct isis_circuit *circuit, stream_putw_at (circuit->snd_stream, lenp, length); retval = circuit->tx (circuit, level); + if (retval != ISIS_OK) + zlog_err ("ISIS-Upd (%s): Send L%d LSP PSNP on %s failed", + circuit->area->area_tag, level, + circuit->interface->name); return retval; } - diff --git a/isisd/isis_pdu.h b/isisd/isis_pdu.h index c4c38e22a..3eca73193 100644 --- a/isisd/isis_pdu.h +++ b/isisd/isis_pdu.h @@ -95,7 +95,7 @@ struct isis_fixed_hdr u_char version2; u_char reserved; u_char max_area_addrs; -}; +} __attribute__ ((packed)); #define ISIS_FIXED_HDR_LEN 8 @@ -114,7 +114,7 @@ struct isis_fixed_hdr * +-------+-------+-------+-------+-------+-------+-------+-------+ * | Holding Time | 2 * +-------+-------+-------+-------+-------+-------+-------+-------+ - * | PDU Lenght | 2 + * | PDU Length | 2 * +-------+-------+-------+-------+-------+-------+-------+-------+ * | R | Priority | 1 * +-------+-------+-------+-------+-------+-------+-------+-------+ @@ -142,7 +142,7 @@ struct isis_lan_hello_hdr * +-------+-------+-------+-------+-------+-------+-------+-------+ * + Holding Time + 2 * +-------+-------+-------+-------+-------+-------+-------+-------+ - * + PDU Lenght + 2 + * + PDU Length + 2 * +-------+-------+-------+-------+-------+-------+-------+-------+ * | Local Circuit ID | 1 * +-------+-------+-------+-------+-------+-------+-------+-------+ @@ -186,12 +186,23 @@ struct isis_link_state_hdr } __attribute__ ((packed)); #define ISIS_LSP_HDR_LEN 19 +/* + * Since the length field of LSP Entries TLV is one byte long, and each LSP + * entry is LSP_ENTRIES_LEN (16) bytes long, the maximum number of LSP entries + * can be accomodated in a TLV is + * 255 / 16 = 15. + * + * Therefore, the maximum length of the LSP Entries TLV is + * 16 * 15 + 2 (header) = 242 bytes. + */ +#define MAX_LSP_ENTRIES_TLV_SIZE 242 + #define L1_COMPLETE_SEQ_NUM 24 #define L2_COMPLETE_SEQ_NUM 25 /* * L1 and L2 IS to IS complete sequence numbers PDU header * +-------+-------+-------+-------+-------+-------+-------+-------+ - * + PDU Lenght + 2 + * + PDU Length + 2 * +-------+-------+-------+-------+-------+-------+-------+-------+ * + Source ID + id_len + 1 * +-------+-------+-------+-------+-------+-------+-------+-------+ @@ -241,6 +252,8 @@ int isis_receive (struct thread *thread); #define ISIS_SNP_PSNP_FLAG 0 #define ISIS_SNP_CSNP_FLAG 1 +#define ISIS_AUTH_MD5_SIZE 16U + /* * Sending functions */ @@ -258,7 +271,4 @@ int ack_lsp (struct isis_link_state_hdr *hdr, void fill_fixed_hdr (struct isis_fixed_hdr *hdr, u_char pdu_type); int send_hello (struct isis_circuit *circuit, int level); -#define ISIS_AUTH_MD5_SIZE 16U -int authentication_check (struct isis_passwd *remote, struct isis_passwd *local, struct isis_circuit *c); - #endif /* _ZEBRA_ISIS_PDU_H */ diff --git a/isisd/isis_pfpacket.c b/isisd/isis_pfpacket.c index 8a5c3ed06..2427047b3 100644 --- a/isisd/isis_pfpacket.c +++ b/isisd/isis_pfpacket.c @@ -26,6 +26,7 @@ #include #include "log.h" +#include "network.h" #include "stream.h" #include "if.h" @@ -54,8 +55,8 @@ u_char ALL_L2_ISS[6] = { 0x01, 0x80, 0xC2, 0x00, 0x00, 0x15 }; u_char ALL_ISS[6] = { 0x09, 0x00, 0x2B, 0x00, 0x00, 0x05 }; u_char ALL_ESS[6] = { 0x09, 0x00, 0x2B, 0x00, 0x00, 0x04 }; -static char discard_buff[8192]; -static char sock_buff[8192]; +static uint8_t discard_buff[8192]; +static uint8_t sock_buff[8192]; /* * if level is 0 we are joining p2p multicast @@ -129,12 +130,13 @@ 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; } circuit->fd = fd; - if (circuit->circ_type == CIRCUIT_T_BROADCAST) + if (if_is_broadcast (circuit->interface)) { /* * Join to multicast groups @@ -142,24 +144,20 @@ 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->circuit_is_type & IS_LEVEL_1) - { - /* joining ALL_L1_ISS */ - retval = isis_multicast_join (circuit->fd, 1, - circuit->interface->ifindex); - /* joining ALL_ISS */ - retval = isis_multicast_join (circuit->fd, 3, - circuit->interface->ifindex); - } - if (circuit->circuit_is_type & IS_LEVEL_2) - /* joining ALL_L2_ISS */ - retval = isis_multicast_join (circuit->fd, 2, - circuit->interface->ifindex); + /* joining ALL_L1_ISS */ + retval |= isis_multicast_join (circuit->fd, 1, + circuit->interface->ifindex); + /* 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, + circuit->interface->ifindex); } else { retval = - isis_multicast_join (circuit->fd, 0, circuit->interface->ifindex); + isis_multicast_join (circuit->fd, 0, circuit->interface->ifindex); } return retval; @@ -184,12 +182,13 @@ isis_sock_init (struct isis_circuit *circuit) goto end; } - if (circuit->circ_type == CIRCUIT_T_BROADCAST) + /* Assign Rx and Tx callbacks are based on real if type */ + if (if_is_broadcast (circuit->interface)) { circuit->tx = isis_send_pdu_bcast; circuit->rx = isis_recv_pdu_bcast; } - else if (circuit->circ_type == CIRCUIT_T_P2P) + else if (if_is_pointopoint (circuit->interface)) { circuit->tx = isis_send_pdu_p2p; circuit->rx = isis_recv_pdu_p2p; @@ -232,19 +231,28 @@ isis_recv_pdu_bcast (struct isis_circuit *circuit, u_char * ssnpa) LLC_LEN, MSG_PEEK, (struct sockaddr *) &s_addr, (socklen_t *) &addr_len); - if (!circuit->area) { - return ISIS_OK; - } - - if (bytesread < 0) + if ((bytesread < 0) || (s_addr.sll_ifindex != (int)circuit->interface->ifindex)) { - zlog_warn ("isis_recv_packet_bcast(): fd %d, recvfrom (): %s", - circuit->fd, safe_strerror (errno)); - zlog_warn ("circuit is %s", circuit->interface->name); - zlog_warn ("circuit fd %d", circuit->fd); - zlog_warn ("bytesread %d", bytesread); + if (bytesread < 0) + { + zlog_warn ("isis_recv_packet_bcast(): ifname %s, fd %d, " + "bytesread %d, recvfrom(): %s", + circuit->interface->name, circuit->fd, bytesread, + safe_strerror (errno)); + } + if (s_addr.sll_ifindex != (int)circuit->interface->ifindex) + { + zlog_warn("packet is received on multiple interfaces: " + "socket interface %d, circuit interface %d, " + "packet type %u", + s_addr.sll_ifindex, circuit->interface->ifindex, + s_addr.sll_pkttype); + } + /* get rid of the packet */ - bytesread = read (circuit->fd, discard_buff, sizeof (discard_buff)); + bytesread = recvfrom (circuit->fd, discard_buff, sizeof (discard_buff), + MSG_DONTWAIT, (struct sockaddr *) &s_addr, + (socklen_t *) &addr_len); return ISIS_WARNING; } /* @@ -253,15 +261,22 @@ isis_recv_pdu_bcast (struct isis_circuit *circuit, u_char * ssnpa) if (!llc_check (llc) || s_addr.sll_pkttype == PACKET_OUTGOING) { /* Read the packet into discard buff */ - bytesread = read (circuit->fd, discard_buff, sizeof (discard_buff)); + bytesread = recvfrom (circuit->fd, discard_buff, sizeof (discard_buff), + MSG_DONTWAIT, (struct sockaddr *) &s_addr, + (socklen_t *) &addr_len); if (bytesread < 0) - zlog_warn ("isis_recv_pdu_bcast(): read() failed"); + zlog_warn ("isis_recv_pdu_bcast(): recvfrom() failed"); return ISIS_WARNING; } /* on lan we have to read to the static buff first */ - bytesread = recvfrom (circuit->fd, sock_buff, circuit->interface->mtu, 0, + bytesread = recvfrom (circuit->fd, sock_buff, sizeof (sock_buff), MSG_DONTWAIT, (struct sockaddr *) &s_addr, (socklen_t *) &addr_len); + if (bytesread < 0) + { + zlog_warn ("isis_recv_pdu_bcast(): recvfrom() failed"); + return ISIS_WARNING; + } /* then we lose the LLC */ stream_write (circuit->rcv_stream, sock_buff + LLC_LEN, bytesread - LLC_LEN); @@ -289,9 +304,11 @@ isis_recv_pdu_p2p (struct isis_circuit *circuit, u_char * ssnpa) if (s_addr.sll_pkttype == PACKET_OUTGOING) { /* Read the packet into discard buff */ - bytesread = read (circuit->fd, discard_buff, sizeof (discard_buff)); + bytesread = recvfrom (circuit->fd, discard_buff, sizeof (discard_buff), + MSG_DONTWAIT, (struct sockaddr *) &s_addr, + (socklen_t *) &addr_len); if (bytesread < 0) - zlog_warn ("isis_recv_pdu_p2p(): read() failed"); + zlog_warn ("isis_recv_pdu_p2p(): recvfrom() failed"); return ISIS_WARNING; } @@ -313,10 +330,12 @@ isis_recv_pdu_p2p (struct isis_circuit *circuit, u_char * ssnpa) int isis_send_pdu_bcast (struct isis_circuit *circuit, int level) { + struct msghdr msg; + struct iovec iov[2]; + /* we need to do the LLC in here because of P2P circuits, which will * not need it */ - int written = 1; struct sockaddr_ll sa; stream_set_getp (circuit->snd_stream, 0); @@ -325,7 +344,10 @@ isis_send_pdu_bcast (struct isis_circuit *circuit, int level) sa.sll_protocol = htons (stream_get_endp (circuit->snd_stream) + LLC_LEN); sa.sll_ifindex = circuit->interface->ifindex; sa.sll_halen = ETH_ALEN; - if (level == 1) + /* RFC5309 section 4.1 recommends ALL_ISS */ + if (circuit->circ_type == CIRCUIT_T_P2P) + memcpy (&sa.sll_addr, ALL_ISS, ETH_ALEN); + else if (level == 1) memcpy (&sa.sll_addr, ALL_L1_ISS, ETH_ALEN); else memcpy (&sa.sll_addr, ALL_L2_ISS, ETH_ALEN); @@ -336,24 +358,32 @@ isis_send_pdu_bcast (struct isis_circuit *circuit, int level) sock_buff[1] = 0xFE; sock_buff[2] = 0x03; - /* then we copy the data */ - memcpy (sock_buff + LLC_LEN, circuit->snd_stream->data, - stream_get_endp (circuit->snd_stream)); - - /* now we can send this */ - written = sendto (circuit->fd, sock_buff, - stream_get_endp(circuit->snd_stream) + LLC_LEN, 0, - (struct sockaddr *) &sa, sizeof (struct sockaddr_ll)); - + memset (&msg, 0, sizeof (msg)); + msg.msg_name = &sa; + msg.msg_namelen = sizeof (struct sockaddr_ll); + msg.msg_iov = iov; + msg.msg_iovlen = 2; + iov[0].iov_base = sock_buff; + iov[0].iov_len = LLC_LEN; + iov[1].iov_base = circuit->snd_stream->data; + iov[1].iov_len = stream_get_endp (circuit->snd_stream); + + if (sendmsg(circuit->fd, &msg, 0) < 0) + { + zlog_warn("IS-IS pfpacket: could not transmit packet on %s: %s", + circuit->interface->name, safe_strerror(errno)); + if (ERRNO_IO_RETRY(errno)) + return ISIS_WARNING; + return ISIS_ERROR; + } return ISIS_OK; } int isis_send_pdu_p2p (struct isis_circuit *circuit, int level) { - - int written = 1; struct sockaddr_ll sa; + ssize_t rv; stream_set_getp (circuit->snd_stream, 0); memset (&sa, 0, sizeof (struct sockaddr_ll)); @@ -369,11 +399,18 @@ isis_send_pdu_p2p (struct isis_circuit *circuit, int level) /* lets try correcting the protocol */ sa.sll_protocol = htons (0x00FE); - written = sendto (circuit->fd, circuit->snd_stream->data, - stream_get_endp (circuit->snd_stream), 0, - (struct sockaddr *) &sa, - sizeof (struct sockaddr_ll)); - + rv = sendto(circuit->fd, circuit->snd_stream->data, + stream_get_endp (circuit->snd_stream), 0, + (struct sockaddr *) &sa, + sizeof (struct sockaddr_ll)); + if (rv < 0) + { + zlog_warn("IS-IS pfpacket: could not transmit packet on %s: %s", + circuit->interface->name, safe_strerror(errno)); + if (ERRNO_IO_RETRY(errno)) + return ISIS_WARNING; + return ISIS_ERROR; + } return ISIS_OK; } diff --git a/isisd/isis_redist.c b/isisd/isis_redist.c new file mode 100644 index 000000000..ad0a31b74 --- /dev/null +++ b/isisd/isis_redist.c @@ -0,0 +1,825 @@ +/* + * IS-IS Rout(e)ing protocol - isis_redist.c + * + * Copyright (C) 2013-2015 Christian Franke + * + * 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. + */ + +#include + +#include "command.h" +#include "if.h" +#include "linklist.h" +#include "memory.h" +#include "memtypes.h" +#include "prefix.h" +#include "routemap.h" +#include "stream.h" +#include "table.h" +#include "vty.h" + +#include "isisd/dict.h" +#include "isisd/isis_constants.h" +#include "isisd/isis_common.h" +#include "isisd/isis_flags.h" +#include "isisd/isis_misc.h" +#include "isisd/isis_circuit.h" +#include "isisd/isis_tlv.h" +#include "isisd/isisd.h" +#include "isisd/isis_lsp.h" +#include "isisd/isis_route.h" +#include "isisd/isis_zebra.h" + +static int +redist_protocol(int family) +{ + if (family == AF_INET) + return 0; + if (family == AF_INET6) + return 1; + + assert(!"Unsupported address family!"); + return 0; +} + +static int +is_default(struct prefix *p) +{ + if (p->family == AF_INET) + if (p->u.prefix4.s_addr == 0 && p->prefixlen == 0) + return 1; + if (p->family == AF_INET6) + if (IN6_IS_ADDR_UNSPECIFIED(&p->u.prefix6) && p->prefixlen == 0) + return 1; + return 0; +} + +static struct route_table* +get_ext_info(struct isis *i, int family) +{ + int protocol = redist_protocol(family); + + return i->ext_info[protocol]; +} + +static struct isis_redist* +get_redist_settings(struct isis_area *area, int family, int type, int level) +{ + int protocol = redist_protocol(family); + + return &area->redist_settings[protocol][type][level-1]; +} + +struct route_table* +get_ext_reach(struct isis_area *area, int family, int level) +{ + int protocol = redist_protocol(family); + + return area->ext_reach[protocol][level-1]; +} + +static struct route_node * +isis_redist_route_node_create(route_table_delegate_t *delegate, + struct route_table *table) +{ + struct route_node *node; + node = XCALLOC(MTYPE_ROUTE_NODE, sizeof(*node)); + return node; +} + +static void +isis_redist_route_node_destroy(route_table_delegate_t *delegate, + struct route_table *table, + struct route_node *node) +{ + if (node->info) + XFREE(MTYPE_ISIS, node->info); + XFREE (MTYPE_ROUTE_NODE, node); +} + +static route_table_delegate_t isis_redist_rt_delegate = { + .create_node = isis_redist_route_node_create, + .destroy_node = isis_redist_route_node_destroy +}; + +/* Install external reachability information into a + * specific area for a specific level. + * Schedule an lsp regenerate if necessary */ +static void +isis_redist_install(struct isis_area *area, int level, + struct prefix *p, struct isis_ext_info *info) +{ + int family = p->family; + struct route_table *er_table = get_ext_reach(area, family, level); + struct route_node *er_node; + + if (!er_table) + { + zlog_warn("%s: External reachability table of area %s" + " is not initialized.", __func__, area->area_tag); + return; + } + + er_node = route_node_get(er_table, p); + if (er_node->info) + { + route_unlock_node(er_node); + + /* Don't update/reschedule lsp generation if nothing changed. */ + if (!memcmp(er_node->info, info, sizeof(*info))) + return; + } + else + { + er_node->info = XMALLOC(MTYPE_ISIS, sizeof(*info)); + } + + memcpy(er_node->info, info, sizeof(*info)); + lsp_regenerate_schedule(area, level, 0); +} + +/* Remove external reachability information from a + * specific area for a specific level. + * Schedule an lsp regenerate if necessary. */ +static void +isis_redist_uninstall(struct isis_area *area, int level, struct prefix *p) +{ + int family = p->family; + struct route_table *er_table = get_ext_reach(area, family, level); + struct route_node *er_node; + + if (!er_table) + { + zlog_warn("%s: External reachability table of area %s" + " is not initialized.", __func__, area->area_tag); + return; + } + + er_node = route_node_lookup(er_table, p); + if (!er_node) + return; + else + route_unlock_node(er_node); + + if (!er_node->info) + return; + + XFREE(MTYPE_ISIS, er_node->info); + route_unlock_node(er_node); + lsp_regenerate_schedule(area, level, 0); +} + +/* Update external reachability info of area for a given level + * and prefix, using the given redistribution settings. */ +static void +isis_redist_update_ext_reach(struct isis_area *area, int level, + struct isis_redist *redist, struct prefix *p, + struct isis_ext_info *info) +{ + struct isis_ext_info area_info; + route_map_result_t map_ret; + + memcpy(&area_info, info, sizeof(area_info)); + if (redist->metric != 0xffffffff) + area_info.metric = redist->metric; + + if (redist->map_name) + { + map_ret = route_map_apply(redist->map, p, RMAP_ISIS, &area_info); + if (map_ret == RMAP_DENYMATCH) + area_info.distance = 255; + } + + /* Allow synthesized default routes only on always orignate */ + if (area_info.origin == DEFAULT_ROUTE + && redist->redist != DEFAULT_ORIGINATE_ALWAYS) + area_info.distance = 255; + + if (area_info.distance < 255) + isis_redist_install(area, level, p, &area_info); + else + isis_redist_uninstall(area, level, p); +} + +static void +isis_redist_ensure_default(struct isis *isis, int family) +{ + struct prefix p; + struct route_table *ei_table = get_ext_info(isis, family); + struct route_node *ei_node; + struct isis_ext_info *info; + + if (family == AF_INET) + { + p.family = AF_INET; + p.prefixlen = 0; + memset(&p.u.prefix4, 0, sizeof(p.u.prefix4)); + } + else if (family == AF_INET6) + { + p.family = AF_INET6; + p.prefixlen = 0; + memset(&p.u.prefix6, 0, sizeof(p.u.prefix6)); + } + else + assert(!"Unknown family!"); + + ei_node = route_node_get(ei_table, &p); + if (ei_node->info) + { + route_unlock_node(ei_node); + return; + } + + ei_node->info = XCALLOC(MTYPE_ISIS, sizeof(struct isis_ext_info)); + + info = ei_node->info; + info->origin = DEFAULT_ROUTE; + info->distance = 254; + info->metric = MAX_WIDE_PATH_METRIC; +} + +/* Handle notification about route being added */ +void +isis_redist_add(int type, struct prefix *p, u_char distance, uint32_t metric) +{ + int family = p->family; + struct route_table *ei_table = get_ext_info(isis, family); + struct route_node *ei_node; + struct isis_ext_info *info; + struct listnode *node; + struct isis_area *area; + int level; + struct isis_redist *redist; + + char debug_buf[BUFSIZ]; + prefix2str(p, debug_buf, sizeof(debug_buf)); + + zlog_debug("%s: New route %s from %s.", __func__, debug_buf, + zebra_route_string(type)); + + if (!ei_table) + { + zlog_warn("%s: External information table not initialized.", + __func__); + return; + } + + ei_node = route_node_get(ei_table, p); + if (ei_node->info) + route_unlock_node(ei_node); + else + ei_node->info = XCALLOC(MTYPE_ISIS, sizeof(struct isis_ext_info)); + + info = ei_node->info; + info->origin = type; + info->distance = distance; + info->metric = metric; + + if (is_default(p)) + type = DEFAULT_ROUTE; + + for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) + for (level = 1; level <= ISIS_LEVELS; level++) + { + redist = get_redist_settings(area, family, type, level); + if (!redist->redist) + continue; + + isis_redist_update_ext_reach(area, level, redist, p, info); + } +} + +void +isis_redist_delete(int type, struct prefix *p) +{ + int family = p->family; + struct route_table *ei_table = get_ext_info(isis, family); + struct route_node *ei_node; + struct listnode *node; + struct isis_area *area; + int level; + struct isis_redist *redist; + + char debug_buf[BUFSIZ]; + prefix2str(p, debug_buf, sizeof(debug_buf)); + + zlog_debug("%s: Removing route %s from %s.", __func__, debug_buf, + zebra_route_string(type)); + + if (is_default(p)) + { + /* Don't remove default route but add synthetic route for use + * by "default-information originate always". Areas without the + * "always" setting will ignore routes with origin DEFAULT_ROUTE. */ + isis_redist_add(DEFAULT_ROUTE, p, 254, MAX_WIDE_PATH_METRIC); + return; + } + + if (!ei_table) + { + zlog_warn("%s: External information table not initialized.", + __func__); + return; + } + + ei_node = route_node_lookup(ei_table, p); + if (!ei_node || !ei_node->info) + { + char buf[BUFSIZ]; + prefix2str(p, buf, sizeof(buf)); + zlog_warn("%s: Got a delete for %s route %s, but that route" + " was never added.", __func__, zebra_route_string(type), + buf); + if (ei_node) + route_unlock_node(ei_node); + return; + } + route_unlock_node(ei_node); + + for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) + for (level = 1; level < ISIS_LEVELS; level++) + { + redist = get_redist_settings(area, family, type, level); + if (!redist->redist) + continue; + + isis_redist_uninstall(area, level, p); + } + + XFREE(MTYPE_ISIS, ei_node->info); + route_unlock_node(ei_node); +} + +static void +isis_redist_routemap_set(struct isis_redist *redist, const char *routemap) +{ + if (redist->map_name) { + XFREE(MTYPE_ISIS, redist->map_name); + redist->map = NULL; + } + + if (routemap && strlen(routemap)) { + redist->map_name = XSTRDUP(MTYPE_ISIS, routemap); + redist->map = route_map_lookup_by_name(routemap); + } +} + +static void +isis_redist_update_zebra_subscriptions(struct isis *isis) +{ + struct listnode *node; + struct isis_area *area; + int type; + int level; + int protocol; + + char do_subscribe[ZEBRA_ROUTE_MAX + 1]; + + memset(do_subscribe, 0, sizeof(do_subscribe)); + + for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) + for (protocol = 0; protocol < REDIST_PROTOCOL_COUNT; protocol++) + for (type = 0; type < ZEBRA_ROUTE_MAX + 1; type++) + for (level = 0; level < ISIS_LEVELS; level++) + if (area->redist_settings[protocol][type][level].redist) + do_subscribe[type] = 1; + + for (type = 0; type < ZEBRA_ROUTE_MAX + 1; type++) + { + /* This field is actually controlling transmission of the IS-IS + * routes to Zebra and has nothing to do with redistribution, + * so skip it. */ + if (type == ZEBRA_ROUTE_ISIS) + continue; + + if (do_subscribe[type]) + isis_zebra_redistribute_set(type); + else + isis_zebra_redistribute_unset(type); + } +} + +static void +isis_redist_set(struct isis_area *area, int level, + int family, int type, uint32_t metric, + const char *routemap, int originate_type) +{ + int protocol = redist_protocol(family); + struct isis_redist *redist = get_redist_settings(area, family, type, level); + int i; + struct route_table *ei_table; + struct route_node *rn; + struct isis_ext_info *info; + + redist->redist = (type == DEFAULT_ROUTE) ? originate_type : 1; + redist->metric = metric; + isis_redist_routemap_set(redist, routemap); + + if (!area->ext_reach[protocol][level-1]) + { + area->ext_reach[protocol][level-1] = + route_table_init_with_delegate(&isis_redist_rt_delegate); + } + + for (i = 0; i < REDIST_PROTOCOL_COUNT; i++) + if (!area->isis->ext_info[i]) + { + area->isis->ext_info[i] = + route_table_init_with_delegate(&isis_redist_rt_delegate); + } + + isis_redist_update_zebra_subscriptions(area->isis); + + if (type == DEFAULT_ROUTE && originate_type == DEFAULT_ORIGINATE_ALWAYS) + isis_redist_ensure_default(area->isis, family); + + ei_table = get_ext_info(area->isis, family); + for (rn = route_top(ei_table); rn; rn = route_next(rn)) + { + if (!rn->info) + continue; + info = rn->info; + + if (type == DEFAULT_ROUTE) + { + if (!is_default(&rn->p)) + continue; + } + else + { + if (info->origin != type) + continue; + } + + isis_redist_update_ext_reach(area, level, redist, &rn->p, info); + } +} + +static void +isis_redist_unset(struct isis_area *area, int level, + int family, int type) +{ + struct isis_redist *redist = get_redist_settings(area, family, type, level); + struct route_table *er_table = get_ext_reach(area, family, level); + struct route_node *rn; + struct isis_ext_info *info; + + if (!redist->redist) + return; + + redist->redist = 0; + if (!er_table) + { + zlog_warn("%s: External reachability table uninitialized.", __func__); + return; + } + + for (rn = route_top(er_table); rn; rn = route_next(rn)) + { + if (!rn->info) + continue; + info = rn->info; + + if (type == DEFAULT_ROUTE) + { + if (!is_default(&rn->p)) + continue; + } + else + { + if (info->origin != type) + continue; + } + + XFREE(MTYPE_ISIS, rn->info); + route_unlock_node(rn); + } + + lsp_regenerate_schedule(area, level, 0); + isis_redist_update_zebra_subscriptions(area->isis); +} + +void +isis_redist_area_finish(struct isis_area *area) +{ + int protocol; + int level; + int type; + + for (protocol = 0; protocol < REDIST_PROTOCOL_COUNT; protocol++) + for (level = 0; level < ISIS_LEVELS; level++) + { + for (type = 0; type < ZEBRA_ROUTE_MAX + 1; type++) + { + struct isis_redist *redist; + + redist = &area->redist_settings[protocol][type][level]; + redist->redist = 0; + if (redist->map_name) + XFREE(MTYPE_ISIS, redist->map_name); + } + route_table_finish(area->ext_reach[protocol][level]); + } + + isis_redist_update_zebra_subscriptions(area->isis); +} + +DEFUN(isis_redistribute, + isis_redistribute_cmd, + "redistribute (ipv4|ipv6) " QUAGGA_REDIST_STR_ISISD + " (level-1|level-2) {metric <0-16777215>|route-map WORD}", + REDIST_STR + "Redistribute IPv4 routes\n" + "Redistribute IPv6 routes\n" + QUAGGA_REDIST_HELP_STR_ISISD + "Redistribute into level-1\n" + "Redistribute into level-2\n" + "Metric for redistributed routes\n" + "ISIS default metric\n" + "Route map reference\n" + "Pointer to route-map entries\n") +{ + struct isis_area *area = vty->index; + int family; + int afi; + int type; + int level; + unsigned long metric; + const char *routemap; + + if (argc < 5) + return CMD_WARNING; + + family = str2family(argv[0]); + if (family < 0) + return CMD_WARNING; + + afi = family2afi(family); + if (!afi) + return CMD_WARNING; + + type = proto_redistnum(afi, argv[1]); + if (type < 0 || type == ZEBRA_ROUTE_ISIS) + return CMD_WARNING; + + if (!strcmp("level-1", argv[2])) + level = 1; + else if (!strcmp("level-2", argv[2])) + level = 2; + else + return CMD_WARNING; + + if ((area->is_type & level) != level) + { + vty_out(vty, "Node is not a level-%d IS%s", level, VTY_NEWLINE); + return CMD_WARNING; + } + + if (argv[3]) + { + char *endp; + metric = strtoul(argv[3], &endp, 10); + if (argv[3][0] == '\0' || *endp != '\0') + return CMD_WARNING; + } + else + { + metric = 0xffffffff; + } + + routemap = argv[4]; + + isis_redist_set(area, level, family, type, metric, routemap, 0); + return 0; +} + +DEFUN(no_isis_redistribute, + no_isis_redistribute_cmd, + "no redistribute (ipv4|ipv6) " QUAGGA_REDIST_STR_ISISD + " (level-1|level-2)", + NO_STR + REDIST_STR + "Redistribute IPv4 routes\n" + "Redistribute IPv6 routes\n" + QUAGGA_REDIST_HELP_STR_ISISD + "Redistribute into level-1\n" + "Redistribute into level-2\n") +{ + struct isis_area *area = vty->index; + int type; + int level; + int family; + int afi; + + if (argc < 3) + return CMD_WARNING; + + family = str2family(argv[0]); + if (family < 0) + return CMD_WARNING; + + afi = family2afi(family); + if (!afi) + return CMD_WARNING; + + type = proto_redistnum(afi, argv[1]); + if (type < 0 || type == ZEBRA_ROUTE_ISIS) + return CMD_WARNING; + + if (!strcmp("level-1", argv[2])) + level = 1; + else if (!strcmp("level-2", argv[2])) + level = 2; + else + return CMD_WARNING; + + isis_redist_unset(area, level, family, type); + return 0; +} + +DEFUN(isis_default_originate, + isis_default_originate_cmd, + "default-information originate (ipv4|ipv6) (level-1|level-2) " + "{always|metric <0-16777215>|route-map WORD}", + "Control distribution of default information\n" + "Distribute a default route\n" + "Distribute default route for IPv4\n" + "Distribute default route for IPv6\n" + "Distribute default route into level-1\n" + "Distribute default route into level-2\n" + "Always advertise default route\n" + "Metric for default route\n" + "ISIS default metric\n" + "Route map reference\n" + "Pointer to route-map entries\n") +{ + struct isis_area *area = vty->index; + int family; + int originate_type; + int level; + unsigned long metric; + const char *routemap; + + if (argc < 5) + return CMD_WARNING; + + family = str2family(argv[0]); + if (family < 0) + return CMD_WARNING; + + if (!strcmp("level-1", argv[1])) + level = 1; + else if (!strcmp("level-2", argv[1])) + level = 2; + else + return CMD_WARNING; + + if ((area->is_type & level) != level) + { + vty_out(vty, "Node is not a level-%d IS%s", level, VTY_NEWLINE); + return CMD_WARNING; + } + + if (argv[2] && *argv[2] != '\0') + originate_type = DEFAULT_ORIGINATE_ALWAYS; + else + originate_type = DEFAULT_ORIGINATE; + + if (family == AF_INET6 && originate_type != DEFAULT_ORIGINATE_ALWAYS) + { + vty_out(vty, "Zebra doesn't implement default-originate for IPv6 yet%s", VTY_NEWLINE); + vty_out(vty, "so use with care or use default-originate always.%s", VTY_NEWLINE); + } + + if (argv[3]) + { + char *endp; + metric = strtoul(argv[3], &endp, 10); + if (argv[3][0] == '\0' || *endp != '\0') + return CMD_WARNING; + } + else + { + metric = 0xffffffff; + } + + routemap = argv[4]; + + isis_redist_set(area, level, family, DEFAULT_ROUTE, metric, routemap, originate_type); + return 0; +} + +DEFUN(no_isis_default_originate, + no_isis_default_originate_cmd, + "no default-information originate (ipv4|ipv6) (level-1|level-2)", + NO_STR + "Control distribution of default information\n" + "Distribute a default route\n" + "Distribute default route for IPv4\n" + "Distribute default route for IPv6\n" + "Distribute default route into level-1\n" + "Distribute default route into level-2\n") +{ + struct isis_area *area = vty->index; + + int family; + int level; + + if (argc < 2) + return CMD_WARNING; + + family = str2family(argv[0]); + if (family < 0) + return CMD_WARNING; + + if (!strcmp("level-1", argv[1])) + level = 1; + else if (!strcmp("level-2", argv[1])) + level = 2; + else + return CMD_WARNING; + + isis_redist_unset(area, level, family, DEFAULT_ROUTE); + return 0; +} + +int +isis_redist_config_write(struct vty *vty, struct isis_area *area, + int family) +{ + int type; + int level; + int write = 0; + struct isis_redist *redist; + const char *family_str; + + if (family == AF_INET) + family_str = "ipv4"; + else if (family == AF_INET6) + family_str = "ipv6"; + else + return 0; + + for (type = 0; type < ZEBRA_ROUTE_MAX; type++) + { + if (type == ZEBRA_ROUTE_ISIS) + continue; + + for (level = 1; level <= ISIS_LEVELS; level++) + { + redist = get_redist_settings(area, family, type, level); + if (!redist->redist) + continue; + vty_out(vty, " redistribute %s %s level-%d", + family_str, zebra_route_string(type), level); + if (redist->metric != 0xffffffff) + vty_out(vty, " metric %u", redist->metric); + if (redist->map_name) + vty_out(vty, " route-map %s", redist->map_name); + vty_out(vty, "%s", VTY_NEWLINE); + write++; + } + } + + for (level = 1; level <= ISIS_LEVELS; level++) + { + redist = get_redist_settings(area, family, DEFAULT_ROUTE, level); + if (!redist->redist) + continue; + vty_out(vty, " default-information originate %s level-%d", + family_str, level); + if (redist->redist == DEFAULT_ORIGINATE_ALWAYS) + vty_out(vty, " always"); + if (redist->metric != 0xffffffff) + vty_out(vty, " metric %u", redist->metric); + if (redist->map_name) + vty_out(vty, " route-map %s", redist->map_name); + vty_out(vty, "%s", VTY_NEWLINE); + write++; + } + + return write; +} + +void +isis_redist_init(void) +{ + install_element(ISIS_NODE, &isis_redistribute_cmd); + install_element(ISIS_NODE, &no_isis_redistribute_cmd); + install_element(ISIS_NODE, &isis_default_originate_cmd); + install_element(ISIS_NODE, &no_isis_default_originate_cmd); +} diff --git a/isisd/isis_redist.h b/isisd/isis_redist.h new file mode 100644 index 000000000..cc9c2e634 --- /dev/null +++ b/isisd/isis_redist.h @@ -0,0 +1,59 @@ +/* + * IS-IS Rout(e)ing protocol - isis_redist.h + * + * Copyright (C) 2013-2015 Christian Franke + * + * 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. + */ + +#ifndef ISIS_REDIST_H +#define ISIS_REDIST_H + +#define REDIST_PROTOCOL_COUNT 2 + +#define DEFAULT_ROUTE ZEBRA_ROUTE_MAX +#define DEFAULT_ORIGINATE 1 +#define DEFAULT_ORIGINATE_ALWAYS 2 + +struct isis_ext_info +{ + int origin; + uint32_t metric; + u_char distance; +}; + +struct isis_redist +{ + int redist; + uint32_t metric; + char *map_name; + struct route_map *map; +}; + +struct isis_area; +struct prefix; +struct vty; + +struct route_table *get_ext_reach(struct isis_area *area, + int family, int level); +void isis_redist_add(int type, struct prefix *p, + u_char distance, uint32_t metric); +void isis_redist_delete(int type, struct prefix *p); +int isis_redist_config_write(struct vty *vty, struct isis_area *area, + int family); +void isis_redist_init(void); +void isis_redist_area_finish(struct isis_area *area); + +#endif diff --git a/isisd/isis_route.c b/isisd/isis_route.c index 1286486ce..c0ec01e84 100644 --- a/isisd/isis_route.c +++ b/isisd/isis_route.c @@ -36,6 +36,7 @@ #include "isis_constants.h" #include "isis_common.h" +#include "isis_flags.h" #include "dict.h" #include "isisd.h" #include "isis_misc.h" @@ -48,11 +49,8 @@ #include "isis_route.h" #include "isis_zebra.h" -extern struct isis *isis; -extern struct thread_master *master; - static struct isis_nexthop * -isis_nexthop_create (struct in_addr *ip, unsigned int ifindex) +isis_nexthop_create (struct in_addr *ip, ifindex_t ifindex) { struct listnode *node; struct isis_nexthop *nexthop; @@ -69,10 +67,6 @@ isis_nexthop_create (struct in_addr *ip, unsigned int ifindex) } nexthop = XCALLOC (MTYPE_ISIS_NEXTHOP, sizeof (struct isis_nexthop)); - if (!nexthop) - { - zlog_err ("ISIS-Rte: isis_nexthop_create: out of memory!"); - } nexthop->ifindex = ifindex; memcpy (&nexthop->ip, ip, sizeof (struct in_addr)); @@ -97,7 +91,7 @@ isis_nexthop_delete (struct isis_nexthop *nexthop) static int nexthoplookup (struct list *nexthops, struct in_addr *ip, - unsigned int ifindex) + ifindex_t ifindex) { struct listnode *node; struct isis_nexthop *nh; @@ -136,15 +130,11 @@ nexthops_print (struct list *nhs) #ifdef HAVE_IPV6 static struct isis_nexthop6 * -isis_nexthop6_new (struct in6_addr *ip6, unsigned int ifindex) +isis_nexthop6_new (struct in6_addr *ip6, ifindex_t ifindex) { struct isis_nexthop6 *nexthop6; nexthop6 = XCALLOC (MTYPE_ISIS_NEXTHOP6, sizeof (struct isis_nexthop6)); - if (!nexthop6) - { - zlog_err ("ISIS-Rte: isis_nexthop_create6: out of memory!"); - } nexthop6->ifindex = ifindex; memcpy (&nexthop6->ip6, ip6, sizeof (struct in6_addr)); @@ -154,7 +144,7 @@ isis_nexthop6_new (struct in6_addr *ip6, unsigned int ifindex) } static struct isis_nexthop6 * -isis_nexthop6_create (struct in6_addr *ip6, unsigned int ifindex) +isis_nexthop6_create (struct in6_addr *ip6, ifindex_t ifindex) { struct listnode *node; struct isis_nexthop6 *nexthop6; @@ -191,7 +181,7 @@ isis_nexthop6_delete (struct isis_nexthop6 *nexthop6) static int nexthop6lookup (struct list *nexthops6, struct in6_addr *ip6, - unsigned int ifindex) + ifindex_t ifindex) { struct listnode *node; struct isis_nexthop6 *nh6; @@ -246,6 +236,7 @@ adjinfo2nexthop (struct list *nexthops, struct isis_adjacency *adj) { nh = isis_nexthop_create (ipv4_addr, adj->circuit->interface->ifindex); + nh->router_address = adj->router_address; listnode_add (nexthops, nh); } } @@ -269,6 +260,7 @@ adjinfo2nexthop6 (struct list *nexthops6, struct isis_adjacency *adj) { nh6 = isis_nexthop6_create (ipv6_addr, adj->circuit->interface->ifindex); + nh6->router_address6 = adj->router_address6; listnode_add (nexthops6, nh6); } } @@ -276,32 +268,43 @@ adjinfo2nexthop6 (struct list *nexthops6, struct isis_adjacency *adj) #endif /* HAVE_IPV6 */ static struct isis_route_info * -isis_route_info_new (uint32_t cost, uint32_t depth, u_char family, - struct list *adjacencies) +isis_route_info_new (struct prefix *prefix, uint32_t cost, uint32_t depth, + struct list *adjacencies) { struct isis_route_info *rinfo; struct isis_adjacency *adj; struct listnode *node; rinfo = XCALLOC (MTYPE_ISIS_ROUTE_INFO, sizeof (struct isis_route_info)); - if (!rinfo) - { - zlog_err ("ISIS-Rte: isis_route_info_new: out of memory!"); - return NULL; - } - if (family == AF_INET) + if (prefix->family == AF_INET) { rinfo->nexthops = list_new (); for (ALL_LIST_ELEMENTS_RO (adjacencies, node, adj)) - adjinfo2nexthop (rinfo->nexthops, adj); + { + /* check for force resync this route */ + if (CHECK_FLAG (adj->circuit->flags, ISIS_CIRCUIT_FLAPPED_AFTER_SPF)) + SET_FLAG (rinfo->flag, ISIS_ROUTE_FLAG_ZEBRA_RESYNC); + /* update neighbor router address */ + if (depth == 2 && prefix->prefixlen == 32) + adj->router_address = prefix->u.prefix4; + adjinfo2nexthop (rinfo->nexthops, adj); + } } #ifdef HAVE_IPV6 - if (family == AF_INET6) + if (prefix->family == AF_INET6) { rinfo->nexthops6 = list_new (); for (ALL_LIST_ELEMENTS_RO (adjacencies, node, adj)) - adjinfo2nexthop6 (rinfo->nexthops6, adj); + { + /* check for force resync this route */ + if (CHECK_FLAG (adj->circuit->flags, ISIS_CIRCUIT_FLAPPED_AFTER_SPF)) + SET_FLAG (rinfo->flag, ISIS_ROUTE_FLAG_ZEBRA_RESYNC); + /* update neighbor router address */ + if (depth == 2 && prefix->prefixlen == 128) + adj->router_address6 = prefix->u.prefix6; + adjinfo2nexthop6 (rinfo->nexthops6, adj); + } } #endif /* HAVE_IPV6 */ @@ -353,18 +356,25 @@ isis_route_info_same (struct isis_route_info *new, #ifdef HAVE_IPV6 struct isis_nexthop6 *nexthop6; #endif /* HAVE_IPV6 */ + + if (!CHECK_FLAG (old->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNCED)) + return 0; + + if (CHECK_FLAG (new->flag, ISIS_ROUTE_FLAG_ZEBRA_RESYNC)) + return 0; + if (!isis_route_info_same_attrib (new, old)) return 0; if (family == AF_INET) { for (ALL_LIST_ELEMENTS_RO (new->nexthops, node, nexthop)) - if (nexthoplookup (old->nexthops, &nexthop->ip, nexthop->ifindex) + if (nexthoplookup (old->nexthops, &nexthop->ip, nexthop->ifindex) == 0) return 0; for (ALL_LIST_ELEMENTS_RO (old->nexthops, node, nexthop)) - if (nexthoplookup (new->nexthops, &nexthop->ip, nexthop->ifindex) + if (nexthoplookup (new->nexthops, &nexthop->ip, nexthop->ifindex) == 0) return 0; } @@ -386,65 +396,6 @@ isis_route_info_same (struct isis_route_info *new, return 1; } -static void -isis_nexthops_merge (struct list *new, struct list *old) -{ - struct listnode *node; - struct isis_nexthop *nexthop; - - for (ALL_LIST_ELEMENTS_RO (new, node, nexthop)) - { - if (nexthoplookup (old, &nexthop->ip, nexthop->ifindex)) - continue; - listnode_add (old, nexthop); - nexthop->lock++; - } -} - -#ifdef HAVE_IPV6 -static void -isis_nexthops6_merge (struct list *new, struct list *old) -{ - struct listnode *node; - struct isis_nexthop6 *nexthop6; - - for (ALL_LIST_ELEMENTS_RO (new, node, nexthop6)) - { - if (nexthop6lookup (old, &nexthop6->ip6, nexthop6->ifindex)) - continue; - listnode_add (old, nexthop6); - nexthop6->lock++; - } -} -#endif /* HAVE_IPV6 */ - -static void -isis_route_info_merge (struct isis_route_info *new, - struct isis_route_info *old, u_char family) -{ - if (family == AF_INET) - isis_nexthops_merge (new->nexthops, old->nexthops); -#ifdef HAVE_IPV6 - else if (family == AF_INET6) - isis_nexthops6_merge (new->nexthops6, old->nexthops6); -#endif /* HAVE_IPV6 */ - - return; -} - -static int -isis_route_info_prefer_new (struct isis_route_info *new, - struct isis_route_info *old) -{ - if (!CHECK_FLAG (old->flag, ISIS_ROUTE_FLAG_ACTIVE)) - return 1; - - if (new->cost < old->cost) - return 1; - - return 0; -} - struct isis_route_info * isis_route_create (struct prefix *prefix, u_int32_t cost, u_int32_t depth, struct list *adjacencies, struct isis_area *area, @@ -459,13 +410,7 @@ isis_route_create (struct prefix *prefix, u_int32_t cost, u_int32_t depth, /* for debugs */ prefix2str (prefix, (char *) buff, BUFSIZ); - rinfo_new = isis_route_info_new (cost, depth, family, adjacencies); - if (!rinfo_new) - { - zlog_err ("ISIS-Rte (%s): isis_route_create: out of memory!", - area->area_tag); - return NULL; - } + rinfo_new = isis_route_info_new (prefix, cost, depth, adjacencies); if (family == AF_INET) route_node = route_node_get (area->route_table[level - 1], prefix); @@ -474,73 +419,41 @@ 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; - rinfo_old = route_node->info; - if (!rinfo_old) - { - if (isis->debugs & DEBUG_RTE_EVENTS) - zlog_debug ("ISIS-Rte (%s) route created: %s", area->area_tag, buff); - SET_FLAG (rinfo_new->flag, ISIS_ROUTE_FLAG_ACTIVE); - route_node->info = rinfo_new; - return rinfo_new; - } - - if (isis->debugs & DEBUG_RTE_EVENTS) - zlog_debug ("ISIS-Rte (%s) route already exists: %s", area->area_tag, - buff); - - if (isis_route_info_same (rinfo_new, rinfo_old, family)) { - if (isis->debugs & DEBUG_RTE_EVENTS) - zlog_debug ("ISIS-Rte (%s) route unchanged: %s", area->area_tag, buff); isis_route_info_delete (rinfo_new); - route_info = rinfo_old; + return NULL; } - else if (isis_route_info_same_attrib (rinfo_new, rinfo_old)) + + rinfo_old = route_node->info; + if (!rinfo_old) { - /* merge the nexthop lists */ if (isis->debugs & DEBUG_RTE_EVENTS) - zlog_debug ("ISIS-Rte (%s) route changed (same attribs): %s", - area->area_tag, buff); -#ifdef EXTREME_DEBUG - if (family == AF_INET) - { - zlog_debug ("Old nexthops"); - nexthops_print (rinfo_old->nexthops); - zlog_debug ("New nexthops"); - nexthops_print (rinfo_new->nexthops); - } - else if (family == AF_INET6) - { - zlog_debug ("Old nexthops"); - nexthops6_print (rinfo_old->nexthops6); - zlog_debug ("New nexthops"); - nexthops6_print (rinfo_new->nexthops6); - } -#endif /* EXTREME_DEBUG */ - isis_route_info_merge (rinfo_new, rinfo_old, family); - isis_route_info_delete (rinfo_new); - route_info = rinfo_old; - UNSET_FLAG (route_info->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNC); + zlog_debug ("ISIS-Rte (%s) route created: %s", area->area_tag, buff); + route_info = rinfo_new; + UNSET_FLAG (route_info->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNCED); } else { - if (isis_route_info_prefer_new (rinfo_new, rinfo_old)) - { - if (isis->debugs & DEBUG_RTE_EVENTS) - zlog_debug ("ISIS-Rte (%s) route changed: %s", area->area_tag, - buff); - isis_route_info_delete (rinfo_old); - route_info = rinfo_new; - } + if (isis->debugs & DEBUG_RTE_EVENTS) + zlog_debug ("ISIS-Rte (%s) route already exists: %s", area->area_tag, + buff); + if (isis_route_info_same (rinfo_new, rinfo_old, family)) + { + if (isis->debugs & DEBUG_RTE_EVENTS) + zlog_debug ("ISIS-Rte (%s) route unchanged: %s", area->area_tag, + buff); + isis_route_info_delete (rinfo_new); + route_info = rinfo_old; + } else - { - if (isis->debugs & DEBUG_RTE_EVENTS) - zlog_debug ("ISIS-Rte (%s) route rejected: %s", area->area_tag, - buff); - isis_route_info_delete (rinfo_new); - route_info = rinfo_old; - } + { + if (isis->debugs & DEBUG_RTE_EVENTS) + zlog_debug ("ISIS-Rte (%s) route changed: %s", area->area_tag, + buff); + isis_route_info_delete (rinfo_old); + route_info = rinfo_new; + UNSET_FLAG (route_info->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNCED); + } } SET_FLAG (route_info->flag, ISIS_ROUTE_FLAG_ACTIVE); @@ -570,7 +483,7 @@ isis_route_delete (struct prefix *prefix, struct route_table *table) return; } - if (CHECK_FLAG (rinfo->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNC)) + if (CHECK_FLAG (rinfo->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNCED)) { UNSET_FLAG (rinfo->flag, ISIS_ROUTE_FLAG_ACTIVE); if (isis->debugs & DEBUG_RTE_EVENTS) @@ -600,10 +513,12 @@ isis_route_validate_table (struct isis_area *area, struct route_table *table) if (isis->debugs & DEBUG_RTE_EVENTS) { prefix2str (&rnode->p, (char *) buff, BUFSIZ); - zlog_debug ("ISIS-Rte (%s): route validate: %s %s %s", + zlog_debug ("ISIS-Rte (%s): route validate: %s %s %s %s", area->area_tag, - (CHECK_FLAG (rinfo->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNC) ? - "sync'ed" : "nosync"), + (CHECK_FLAG (rinfo->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNCED) ? + "synced" : "not-synced"), + (CHECK_FLAG (rinfo->flag, ISIS_ROUTE_FLAG_ZEBRA_RESYNC) ? + "resync" : "not-resync"), (CHECK_FLAG (rinfo->flag, ISIS_ROUTE_FLAG_ACTIVE) ? "active" : "inactive"), buff); } @@ -706,41 +621,58 @@ isis_route_validate_merge (struct isis_area *area, int family) /* Walk through route tables and propagate necessary changes into RIB. In case * of L1L2 area, level tables have to be merged at first. */ -int -isis_route_validate (struct thread *thread) +void +isis_route_validate (struct isis_area *area) { - struct isis_area *area; - - area = THREAD_ARG (thread); + struct listnode *node; + struct isis_circuit *circuit; if (area->is_type == IS_LEVEL_1) - { - isis_route_validate_table (area, area->route_table[0]); - goto validate_ipv6; - } - if (area->is_type == IS_LEVEL_2) - { - isis_route_validate_table (area, area->route_table[1]); - goto validate_ipv6; - } - - isis_route_validate_merge (area, AF_INET); + isis_route_validate_table (area, area->route_table[0]); + else if (area->is_type == IS_LEVEL_2) + isis_route_validate_table (area, area->route_table[1]); + else + isis_route_validate_merge (area, AF_INET); -validate_ipv6: #ifdef HAVE_IPV6 if (area->is_type == IS_LEVEL_1) + isis_route_validate_table (area, area->route_table6[0]); + else if (area->is_type == IS_LEVEL_2) + isis_route_validate_table (area, area->route_table6[1]); + else + isis_route_validate_merge (area, AF_INET6); +#endif + + if (!area->circuit_list) { + return; + } + /* walk all circuits and reset any spf specific flags */ + for (ALL_LIST_ELEMENTS_RO (area->circuit_list, node, circuit)) + UNSET_FLAG(circuit->flags, ISIS_CIRCUIT_FLAPPED_AFTER_SPF); + + return; +} + +void +isis_route_invalidate_table (struct isis_area *area, struct route_table *table) +{ + struct route_node *rode; + struct isis_route_info *rinfo; + for (rode = route_top (table); rode; rode = route_next (rode)) { - isis_route_validate_table (area, area->route_table6[0]); - return ISIS_OK; - } - if (area->is_type == IS_LEVEL_2) - { - isis_route_validate_table (area, area->route_table6[1]); - return ISIS_OK; - } + if (rode->info == NULL) + continue; + rinfo = rode->info; - isis_route_validate_merge (area, AF_INET6); -#endif + UNSET_FLAG (rinfo->flag, ISIS_ROUTE_FLAG_ACTIVE); + } +} - return ISIS_OK; +void +isis_route_invalidate (struct isis_area *area) +{ + if (area->is_type & IS_LEVEL_1) + isis_route_invalidate_table (area, area->route_table[0]); + if (area->is_type & IS_LEVEL_2) + isis_route_invalidate_table (area, area->route_table[1]); } diff --git a/isisd/isis_route.h b/isisd/isis_route.h index 4eac79b86..0d2379cbe 100644 --- a/isisd/isis_route.h +++ b/isisd/isis_route.h @@ -28,23 +28,26 @@ #ifdef HAVE_IPV6 struct isis_nexthop6 { - unsigned int ifindex; + ifindex_t ifindex; struct in6_addr ip6; + struct in6_addr router_address6; unsigned int lock; }; #endif /* HAVE_IPV6 */ struct isis_nexthop { - unsigned int ifindex; + ifindex_t ifindex; struct in_addr ip; + struct in_addr router_address; unsigned int lock; }; struct isis_route_info { -#define ISIS_ROUTE_FLAG_ZEBRA_SYNC 0x01 -#define ISIS_ROUTE_FLAG_ACTIVE 0x02 +#define ISIS_ROUTE_FLAG_ACTIVE 0x01 /* active route for the prefix */ +#define ISIS_ROUTE_FLAG_ZEBRA_SYNCED 0x02 /* set when route synced to zebra */ +#define ISIS_ROUTE_FLAG_ZEBRA_RESYNC 0x04 /* set when route needs to sync */ u_char flag; u_int32_t cost; u_int32_t depth; @@ -59,6 +62,9 @@ struct isis_route_info *isis_route_create (struct prefix *prefix, struct list *adjacencies, struct isis_area *area, int level); -int isis_route_validate (struct thread *thread); +void isis_route_validate (struct isis_area *area); +void isis_route_invalidate_table (struct isis_area *area, + struct route_table *table); +void isis_route_invalidate (struct isis_area *area); #endif /* _ZEBRA_ISIS_ROUTE_H */ diff --git a/isisd/isis_routemap.c b/isisd/isis_routemap.c index cff0fa3ff..3443a0a59 100644 --- a/isisd/isis_routemap.c +++ b/isisd/isis_routemap.c @@ -1,13 +1,10 @@ /* - * IS-IS Rout(e)ing protocol - isis_routemap.c - * - * Copyright (C) 2001,2002 Sampo Saaristo - * Tampere University of Technology - * Institute of Communications Engineering + * IS-IS Rout(e)ing protocol - isis_routemap.c * + * Copyright (C) 2013-2015 Christian Franke * * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public Licenseas published by the Free + * 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. * @@ -20,21 +17,26 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + #include -#include "thread.h" +#include "command.h" +#include "filter.h" +#include "hash.h" +#include "if.h" #include "linklist.h" -#include "vty.h" #include "log.h" #include "memory.h" #include "prefix.h" -#include "hash.h" -#include "if.h" -#include "table.h" +#include "plist.h" #include "routemap.h" +#include "table.h" +#include "thread.h" +#include "vty.h" #include "isis_constants.h" #include "isis_common.h" +#include "isis_flags.h" #include "dict.h" #include "isisd.h" #include "isis_misc.h" @@ -46,61 +48,518 @@ #include "isis_spf.h" #include "isis_route.h" #include "isis_zebra.h" +#include "isis_routemap.h" -extern struct isis *isis; +static route_map_result_t +route_match_ip_address(void *rule, struct prefix *prefix, + route_map_object_t type, void *object) +{ + struct access_list *alist; -/* - * Prototypes. - */ -void isis_route_map_upd(const char *); -void isis_route_map_event(route_map_event_t, const char *); -void isis_route_map_init(void); + if (type != RMAP_ISIS) + return RMAP_NOMATCH; + alist = access_list_lookup(AFI_IP, (char*)rule); + if (access_list_apply(alist, prefix) != FILTER_DENY) + return RMAP_MATCH; -void -isis_route_map_upd (const char *name) + return RMAP_NOMATCH; +} + +static void * +route_match_ip_address_compile(const char *arg) +{ + return XSTRDUP(MTYPE_ROUTE_MAP_COMPILED, arg); +} + +static void +route_match_ip_address_free(void *rule) +{ + XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); +} + +static struct route_map_rule_cmd route_match_ip_address_cmd = +{ + "ip address", + route_match_ip_address, + route_match_ip_address_compile, + route_match_ip_address_free +}; + +/* ------------------------------------------------------------*/ + +static route_map_result_t +route_match_ip_address_prefix_list(void *rule, struct prefix *prefix, + route_map_object_t type, void *object) +{ + struct prefix_list *plist; + + if (type != RMAP_ISIS) + return RMAP_NOMATCH; + + plist = prefix_list_lookup(AFI_IP, (char*)rule); + if (prefix_list_apply(plist, prefix) != PREFIX_DENY) + return RMAP_MATCH; + + return RMAP_NOMATCH; +} + +static void * +route_match_ip_address_prefix_list_compile(const char *arg) +{ + return XSTRDUP (MTYPE_ROUTE_MAP_COMPILED, arg); +} + +static void +route_match_ip_address_prefix_list_free (void *rule) +{ + XFREE (MTYPE_ROUTE_MAP_COMPILED, rule); +} + +struct route_map_rule_cmd route_match_ip_address_prefix_list_cmd = +{ + "ip address prefix-list", + route_match_ip_address_prefix_list, + route_match_ip_address_prefix_list_compile, + route_match_ip_address_prefix_list_free +}; + +/* ------------------------------------------------------------*/ + +static route_map_result_t +route_match_ipv6_address(void *rule, struct prefix *prefix, + route_map_object_t type, void *object) { - int i = 0; + struct access_list *alist; - if (!isis) - return; + if (type != RMAP_ISIS) + return RMAP_NOMATCH; + + alist = access_list_lookup(AFI_IP6, (char*)rule); + if (access_list_apply(alist, prefix) != FILTER_DENY) + return RMAP_MATCH; + + return RMAP_NOMATCH; +} + +static void * +route_match_ipv6_address_compile(const char *arg) +{ + return XSTRDUP(MTYPE_ROUTE_MAP_COMPILED, arg); +} + +static void +route_match_ipv6_address_free(void *rule) +{ + XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); +} + +static struct route_map_rule_cmd route_match_ipv6_address_cmd = +{ + "ipv6 address", + route_match_ipv6_address, + route_match_ipv6_address_compile, + route_match_ipv6_address_free +}; + +/* ------------------------------------------------------------*/ + +static route_map_result_t +route_match_ipv6_address_prefix_list(void *rule, struct prefix *prefix, + route_map_object_t type, void *object) +{ + struct prefix_list *plist; - for (i = 0; i <= ZEBRA_ROUTE_MAX; i++) + if (type != RMAP_ISIS) + return RMAP_NOMATCH; + + plist = prefix_list_lookup(AFI_IP6, (char*)rule); + if (prefix_list_apply(plist, prefix) != PREFIX_DENY) + return RMAP_MATCH; + + return RMAP_NOMATCH; +} + +static void * +route_match_ipv6_address_prefix_list_compile(const char *arg) +{ + return XSTRDUP (MTYPE_ROUTE_MAP_COMPILED, arg); +} + +static void +route_match_ipv6_address_prefix_list_free (void *rule) +{ + XFREE (MTYPE_ROUTE_MAP_COMPILED, rule); +} + +struct route_map_rule_cmd route_match_ipv6_address_prefix_list_cmd = +{ + "ipv6 address prefix-list", + route_match_ipv6_address_prefix_list, + route_match_ipv6_address_prefix_list_compile, + route_match_ipv6_address_prefix_list_free +}; + +/* ------------------------------------------------------------*/ + +static route_map_result_t +route_set_metric(void *rule, struct prefix *prefix, + route_map_object_t type, void *object) +{ + uint32_t *metric; + struct isis_ext_info *info; + + if (type == RMAP_ISIS) { - if (isis->rmap[i].name) - isis->rmap[i].map = isis->rmap[i].map = - route_map_lookup_by_name (isis->rmap[i].name); - else - isis->rmap[i].map = NULL; + metric = rule; + info = object; + + info->metric = *metric; } - /* FIXME: do the address family sub-mode AF_INET6 here ? */ + return RMAP_OKAY; } -void -isis_route_map_event (route_map_event_t event, const char *name) +static void * +route_set_metric_compile(const char *arg) +{ + unsigned long metric; + char *endp; + uint32_t *ret; + + metric = strtoul(arg, &endp, 10); + if (arg[0] == '\0' || *endp != '\0' || metric > MAX_WIDE_PATH_METRIC) + return NULL; + + ret = XCALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(*ret)); + *ret = metric; + + return ret; +} + +static void +route_set_metric_free(void *rule) { - int type; + XFREE(MTYPE_ROUTE_MAP_COMPILED, rule); +} + +static struct route_map_rule_cmd route_set_metric_cmd = +{ + "metric", + route_set_metric, + route_set_metric_compile, + route_set_metric_free +}; - if (!isis) - return; +/* ------------------------------------------------------------*/ + +static int +isis_route_match_add(struct vty *vty, struct route_map_index *index, + const char *command, const char *arg) +{ + int ret; - for (type = 0; type <= ZEBRA_ROUTE_MAX; type++) + ret = route_map_add_match (index, command, arg); + if (ret) { - if (isis->rmap[type].name && isis->rmap[type].map && - !strcmp (isis->rmap[type].name, name)) - { - isis_distribute_list_update (type); - } + switch (ret) + { + case RMAP_RULE_MISSING: + vty_out (vty, "%% Can't find rule.%s", VTY_NEWLINE); + return CMD_WARNING; + case RMAP_COMPILE_ERROR: + vty_out (vty, "%% Argument is malformed.%s", VTY_NEWLINE); + return CMD_WARNING; + } } + return CMD_SUCCESS; +} + +static int +isis_route_match_delete(struct vty *vty, struct route_map_index *index, + const char *command, const char *arg) +{ + int ret; + + ret = route_map_delete_match (index, command, arg); + if (ret) + { + switch (ret) + { + case RMAP_RULE_MISSING: + vty_out (vty, "%% Can't find rule.%s", VTY_NEWLINE); + return CMD_WARNING; + case RMAP_COMPILE_ERROR: + vty_out (vty, "%% Argument is malformed.%s", VTY_NEWLINE); + return CMD_WARNING; + } + } + return CMD_SUCCESS; +} + +static int +isis_route_set_add(struct vty *vty, struct route_map_index *index, + const char *command, const char *arg) +{ + int ret; + + ret = route_map_add_set(index, command, arg); + if (ret) + { + switch (ret) + { + case RMAP_RULE_MISSING: + vty_out (vty, "%% Can't find rule.%s", VTY_NEWLINE); + return CMD_WARNING; + case RMAP_COMPILE_ERROR: + vty_out (vty, "%% Argument is malformed.%s", VTY_NEWLINE); + return CMD_WARNING; + } + } + + return CMD_SUCCESS; +} + +static int +isis_route_set_delete (struct vty *vty, struct route_map_index *index, + const char *command, const char *arg) +{ + int ret; + + ret = route_map_delete_set (index, command, arg); + if (ret) + { + switch (ret) + { + case RMAP_RULE_MISSING: + vty_out (vty, "%% Can't find rule.%s", VTY_NEWLINE); + return CMD_WARNING; + case RMAP_COMPILE_ERROR: + vty_out (vty, "%% Argument is malformed.%s", VTY_NEWLINE); + return CMD_WARNING; + } + } + + return CMD_SUCCESS; +} + +/* ------------------------------------------------------------*/ + +DEFUN(match_ip_address, + match_ip_address_cmd, + "match ip address (<1-199>|<1300-2699>|WORD)", + MATCH_STR + IP_STR + "Match address of route\n" + "IP access-list number\n" + "IP access-list number (expanded range)\n" + "IP Access-list name\n") +{ + return isis_route_match_add(vty, vty->index, "ip address", argv[0]); +} + +DEFUN(no_match_ip_address, + no_match_ip_address_val_cmd, + "no match ip address (<1-199>|<1300-2699>|WORD)", + NO_STR + MATCH_STR + IP_STR + "Match address of route\n" + "IP access-list number\n" + "IP access-list number (expanded range)\n" + "IP Access-list name\n") +{ + if (argc == 0) + return isis_route_match_delete(vty, vty->index, "ip address", NULL); + return isis_route_match_delete(vty, vty->index, "ip address", argv[0]); } +ALIAS(no_match_ip_address, + no_match_ip_address_cmd, + "no match ip address", + NO_STR + MATCH_STR + IP_STR + "Match address of route\n" +); + +/* ------------------------------------------------------------*/ + +DEFUN(match_ip_address_prefix_list, + match_ip_address_prefix_list_cmd, + "match ip address prefix-list WORD", + MATCH_STR + IP_STR + "Match address of route\n" + "Match entries of prefix-lists\n" + "IP prefix-list name\n") +{ + return isis_route_match_add(vty, vty->index, "ip address prefix-list", argv[0]); +} + +DEFUN(no_match_ip_address_prefix_list, + no_match_ip_address_prefix_list_cmd, + "no match ip address prefix-list", + NO_STR + MATCH_STR + IP_STR + "Match address of route\n" + "Match entries of prefix-lists\n") +{ + if (argc == 0) + return isis_route_match_delete (vty, vty->index, "ip address prefix-list", NULL); + return isis_route_match_delete (vty, vty->index, "ip address prefix-list", argv[0]); +} + +ALIAS(no_match_ip_address_prefix_list, + no_match_ip_address_prefix_list_val_cmd, + "no match ip address prefix-list WORD", + NO_STR + MATCH_STR + IP_STR + "Match address of route\n" + "Match entries of prefix-lists\n" + "IP prefix-list name\n" +); + +/* ------------------------------------------------------------*/ + +DEFUN(match_ipv6_address, + match_ipv6_address_cmd, + "match ipv6 address WORD", + MATCH_STR + IPV6_STR + "Match IPv6 address of route\n" + "IPv6 access-list name\n") +{ + return isis_route_match_add(vty, vty->index, "ipv6 address", argv[0]); +} + +DEFUN(no_match_ipv6_address, + no_match_ipv6_address_val_cmd, + "no match ipv6 address WORD", + NO_STR + MATCH_STR + IPV6_STR + "Match IPv6 address of route\n" + "IPv6 access-list name\n") +{ + if (argc == 0) + return isis_route_match_delete(vty, vty->index, "ipv6 address", NULL); + return isis_route_match_delete(vty, vty->index, "ipv6 address", argv[0]); +} + +ALIAS(no_match_ipv6_address, + no_match_ipv6_address_cmd, + "no match ipv6 address", + NO_STR + MATCH_STR + IPV6_STR + "Match IPv6 address of route\n" +); + +/* ------------------------------------------------------------*/ + +DEFUN(match_ipv6_address_prefix_list, + match_ipv6_address_prefix_list_cmd, + "match ipv6 address prefix-list WORD", + MATCH_STR + IPV6_STR + "Match address of route\n" + "Match entries of prefix-lists\n" + "IP prefix-list name\n") +{ + return isis_route_match_add(vty, vty->index, "ipv6 address prefix-list", argv[0]); +} + +DEFUN(no_match_ipv6_address_prefix_list, + no_match_ipv6_address_prefix_list_cmd, + "no match ipv6 address prefix-list", + NO_STR + MATCH_STR + IPV6_STR + "Match address of route\n" + "Match entries of prefix-lists\n") +{ + if (argc == 0) + return isis_route_match_delete (vty, vty->index, "ipv6 address prefix-list", NULL); + return isis_route_match_delete (vty, vty->index, "ipv6 address prefix-list", argv[0]); +} + +ALIAS(no_match_ipv6_address_prefix_list, + no_match_ipv6_address_prefix_list_val_cmd, + "no match ipv6 address prefix-list WORD", + NO_STR + MATCH_STR + IPV6_STR + "Match address of route\n" + "Match entries of prefix-lists\n" + "IP prefix-list name\n" +); + +/* ------------------------------------------------------------*/ + +/* set metric already exists e.g. in the ospf routemap. vtysh doesn't cope well with different + * commands at the same node, therefore add set metric with the same 32-bit range as ospf and + * verify that the input is a valid isis metric */ +DEFUN(set_metric, + set_metric_cmd, + "set metric <0-4294967295>", + SET_STR + "Metric vale for destination routing protocol\n" + "Metric value\n") +{ + return isis_route_set_add(vty, vty->index, "metric", argv[0]); +} + +DEFUN(no_set_metric, + no_set_metric_val_cmd, + "no set metric <0-4294967295>", + NO_STR + SET_STR + "Metric value for destination routing protocol\n" + "Metric value\n") +{ + if (argc == 0) + return isis_route_set_delete(vty, vty->index, "metric", NULL); + return isis_route_set_delete(vty, vty->index, "metric", argv[0]); +} + +ALIAS(no_set_metric, + no_set_metric_cmd, + "no set metric", + NO_STR + SET_STR + "Metric vale for destination routing protocol\n" +); + void -isis_route_map_init (void) +isis_route_map_init(void) { - route_map_init (); - route_map_init_vty (); + route_map_init(); + route_map_init_vty(); + + route_map_install_match(&route_match_ip_address_cmd); + install_element(RMAP_NODE, &match_ip_address_cmd); + install_element(RMAP_NODE, &no_match_ip_address_val_cmd); + install_element(RMAP_NODE, &no_match_ip_address_cmd); + + route_map_install_match(&route_match_ip_address_prefix_list_cmd); + install_element(RMAP_NODE, &match_ip_address_prefix_list_cmd); + install_element(RMAP_NODE, &no_match_ip_address_prefix_list_val_cmd); + install_element(RMAP_NODE, &no_match_ip_address_prefix_list_cmd); + + route_map_install_match(&route_match_ipv6_address_cmd); + install_element(RMAP_NODE, &match_ipv6_address_cmd); + install_element(RMAP_NODE, &no_match_ipv6_address_val_cmd); + install_element(RMAP_NODE, &no_match_ipv6_address_cmd); + + route_map_install_match(&route_match_ipv6_address_prefix_list_cmd); + install_element(RMAP_NODE, &match_ipv6_address_prefix_list_cmd); + install_element(RMAP_NODE, &no_match_ipv6_address_prefix_list_val_cmd); + install_element(RMAP_NODE, &no_match_ipv6_address_prefix_list_cmd); - route_map_add_hook (isis_route_map_upd); - route_map_delete_hook (isis_route_map_upd); - route_map_event_hook (isis_route_map_event); + route_map_install_set(&route_set_metric_cmd); + install_element(RMAP_NODE, &set_metric_cmd); + install_element(RMAP_NODE, &no_set_metric_val_cmd); + install_element(RMAP_NODE, &no_set_metric_cmd); } diff --git a/isisd/isis_routemap.h b/isisd/isis_routemap.h new file mode 100644 index 000000000..1cb063fee --- /dev/null +++ b/isisd/isis_routemap.h @@ -0,0 +1,25 @@ +/* + * IS-IS Rout(e)ing protocol - isis_routemap.h + * + * Copyright (C) 2013-2015 Christian Franke + * + * 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. + */ +#ifndef ISIS_ROUTEMAP_H +#define ISIS_ROUTEMAP_H + +void isis_route_map_init(void); + +#endif diff --git a/isisd/isis_spf.c b/isisd/isis_spf.c index 5d0b161f4..6b2456f9f 100644 --- a/isisd/isis_spf.c +++ b/isisd/isis_spf.c @@ -36,6 +36,7 @@ #include "isis_constants.h" #include "isis_common.h" +#include "isis_flags.h" #include "dict.h" #include "isisd.h" #include "isis_misc.h" @@ -49,10 +50,6 @@ #include "isis_route.h" #include "isis_csm.h" -extern struct isis *isis; -extern struct thread_master *master; -extern struct host host; - int isis_run_spf_l1 (struct thread *thread); int isis_run_spf_l2 (struct thread *thread); @@ -113,7 +110,6 @@ remove_excess_adjs (struct list *adjs) return; } -#ifdef EXTREME_DEBUG static const char * vtype2string (enum vertextype vtype) { @@ -164,12 +160,12 @@ vid2string (struct isis_vertex *vertex, u_char * buff) { case VTYPE_PSEUDO_IS: case VTYPE_PSEUDO_TE_IS: - return rawlspid_print (vertex->N.id); + return print_sys_hostname (vertex->N.id); break; case VTYPE_NONPSEUDO_IS: case VTYPE_NONPSEUDO_TE_IS: case VTYPE_ES: - return sysid_print (vertex->N.id); + return print_sys_hostname (vertex->N.id); break; case VTYPE_IPREACH_INTERNAL: case VTYPE_IPREACH_EXTERNAL: @@ -186,149 +182,260 @@ vid2string (struct isis_vertex *vertex, u_char * buff) return (char *) buff; } -#endif /* EXTREME_DEBUG */ -static struct isis_spftree * -isis_spftree_new () +static struct isis_vertex * +isis_vertex_new (void *id, enum vertextype vtype) { - struct isis_spftree *tree; + struct isis_vertex *vertex; - tree = XCALLOC (MTYPE_ISIS_SPFTREE, sizeof (struct isis_spftree)); - if (tree == NULL) + vertex = XCALLOC (MTYPE_ISIS_VERTEX, sizeof (struct isis_vertex)); + + vertex->type = vtype; + switch (vtype) { - zlog_err ("ISIS-Spf: isis_spftree_new Out of memory!"); - return NULL; + case VTYPE_ES: + case VTYPE_NONPSEUDO_IS: + case VTYPE_NONPSEUDO_TE_IS: + memcpy (vertex->N.id, (u_char *) id, ISIS_SYS_ID_LEN); + break; + case VTYPE_PSEUDO_IS: + case VTYPE_PSEUDO_TE_IS: + memcpy (vertex->N.id, (u_char *) id, ISIS_SYS_ID_LEN + 1); + break; + case VTYPE_IPREACH_INTERNAL: + case VTYPE_IPREACH_EXTERNAL: + case VTYPE_IPREACH_TE: +#ifdef HAVE_IPV6 + case VTYPE_IP6REACH_INTERNAL: + case VTYPE_IP6REACH_EXTERNAL: +#endif /* HAVE_IPV6 */ + memcpy (&vertex->N.prefix, (struct prefix *) id, + sizeof (struct prefix)); + break; + default: + zlog_err ("WTF!"); } - tree->tents = list_new (); - tree->paths = list_new (); - return tree; + vertex->Adj_N = list_new (); + vertex->parents = list_new (); + vertex->children = list_new (); + + return vertex; } static void isis_vertex_del (struct isis_vertex *vertex) { list_delete (vertex->Adj_N); + vertex->Adj_N = NULL; + list_delete (vertex->parents); + vertex->parents = NULL; + list_delete (vertex->children); + vertex->children = NULL; + memset(vertex, 0, sizeof(struct isis_vertex)); XFREE (MTYPE_ISIS_VERTEX, vertex); return; } -#if 0 /* HT: Not used yet. */ static void +isis_vertex_adj_del (struct isis_vertex *vertex, struct isis_adjacency *adj) +{ + struct listnode *node, *nextnode; + if (!vertex) + return; + for (node = listhead (vertex->Adj_N); node; node = nextnode) + { + nextnode = listnextnode(node); + if (listgetdata(node) == adj) + list_delete_node(vertex->Adj_N, node); + } + return; +} + +struct isis_spftree * +isis_spftree_new (struct isis_area *area) +{ + struct isis_spftree *tree; + + tree = XCALLOC (MTYPE_ISIS_SPFTREE, sizeof (struct isis_spftree)); + if (tree == NULL) + { + zlog_err ("ISIS-Spf: isis_spftree_new Out of memory!"); + return NULL; + } + + tree->tents = list_new (); + tree->paths = list_new (); + tree->area = area; + tree->last_run_timestamp = 0; + tree->last_run_duration = 0; + tree->runcount = 0; + tree->pending = 0; + return tree; +} + +void isis_spftree_del (struct isis_spftree *spftree) { + THREAD_TIMER_OFF (spftree->t_spf); + spftree->tents->del = (void (*)(void *)) isis_vertex_del; list_delete (spftree->tents); + spftree->tents = NULL; spftree->paths->del = (void (*)(void *)) isis_vertex_del; list_delete (spftree->paths); + spftree->paths = NULL; XFREE (MTYPE_ISIS_SPFTREE, spftree); return; } -#endif + +void +isis_spftree_adj_del (struct isis_spftree *spftree, struct isis_adjacency *adj) +{ + struct listnode *node; + if (!adj) + return; + for (node = listhead (spftree->tents); node; node = listnextnode (node)) + isis_vertex_adj_del (listgetdata (node), adj); + for (node = listhead (spftree->paths); node; node = listnextnode (node)) + isis_vertex_adj_del (listgetdata (node), adj); + return; +} void spftree_area_init (struct isis_area *area) { - if ((area->is_type & IS_LEVEL_1) && area->spftree[0] == NULL) - { - area->spftree[0] = isis_spftree_new (); + if (area->is_type & IS_LEVEL_1) + { + if (area->spftree[0] == NULL) + area->spftree[0] = isis_spftree_new (area); #ifdef HAVE_IPV6 - area->spftree6[0] = isis_spftree_new (); + if (area->spftree6[0] == NULL) + area->spftree6[0] = isis_spftree_new (area); #endif + } - /* thread_add_timer (master, isis_run_spf_l1, area, - isis_jitter (PERIODIC_SPF_INTERVAL, 10)); */ - } - - if ((area->is_type & IS_LEVEL_2) && area->spftree[1] == NULL) - { - area->spftree[1] = isis_spftree_new (); + if (area->is_type & IS_LEVEL_2) + { + if (area->spftree[1] == NULL) + area->spftree[1] = isis_spftree_new (area); #ifdef HAVE_IPV6 - area->spftree6[1] = isis_spftree_new (); + if (area->spftree6[1] == NULL) + area->spftree6[1] = isis_spftree_new (area); #endif - /* thread_add_timer (master, isis_run_spf_l2, area, - isis_jitter (PERIODIC_SPF_INTERVAL, 10)); */ - } + } return; } -static struct isis_vertex * -isis_vertex_new (void *id, enum vertextype vtype) +void +spftree_area_del (struct isis_area *area) { - struct isis_vertex *vertex; - - vertex = XCALLOC (MTYPE_ISIS_VERTEX, sizeof (struct isis_vertex)); - if (vertex == NULL) + if (area->is_type & IS_LEVEL_1) + { + if (area->spftree[0] != NULL) { - zlog_err ("isis_vertex_new Out of memory!"); - return NULL; + isis_spftree_del (area->spftree[0]); + area->spftree[0] = NULL; + } +#ifdef HAVE_IPV6 + if (area->spftree6[0]) + { + isis_spftree_del (area->spftree6[0]); + area->spftree6[0] = NULL; } +#endif + } - vertex->type = vtype; - switch (vtype) + if (area->is_type & IS_LEVEL_2) + { + if (area->spftree[1] != NULL) { - case VTYPE_ES: - case VTYPE_NONPSEUDO_IS: - case VTYPE_NONPSEUDO_TE_IS: - memcpy (vertex->N.id, (u_char *) id, ISIS_SYS_ID_LEN); - break; - case VTYPE_PSEUDO_IS: - case VTYPE_PSEUDO_TE_IS: - memcpy (vertex->N.id, (u_char *) id, ISIS_SYS_ID_LEN + 1); - break; - case VTYPE_IPREACH_INTERNAL: - case VTYPE_IPREACH_EXTERNAL: - case VTYPE_IPREACH_TE: + isis_spftree_del (area->spftree[1]); + area->spftree[1] = NULL; + } #ifdef HAVE_IPV6 - case VTYPE_IP6REACH_INTERNAL: - case VTYPE_IP6REACH_EXTERNAL: -#endif /* HAVE_IPV6 */ - memcpy (&vertex->N.prefix, (struct prefix *) id, - sizeof (struct prefix)); - break; - default: - zlog_err ("WTF!"); + if (area->spftree6[1] != NULL) + { + isis_spftree_del (area->spftree6[1]); + area->spftree6[1] = NULL; } +#endif + } - vertex->Adj_N = list_new (); + return; +} - return vertex; +void +spftree_area_adj_del (struct isis_area *area, struct isis_adjacency *adj) +{ + if (area->is_type & IS_LEVEL_1) + { + if (area->spftree[0] != NULL) + isis_spftree_adj_del (area->spftree[0], adj); +#ifdef HAVE_IPV6 + if (area->spftree6[0] != NULL) + isis_spftree_adj_del (area->spftree6[0], adj); +#endif + } + + if (area->is_type & IS_LEVEL_2) + { + if (area->spftree[1] != NULL) + isis_spftree_adj_del (area->spftree[1], adj); +#ifdef HAVE_IPV6 + if (area->spftree6[1] != NULL) + isis_spftree_adj_del (area->spftree6[1], adj); +#endif + } + + return; +} + +/* + * Find the system LSP: returns the LSP in our LSP database + * associated with the given system ID. + */ +static struct isis_lsp * +isis_root_system_lsp (struct isis_area *area, int level, u_char *sysid) +{ + struct isis_lsp *lsp; + u_char lspid[ISIS_SYS_ID_LEN + 2]; + + memcpy (lspid, sysid, ISIS_SYS_ID_LEN); + LSP_PSEUDO_ID (lspid) = 0; + LSP_FRAGMENT (lspid) = 0; + lsp = lsp_search (lspid, area->lspdb[level - 1]); + if (lsp && lsp->lsp_header->rem_lifetime != 0) + return lsp; + return NULL; } /* * Add this IS to the root of SPT */ -static void -isis_spf_add_self (struct isis_spftree *spftree, struct isis_area *area, - int level) +static struct isis_vertex * +isis_spf_add_root (struct isis_spftree *spftree, int level, u_char *sysid) { struct isis_vertex *vertex; struct isis_lsp *lsp; - u_char lspid[ISIS_SYS_ID_LEN + 2]; #ifdef EXTREME_DEBUG u_char buff[BUFSIZ]; #endif /* EXTREME_DEBUG */ - memcpy (lspid, isis->sysid, ISIS_SYS_ID_LEN); - LSP_PSEUDO_ID (lspid) = 0; - LSP_FRAGMENT (lspid) = 0; - - lsp = lsp_search (lspid, area->lspdb[level - 1]); + lsp = isis_root_system_lsp (spftree->area, level, sysid); if (lsp == NULL) zlog_warn ("ISIS-Spf: could not find own l%d LSP!", level); - if (!area->oldmetric) - vertex = isis_vertex_new (isis->sysid, VTYPE_NONPSEUDO_TE_IS); + if (!spftree->area->oldmetric) + vertex = isis_vertex_new (sysid, VTYPE_NONPSEUDO_TE_IS); else - vertex = isis_vertex_new (isis->sysid, VTYPE_NONPSEUDO_IS); - - vertex->lsp = lsp; + vertex = isis_vertex_new (sysid, VTYPE_NONPSEUDO_IS); listnode_add (spftree->paths, vertex); @@ -338,7 +445,7 @@ isis_spf_add_self (struct isis_spftree *spftree, struct isis_area *area, vertex->depth, vertex->d_N); #endif /* EXTREME_DEBUG */ - return; + return vertex; } static struct isis_vertex * @@ -390,26 +497,40 @@ isis_find_vertex (struct list *list, void *id, enum vertextype vtype) */ static struct isis_vertex * isis_spf_add2tent (struct isis_spftree *spftree, enum vertextype vtype, - void *id, struct isis_adjacency *adj, u_int32_t cost, - int depth, int family) + void *id, uint32_t cost, int depth, int family, + struct isis_adjacency *adj, struct isis_vertex *parent) { struct isis_vertex *vertex, *v; struct listnode *node; + struct isis_adjacency *parent_adj; #ifdef EXTREME_DEBUG u_char buff[BUFSIZ]; #endif + assert (isis_find_vertex (spftree->paths, id, vtype) == NULL); + assert (isis_find_vertex (spftree->tents, id, vtype) == NULL); vertex = isis_vertex_new (id, vtype); vertex->d_N = cost; vertex->depth = depth; - if (adj) + if (parent) { + listnode_add (vertex->parents, parent); + if (listnode_lookup (parent->children, vertex) == NULL) + listnode_add (parent->children, vertex); + } + + if (parent && parent->Adj_N && listcount(parent->Adj_N) > 0) { + for (ALL_LIST_ELEMENTS_RO (parent->Adj_N, node, parent_adj)) + listnode_add (vertex->Adj_N, parent_adj); + } else if (adj) { listnode_add (vertex->Adj_N, adj); + } #ifdef EXTREME_DEBUG - zlog_debug ("ISIS-Spf: add to TENT %s %s depth %d dist %d", + zlog_debug ("ISIS-Spf: add to TENT %s %s %s depth %d dist %d adjcount %d", + print_sys_hostname (vertex->N.id), vtype2string (vertex->type), vid2string (vertex, buff), - vertex->depth, vertex->d_N); + vertex->depth, vertex->d_N, listcount(vertex->Adj_N)); #endif /* EXTREME_DEBUG */ if (list_isempty (spftree->tents)) @@ -417,8 +538,8 @@ isis_spf_add2tent (struct isis_spftree *spftree, enum vertextype vtype, listnode_add (spftree->tents, vertex); return vertex; } - - /* XXX: This cant use the standard ALL_LIST_ELEMENT macro */ + + /* XXX: This cant use the standard ALL_LIST_ELEMENTS macro */ for (node = listhead (spftree->tents); node; node = listnextnode (node)) { v = listgetdata (node); @@ -427,35 +548,24 @@ isis_spf_add2tent (struct isis_spftree *spftree, enum vertextype vtype, list_add_node_prev (spftree->tents, node, vertex); break; } - else if (v->d_N == vertex->d_N) + else if (v->d_N == vertex->d_N && v->type > vertex->type) { /* Tie break, add according to type */ - while (v && v->d_N == vertex->d_N && v->type > vertex->type) - { - if (v->type > vertex->type) - { - break; - } - /* XXX: this seems dubious, node is the loop iterator */ - node = listnextnode (node); - (node) ? (v = listgetdata (node)) : (v = NULL); - } - list_add_node_prev (spftree->tents, node, vertex); - break; - } - else if (node->next == NULL) - { - list_add_node_next (spftree->tents, node, vertex); + list_add_node_prev (spftree->tents, node, vertex); break; } } + + if (node == NULL) + listnode_add (spftree->tents, vertex); + return vertex; } -static struct isis_vertex * +static void isis_spf_add_local (struct isis_spftree *spftree, enum vertextype vtype, - void *id, struct isis_adjacency *adj, u_int32_t cost, - int family) + void *id, struct isis_adjacency *adj, uint32_t cost, + int family, struct isis_vertex *parent) { struct isis_vertex *vertex; @@ -471,40 +581,65 @@ isis_spf_add_local (struct isis_spftree *spftree, enum vertextype vtype, /* d) */ if (listcount (vertex->Adj_N) > ISIS_MAX_PATH_SPLITS) remove_excess_adjs (vertex->Adj_N); + if (parent && (listnode_lookup (vertex->parents, parent) == NULL)) + listnode_add (vertex->parents, parent); + if (parent && (listnode_lookup (parent->children, vertex) == NULL)) + listnode_add (parent->children, vertex); + return; } - /* f) */ - else if (vertex->d_N > cost) + else if (vertex->d_N < cost) { - listnode_delete (spftree->tents, vertex); - goto add2tent; + /* e) do nothing */ + return; } - /* e) do nothing */ - return vertex; + else { /* vertex->d_N > cost */ + /* f) */ + struct listnode *pnode, *pnextnode; + struct isis_vertex *pvertex; + listnode_delete (spftree->tents, vertex); + assert (listcount (vertex->children) == 0); + for (ALL_LIST_ELEMENTS (vertex->parents, pnode, pnextnode, pvertex)) + listnode_delete(pvertex->children, vertex); + isis_vertex_del (vertex); + } } -add2tent: - return isis_spf_add2tent (spftree, vtype, id, adj, cost, 1, family); + isis_spf_add2tent (spftree, vtype, id, cost, 1, family, adj, parent); + return; } static void process_N (struct isis_spftree *spftree, enum vertextype vtype, void *id, - u_int16_t dist, u_int16_t depth, struct isis_adjacency *adj, - int family) + uint32_t dist, uint16_t depth, int family, + struct isis_vertex *parent) { struct isis_vertex *vertex; #ifdef EXTREME_DEBUG u_char buff[255]; #endif + assert (spftree && parent); + + /* RFC3787 section 5.1 */ + if (spftree->area->newmetric == 1) + { + if (dist > MAX_WIDE_PATH_METRIC) + return; + } /* C.2.6 b) */ - if (dist > MAX_PATH_METRIC) - return; + else if (spftree->area->oldmetric == 1) + { + if (dist > MAX_NARROW_PATH_METRIC) + return; + } + /* c) */ vertex = isis_find_vertex (spftree->paths, id, vtype); if (vertex) { #ifdef EXTREME_DEBUG - zlog_debug ("ISIS-Spf: process_N %s %s dist %d already found from PATH", + zlog_debug ("ISIS-Spf: process_N %s %s %s dist %d already found from PATH", + print_sys_hostname (vertex->N.id), vtype2string (vtype), vid2string (vertex, buff), dist); #endif /* EXTREME_DEBUG */ assert (dist >= vertex->d_N); @@ -517,16 +652,26 @@ process_N (struct isis_spftree *spftree, enum vertextype vtype, void *id, { /* 1) */ #ifdef EXTREME_DEBUG - zlog_debug ("ISIS-Spf: process_N %s %s dist %d", - vtype2string (vtype), vid2string (vertex, buff), dist); + zlog_debug ("ISIS-Spf: process_N %s %s %s dist %d parent %s adjcount %d", + print_sys_hostname (vertex->N.id), + vtype2string (vtype), vid2string (vertex, buff), dist, + (parent ? print_sys_hostname (parent->N.id) : "null"), + (parent ? listcount (parent->Adj_N) : 0)); #endif /* EXTREME_DEBUG */ if (vertex->d_N == dist) { - if (adj) - listnode_add (vertex->Adj_N, adj); + struct listnode *node; + struct isis_adjacency *parent_adj; + for (ALL_LIST_ELEMENTS_RO (parent->Adj_N, node, parent_adj)) + if (listnode_lookup(vertex->Adj_N, parent_adj) == NULL) + listnode_add (vertex->Adj_N, parent_adj); /* 2) */ if (listcount (vertex->Adj_N) > ISIS_MAX_PATH_SPLITS) remove_excess_adjs (vertex->Adj_N); + if (listnode_lookup (vertex->parents, parent) == NULL) + listnode_add (vertex->parents, parent); + if (listnode_lookup (parent->children, vertex) == NULL) + listnode_add (parent->children, vertex); /* 3) */ return; } @@ -537,11 +682,23 @@ process_N (struct isis_spftree *spftree, enum vertextype vtype, void *id, } else { + struct listnode *pnode, *pnextnode; + struct isis_vertex *pvertex; listnode_delete (spftree->tents, vertex); + assert (listcount (vertex->children) == 0); + for (ALL_LIST_ELEMENTS (vertex->parents, pnode, pnextnode, pvertex)) + listnode_delete(pvertex->children, vertex); + isis_vertex_del (vertex); } } - isis_spf_add2tent (spftree, vtype, id, adj, dist, depth, family); +#ifdef EXTREME_DEBUG + zlog_debug ("ISIS-Spf: process_N add2tent %s %s dist %d parent %s", + print_sys_hostname(id), vtype2string (vtype), dist, + (parent ? print_sys_hostname (parent->N.id) : "null")); +#endif /* EXTREME_DEBUG */ + + isis_spf_add2tent (spftree, vtype, id, dist, depth, family, NULL, parent); return; } @@ -551,10 +708,10 @@ process_N (struct isis_spftree *spftree, enum vertextype vtype, void *id, static int isis_spf_process_lsp (struct isis_spftree *spftree, struct isis_lsp *lsp, uint32_t cost, uint16_t depth, int family, - struct isis_adjacency *adj) + u_char *root_sysid, struct isis_vertex *parent) { struct listnode *node, *fragnode = NULL; - u_int16_t dist; + uint32_t dist; struct is_neigh *is_neigh; struct te_is_neigh *te_is_neigh; struct ipv4_reachability *ipreach; @@ -564,114 +721,125 @@ isis_spf_process_lsp (struct isis_spftree *spftree, struct isis_lsp *lsp, #ifdef HAVE_IPV6 struct ipv6_reachability *ip6reach; #endif /* HAVE_IPV6 */ + static const u_char null_sysid[ISIS_SYS_ID_LEN]; - if (lsp->tlv_data.nlpids == NULL || !speaks (lsp->tlv_data.nlpids, family)) + if (!speaks (lsp->tlv_data.nlpids, family)) return ISIS_OK; lspfragloop: if (lsp->lsp_header->seq_num == 0) { - zlog_warn ("isis_spf_process_lsp(): lsp with 0 seq_num" - " - do not process"); + zlog_warn ("isis_spf_process_lsp(): lsp with 0 seq_num - ignore"); return ISIS_WARNING; } +#ifdef EXTREME_DEBUG + zlog_debug ("ISIS-Spf: process_lsp %s", print_sys_hostname(lsp->lsp_header->lsp_id)); +#endif /* EXTREME_DEBUG */ + if (!ISIS_MASK_LSP_OL_BIT (lsp->lsp_header->lsp_bits)) + { + if (lsp->tlv_data.is_neighs) { - if (lsp->tlv_data.is_neighs) - { - for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.is_neighs, node, is_neigh)) - { - /* C.2.6 a) */ - /* Two way connectivity */ - if (!memcmp (is_neigh->neigh_id, isis->sysid, ISIS_SYS_ID_LEN)) - continue; - dist = cost + is_neigh->metrics.metric_default; - vtype = LSP_PSEUDO_ID (is_neigh->neigh_id) ? VTYPE_PSEUDO_IS - : VTYPE_NONPSEUDO_IS; - process_N (spftree, vtype, (void *) is_neigh->neigh_id, dist, - depth + 1, adj, family); - } - } - if (lsp->tlv_data.te_is_neighs) - { - for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.te_is_neighs, node, - te_is_neigh)) - { - uint32_t metric; - if (!memcmp (te_is_neigh->neigh_id, isis->sysid, ISIS_SYS_ID_LEN)) - continue; - memcpy (&metric, te_is_neigh->te_metric, 3); - dist = cost + ntohl (metric << 8); - vtype = LSP_PSEUDO_ID (te_is_neigh->neigh_id) ? VTYPE_PSEUDO_TE_IS - : VTYPE_NONPSEUDO_TE_IS; - process_N (spftree, vtype, (void *) te_is_neigh->neigh_id, dist, - depth + 1, adj, family); - } - } - if (family == AF_INET && lsp->tlv_data.ipv4_int_reachs) - { - prefix.family = AF_INET; - for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.ipv4_int_reachs, - node, ipreach)) - { - dist = cost + ipreach->metrics.metric_default; - vtype = VTYPE_IPREACH_INTERNAL; - prefix.u.prefix4 = ipreach->prefix; - prefix.prefixlen = ip_masklen (ipreach->mask); - process_N (spftree, vtype, (void *) &prefix, dist, depth + 1, - adj, family); - } - } + for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.is_neighs, node, is_neigh)) + { + /* C.2.6 a) */ + /* Two way connectivity */ + if (!memcmp (is_neigh->neigh_id, root_sysid, ISIS_SYS_ID_LEN)) + continue; + if (!memcmp (is_neigh->neigh_id, null_sysid, ISIS_SYS_ID_LEN)) + continue; + dist = cost + is_neigh->metrics.metric_default; + vtype = LSP_PSEUDO_ID (is_neigh->neigh_id) ? VTYPE_PSEUDO_IS + : VTYPE_NONPSEUDO_IS; + process_N (spftree, vtype, (void *) is_neigh->neigh_id, dist, + depth + 1, family, parent); + } + } + if (lsp->tlv_data.te_is_neighs) + { + for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.te_is_neighs, node, + te_is_neigh)) + { + if (!memcmp (te_is_neigh->neigh_id, root_sysid, ISIS_SYS_ID_LEN)) + continue; + if (!memcmp (te_is_neigh->neigh_id, null_sysid, ISIS_SYS_ID_LEN)) + continue; + dist = cost + GET_TE_METRIC(te_is_neigh); + vtype = LSP_PSEUDO_ID (te_is_neigh->neigh_id) ? VTYPE_PSEUDO_TE_IS + : VTYPE_NONPSEUDO_TE_IS; + process_N (spftree, vtype, (void *) te_is_neigh->neigh_id, dist, + depth + 1, family, parent); + } + } + } - if (family == AF_INET && lsp->tlv_data.ipv4_ext_reachs) - { - prefix.family = AF_INET; - for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.ipv4_ext_reachs, - node, ipreach)) - { - dist = cost + ipreach->metrics.metric_default; - vtype = VTYPE_IPREACH_EXTERNAL; - prefix.u.prefix4 = ipreach->prefix; - prefix.prefixlen = ip_masklen (ipreach->mask); - process_N (spftree, vtype, (void *) &prefix, dist, depth + 1, - adj, family); - } - } - if (family == AF_INET && lsp->tlv_data.te_ipv4_reachs) - { - prefix.family = AF_INET; - for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.te_ipv4_reachs, - node, te_ipv4_reach)) - { - dist = cost + ntohl (te_ipv4_reach->te_metric); - vtype = VTYPE_IPREACH_TE; - prefix.u.prefix4 = newprefix2inaddr (&te_ipv4_reach->prefix_start, - te_ipv4_reach->control); - prefix.prefixlen = (te_ipv4_reach->control & 0x3F); - process_N (spftree, vtype, (void *) &prefix, dist, depth + 1, - adj, family); - } - } + if (family == AF_INET && lsp->tlv_data.ipv4_int_reachs) + { + prefix.family = AF_INET; + for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.ipv4_int_reachs, node, ipreach)) + { + dist = cost + ipreach->metrics.metric_default; + vtype = VTYPE_IPREACH_INTERNAL; + prefix.u.prefix4 = ipreach->prefix; + prefix.prefixlen = ip_masklen (ipreach->mask); + apply_mask (&prefix); + process_N (spftree, vtype, (void *) &prefix, dist, depth + 1, + family, parent); + } + } + if (family == AF_INET && lsp->tlv_data.ipv4_ext_reachs) + { + prefix.family = AF_INET; + for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.ipv4_ext_reachs, node, ipreach)) + { + dist = cost + ipreach->metrics.metric_default; + vtype = VTYPE_IPREACH_EXTERNAL; + prefix.u.prefix4 = ipreach->prefix; + prefix.prefixlen = ip_masklen (ipreach->mask); + apply_mask (&prefix); + process_N (spftree, vtype, (void *) &prefix, dist, depth + 1, + family, parent); + } + } + if (family == AF_INET && lsp->tlv_data.te_ipv4_reachs) + { + prefix.family = AF_INET; + for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.te_ipv4_reachs, + node, te_ipv4_reach)) + { + assert ((te_ipv4_reach->control & 0x3F) <= IPV4_MAX_BITLEN); + + dist = cost + ntohl (te_ipv4_reach->te_metric); + vtype = VTYPE_IPREACH_TE; + prefix.u.prefix4 = newprefix2inaddr (&te_ipv4_reach->prefix_start, + te_ipv4_reach->control); + prefix.prefixlen = (te_ipv4_reach->control & 0x3F); + apply_mask (&prefix); + process_N (spftree, vtype, (void *) &prefix, dist, depth + 1, + family, parent); + } + } #ifdef HAVE_IPV6 - if (family == AF_INET6 && lsp->tlv_data.ipv6_reachs) - { - prefix.family = AF_INET6; - for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.ipv6_reachs, - node, ip6reach)) - { - dist = cost + ip6reach->metric; - vtype = (ip6reach->control_info & CTRL_INFO_DISTRIBUTION) ? - VTYPE_IP6REACH_EXTERNAL : VTYPE_IP6REACH_INTERNAL; - prefix.prefixlen = ip6reach->prefix_len; - memcpy (&prefix.u.prefix6.s6_addr, ip6reach->prefix, - PSIZE (ip6reach->prefix_len)); - process_N (spftree, vtype, (void *) &prefix, dist, depth + 1, - adj, family); - } - } -#endif /* HAVE_IPV6 */ + if (family == AF_INET6 && lsp->tlv_data.ipv6_reachs) + { + prefix.family = AF_INET6; + for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.ipv6_reachs, node, ip6reach)) + { + assert (ip6reach->prefix_len <= IPV6_MAX_BITLEN); + + dist = cost + ntohl(ip6reach->metric); + vtype = (ip6reach->control_info & CTRL_INFO_DISTRIBUTION) ? + VTYPE_IP6REACH_EXTERNAL : VTYPE_IP6REACH_INTERNAL; + prefix.prefixlen = ip6reach->prefix_len; + memcpy (&prefix.u.prefix6.s6_addr, ip6reach->prefix, + PSIZE (ip6reach->prefix_len)); + apply_mask (&prefix); + process_N (spftree, vtype, (void *) &prefix, dist, depth + 1, + family, parent); } + } +#endif /* HAVE_IPV6 */ if (fragnode == NULL) fragnode = listhead (lsp->lspu.frags); @@ -689,14 +857,16 @@ isis_spf_process_lsp (struct isis_spftree *spftree, struct isis_lsp *lsp, static int isis_spf_process_pseudo_lsp (struct isis_spftree *spftree, - struct isis_lsp *lsp, uint16_t cost, + struct isis_lsp *lsp, uint32_t cost, uint16_t depth, int family, - struct isis_adjacency *adj) + u_char *root_sysid, + struct isis_vertex *parent) { struct listnode *node, *fragnode = NULL; struct is_neigh *is_neigh; struct te_is_neigh *te_is_neigh; enum vertextype vtype; + uint32_t dist; pseudofragloop: @@ -707,41 +877,36 @@ isis_spf_process_pseudo_lsp (struct isis_spftree *spftree, return ISIS_WARNING; } +#ifdef EXTREME_DEBUG + zlog_debug ("ISIS-Spf: process_pseudo_lsp %s", + print_sys_hostname(lsp->lsp_header->lsp_id)); +#endif /* EXTREME_DEBUG */ + + /* RFC3787 section 4 SHOULD ignore overload bit in pseudo LSPs */ + if (lsp->tlv_data.is_neighs) for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.is_neighs, node, is_neigh)) { - vtype = LSP_PSEUDO_ID (is_neigh->neigh_id) ? VTYPE_PSEUDO_IS - : VTYPE_NONPSEUDO_IS; /* Two way connectivity */ - if (!memcmp (is_neigh->neigh_id, isis->sysid, ISIS_SYS_ID_LEN)) + if (!memcmp (is_neigh->neigh_id, root_sysid, ISIS_SYS_ID_LEN)) continue; - if ((depth > 0 || isis_find_vertex - (spftree->tents, (void *) is_neigh->neigh_id, vtype) == NULL) - && isis_find_vertex (spftree->paths, (void *) is_neigh->neigh_id, - vtype) == NULL) - { - /* C.2.5 i) */ - isis_spf_add2tent (spftree, vtype, is_neigh->neigh_id, adj, - cost, depth, family); - } + dist = cost + is_neigh->metrics.metric_default; + vtype = LSP_PSEUDO_ID (is_neigh->neigh_id) ? VTYPE_PSEUDO_IS + : VTYPE_NONPSEUDO_IS; + process_N (spftree, vtype, (void *) is_neigh->neigh_id, dist, + depth + 1, family, parent); } if (lsp->tlv_data.te_is_neighs) for (ALL_LIST_ELEMENTS_RO (lsp->tlv_data.te_is_neighs, node, te_is_neigh)) { - vtype = LSP_PSEUDO_ID (te_is_neigh->neigh_id) ? VTYPE_PSEUDO_TE_IS - : VTYPE_NONPSEUDO_TE_IS; /* Two way connectivity */ - if (!memcmp (te_is_neigh->neigh_id, isis->sysid, ISIS_SYS_ID_LEN)) + if (!memcmp (te_is_neigh->neigh_id, root_sysid, ISIS_SYS_ID_LEN)) continue; - if ((depth > 0 || isis_find_vertex - (spftree->tents, (void *) te_is_neigh->neigh_id, vtype) == NULL) - && isis_find_vertex (spftree->paths, (void *) te_is_neigh->neigh_id, - vtype) == NULL) - { - /* C.2.5 i) */ - isis_spf_add2tent (spftree, vtype, te_is_neigh->neigh_id, adj, - cost, depth, family); - } + dist = cost + GET_TE_METRIC(te_is_neigh); + vtype = LSP_PSEUDO_ID (te_is_neigh->neigh_id) ? VTYPE_PSEUDO_TE_IS + : VTYPE_NONPSEUDO_TE_IS; + process_N (spftree, vtype, (void *) te_is_neigh->neigh_id, dist, + depth + 1, family, parent); } if (fragnode == NULL) @@ -759,10 +924,10 @@ isis_spf_process_pseudo_lsp (struct isis_spftree *spftree, } static int -isis_spf_preload_tent (struct isis_spftree *spftree, - struct isis_area *area, int level, int family) +isis_spf_preload_tent (struct isis_spftree *spftree, int level, + int family, u_char *root_sysid, + struct isis_vertex *parent) { - struct isis_vertex *vertex; struct isis_circuit *circuit; struct listnode *cnode, *anode, *ipnode; struct isis_adjacency *adj; @@ -773,15 +938,16 @@ isis_spf_preload_tent (struct isis_spftree *spftree, struct prefix prefix; int retval = ISIS_OK; u_char lsp_id[ISIS_SYS_ID_LEN + 2]; + static u_char null_lsp_id[ISIS_SYS_ID_LEN + 2]; #ifdef HAVE_IPV6 struct prefix_ipv6 *ipv6; #endif /* HAVE_IPV6 */ - for (ALL_LIST_ELEMENTS_RO (area->circuit_list, cnode, circuit)) + for (ALL_LIST_ELEMENTS_RO (spftree->area->circuit_list, cnode, circuit)) { if (circuit->state != C_STATE_UP) continue; - if (!(circuit->circuit_is_type & level)) + if (!(circuit->is_type & level)) continue; if (family == AF_INET && !circuit->ip_router) continue; @@ -799,8 +965,9 @@ isis_spf_preload_tent (struct isis_spftree *spftree, { prefix.u.prefix4 = ipv4->prefix; prefix.prefixlen = ipv4->prefixlen; + apply_mask (&prefix); isis_spf_add_local (spftree, VTYPE_IPREACH_INTERNAL, &prefix, - NULL, 0, family); + NULL, 0, family, parent); } } #ifdef HAVE_IPV6 @@ -811,8 +978,9 @@ isis_spf_preload_tent (struct isis_spftree *spftree, { prefix.prefixlen = ipv6->prefixlen; prefix.u.prefix6 = ipv6->prefix; + apply_mask (&prefix); isis_spf_add_local (spftree, VTYPE_IP6REACH_INTERNAL, - &prefix, NULL, 0, family); + &prefix, NULL, 0, family, parent); } } #endif /* HAVE_IPV6 */ @@ -832,40 +1000,41 @@ isis_spf_preload_tent (struct isis_spftree *spftree, level, circuit->interface->name); continue; } - anode = listhead (adj_list); - while (anode) + for (ALL_LIST_ELEMENTS_RO (adj_list, anode, adj)) { - adj = listgetdata (anode); if (!speaks (&adj->nlpids, family)) - { - anode = listnextnode (anode); continue; - } switch (adj->sys_type) { case ISIS_SYSTYPE_ES: isis_spf_add_local (spftree, VTYPE_ES, adj->sysid, adj, - circuit->te_metric[level - 1], family); + circuit->te_metric[level - 1], + family, parent); break; case ISIS_SYSTYPE_IS: case ISIS_SYSTYPE_L1_IS: case ISIS_SYSTYPE_L2_IS: - vertex = - isis_spf_add_local (spftree, VTYPE_NONPSEUDO_IS, - adj->sysid, adj, - circuit->te_metric[level - 1], family); + isis_spf_add_local (spftree, + spftree->area->oldmetric ? + VTYPE_NONPSEUDO_IS : + VTYPE_NONPSEUDO_TE_IS, + adj->sysid, adj, + circuit->te_metric[level - 1], + family, parent); memcpy (lsp_id, adj->sysid, ISIS_SYS_ID_LEN); LSP_PSEUDO_ID (lsp_id) = 0; LSP_FRAGMENT (lsp_id) = 0; - lsp = lsp_search (lsp_id, area->lspdb[level - 1]); - if (!lsp) - zlog_warn ("No lsp found for IS adjacency"); + lsp = lsp_search (lsp_id, spftree->area->lspdb[level - 1]); + if (lsp == NULL || lsp->lsp_header->rem_lifetime == 0) + zlog_warn ("ISIS-Spf: No LSP %s found for IS adjacency " + "L%d on %s (ID %u)", + rawlspid_print (lsp_id), level, + circuit->interface->name, circuit->circuit_id); break; case ISIS_SYSTYPE_UNKNOWN: default: zlog_warn ("isis_spf_preload_tent unknow adj type"); } - anode = listnextnode (anode); } list_delete (adj_list); /* @@ -875,23 +1044,36 @@ isis_spf_preload_tent (struct isis_spftree *spftree, memcpy (lsp_id, circuit->u.bc.l1_desig_is, ISIS_SYS_ID_LEN + 1); else memcpy (lsp_id, circuit->u.bc.l2_desig_is, ISIS_SYS_ID_LEN + 1); - lsp = lsp_search (lsp_id, area->lspdb[level - 1]); + /* can happen during DR reboot */ + if (memcmp (lsp_id, null_lsp_id, ISIS_SYS_ID_LEN + 1) == 0) + { + if (isis->debugs & DEBUG_SPF_EVENTS) + zlog_debug ("ISIS-Spf: No L%d DR on %s (ID %d)", + level, circuit->interface->name, circuit->circuit_id); + continue; + } adj = isis_adj_lookup (lsp_id, adjdb); /* if no adj, we are the dis or error */ if (!adj && !circuit->u.bc.is_dr[level - 1]) { - zlog_warn ("ISIS-Spf: No adjacency found for DR"); + zlog_warn ("ISIS-Spf: No adjacency found from root " + "to L%d DR %s on %s (ID %d)", + level, rawlspid_print (lsp_id), + circuit->interface->name, circuit->circuit_id); + continue; } - else if (lsp == NULL || lsp->lsp_header->rem_lifetime == 0) + lsp = lsp_search (lsp_id, spftree->area->lspdb[level - 1]); + if (lsp == NULL || lsp->lsp_header->rem_lifetime == 0) { - zlog_warn ("ISIS-Spf: No lsp found for DR"); - } - else - { - isis_spf_process_pseudo_lsp (spftree, lsp, - circuit->te_metric[level - 1], 0, family, adj); - + zlog_warn ("ISIS-Spf: No lsp (%p) found from root " + "to L%d DR %s on %s (ID %d)", + (void *)lsp, level, rawlspid_print (lsp_id), + circuit->interface->name, circuit->circuit_id); + continue; } + isis_spf_process_pseudo_lsp (spftree, lsp, + circuit->te_metric[level - 1], 0, + family, root_sysid, parent); } else if (circuit->circ_type == CIRCUIT_T_P2P) { @@ -902,28 +1084,36 @@ isis_spf_preload_tent (struct isis_spftree *spftree, { case ISIS_SYSTYPE_ES: isis_spf_add_local (spftree, VTYPE_ES, adj->sysid, adj, - circuit->te_metric[level - 1], family); + circuit->te_metric[level - 1], family, + parent); break; case ISIS_SYSTYPE_IS: case ISIS_SYSTYPE_L1_IS: case ISIS_SYSTYPE_L2_IS: if (speaks (&adj->nlpids, family)) - isis_spf_add_local (spftree, VTYPE_NONPSEUDO_IS, adj->sysid, + isis_spf_add_local (spftree, + spftree->area->oldmetric ? + VTYPE_NONPSEUDO_IS : + VTYPE_NONPSEUDO_TE_IS, + adj->sysid, adj, circuit->te_metric[level - 1], - family); + family, parent); break; case ISIS_SYSTYPE_UNKNOWN: default: - zlog_warn ("isis_spf_preload_tent unknow adj type"); + zlog_warn ("isis_spf_preload_tent unknown adj type"); break; } } + else if (circuit->circ_type == CIRCUIT_T_LOOPBACK) + { + continue; + } else { zlog_warn ("isis_spf_preload_tent unsupported media"); retval = ISIS_WARNING; } - } return retval; @@ -935,25 +1125,30 @@ isis_spf_preload_tent (struct isis_spftree *spftree, */ static void add_to_paths (struct isis_spftree *spftree, struct isis_vertex *vertex, - struct isis_area *area, int level) + int level) { -#ifdef EXTREME_DEBUG u_char buff[BUFSIZ]; -#endif /* EXTREME_DEBUG */ + + if (isis_find_vertex (spftree->paths, vertex->N.id, vertex->type)) + return; listnode_add (spftree->paths, vertex); #ifdef EXTREME_DEBUG - zlog_debug ("ISIS-Spf: added %s %s depth %d dist %d to PATHS", + zlog_debug ("ISIS-Spf: added %s %s %s depth %d dist %d to PATHS", + print_sys_hostname (vertex->N.id), vtype2string (vertex->type), vid2string (vertex, buff), vertex->depth, vertex->d_N); #endif /* EXTREME_DEBUG */ + if (vertex->type > VTYPE_ES) { if (listcount (vertex->Adj_N) > 0) isis_route_create ((struct prefix *) &vertex->N.prefix, vertex->d_N, - vertex->depth, vertex->Adj_N, area, level); + vertex->depth, vertex->Adj_N, spftree->area, level); else if (isis->debugs & DEBUG_SPF_EVENTS) - zlog_debug ("ISIS-Spf: no adjacencies do not install route"); + zlog_debug ("ISIS-Spf: no adjacencies do not install route for " + "%s depth %d dist %d", vid2string (vertex, buff), + vertex->depth, vertex->d_N); } return; @@ -966,23 +1161,27 @@ init_spt (struct isis_spftree *spftree) list_delete_all_node (spftree->tents); list_delete_all_node (spftree->paths); spftree->tents->del = spftree->paths->del = NULL; - return; } static int -isis_run_spf (struct isis_area *area, int level, int family) +isis_run_spf (struct isis_area *area, int level, int family, u_char *sysid) { int retval = ISIS_OK; struct listnode *node; struct isis_vertex *vertex; + struct isis_vertex *root_vertex; struct isis_spftree *spftree = NULL; u_char lsp_id[ISIS_SYS_ID_LEN + 2]; struct isis_lsp *lsp; - struct isis_adjacency *adj = NULL; struct route_table *table = NULL; - struct route_node *rode; - struct isis_route_info *rinfo; + struct timeval time_now; + unsigned long long start_time, end_time; + + /* Get time that can't roll backwards. */ + quagga_gettime(QUAGGA_CLK_MONOTONIC, &time_now); + start_time = time_now.tv_sec; + start_time = (start_time * 1000000) + time_now.tv_usec; if (family == AF_INET) spftree = area->spftree[level - 1]; @@ -990,8 +1189,8 @@ isis_run_spf (struct isis_area *area, int level, int family) else if (family == AF_INET6) spftree = area->spftree6[level - 1]; #endif - assert (spftree); + assert (sysid); /* Make all routes in current route table inactive. */ if (family == AF_INET) @@ -1001,71 +1200,66 @@ isis_run_spf (struct isis_area *area, int level, int family) table = area->route_table6[level - 1]; #endif - for (rode = route_top (table); rode; rode = route_next (rode)) - { - if (rode->info == NULL) - continue; - rinfo = rode->info; - - UNSET_FLAG (rinfo->flag, ISIS_ROUTE_FLAG_ACTIVE); - } + isis_route_invalidate_table (area, table); /* * C.2.5 Step 0 */ init_spt (spftree); /* a) */ - isis_spf_add_self (spftree, area, level); + root_vertex = isis_spf_add_root (spftree, level, sysid); /* b) */ - retval = isis_spf_preload_tent (spftree, area, level, family); + retval = isis_spf_preload_tent (spftree, level, family, sysid, root_vertex); + if (retval != ISIS_OK) + { + zlog_warn ("ISIS-Spf: failed to load TENT SPF-root:%s", print_sys_hostname(sysid)); + goto out; + } /* * C.2.7 Step 2 */ if (listcount (spftree->tents) == 0) { - zlog_warn ("ISIS-Spf: TENT is empty"); + zlog_warn ("ISIS-Spf: TENT is empty SPF-root:%s", print_sys_hostname(sysid)); goto out; } while (listcount (spftree->tents) > 0) { - /* C.2.7 a) 1) */ node = listhead (spftree->tents); vertex = listgetdata (node); - /* C.2.7 a) 2) */ - list_delete_node (spftree->tents, node); - - /* C.2.7 a) 3) */ - if (isis_find_vertex (spftree->paths, vertex->N.id, vertex->type)) - continue; - add_to_paths (spftree, vertex, area, level); - - if (vertex->type == VTYPE_PSEUDO_IS || - vertex->type == VTYPE_NONPSEUDO_IS || - vertex->type == VTYPE_PSEUDO_TE_IS || - vertex->type == VTYPE_NONPSEUDO_TE_IS ) - { - if (listcount(vertex->Adj_N) == 0) { - continue; - } - adj = listgetdata(vertex->Adj_N->head); +#ifdef EXTREME_DEBUG + zlog_debug ("ISIS-Spf: get TENT node %s %s depth %d dist %d to PATHS", + print_sys_hostname (vertex->N.id), + vtype2string (vertex->type), vertex->depth, vertex->d_N); +#endif /* EXTREME_DEBUG */ + /* Remove from tent list and add to paths list */ + list_delete_node (spftree->tents, node); + add_to_paths (spftree, vertex, level); + switch (vertex->type) + { + case VTYPE_PSEUDO_IS: + case VTYPE_NONPSEUDO_IS: + case VTYPE_PSEUDO_TE_IS: + case VTYPE_NONPSEUDO_TE_IS: memcpy (lsp_id, vertex->N.id, ISIS_SYS_ID_LEN + 1); LSP_FRAGMENT (lsp_id) = 0; lsp = lsp_search (lsp_id, area->lspdb[level - 1]); - if (lsp) + if (lsp && lsp->lsp_header->rem_lifetime != 0) { if (LSP_PSEUDO_ID (lsp_id)) { isis_spf_process_pseudo_lsp (spftree, lsp, vertex->d_N, - vertex->depth, family, adj); + vertex->depth, family, sysid, + vertex); } else { isis_spf_process_lsp (spftree, lsp, vertex->d_N, - vertex->depth, family, adj); + vertex->depth, family, sysid, vertex); } } else @@ -1073,13 +1267,21 @@ isis_run_spf (struct isis_area *area, int level, int family) zlog_warn ("ISIS-Spf: No LSP found for %s", rawlspid_print (lsp_id)); } + break; + default:; } } out: - thread_add_event (master, isis_route_validate, area, 0); - spftree->lastrun = time (NULL); + isis_route_validate (area); spftree->pending = 0; + spftree->runcount++; + spftree->last_run_timestamp = time (NULL); + quagga_gettime(QUAGGA_CLK_MONOTONIC, &time_now); + end_time = time_now.tv_sec; + end_time = (end_time * 1000000) + time_now.tv_usec; + spftree->last_run_duration = end_time - start_time; + return retval; } @@ -1094,6 +1296,7 @@ isis_run_spf_l1 (struct thread *thread) assert (area); area->spftree[0]->t_spf = NULL; + area->spftree[0]->pending = 0; if (!(area->is_type & IS_LEVEL_1)) { @@ -1107,10 +1310,7 @@ isis_run_spf_l1 (struct thread *thread) zlog_debug ("ISIS-Spf (%s) L1 SPF needed, periodic SPF", area->area_tag); if (area->ip_circuits) - retval = isis_run_spf (area, 1, AF_INET); - - THREAD_TIMER_ON (master, area->spftree[0]->t_spf, isis_run_spf_l1, area, - isis_jitter (PERIODIC_SPF_INTERVAL, 10)); + retval = isis_run_spf (area, 1, AF_INET, isis->sysid); return retval; } @@ -1125,6 +1325,7 @@ isis_run_spf_l2 (struct thread *thread) assert (area); area->spftree[1]->t_spf = NULL; + area->spftree[1]->pending = 0; if (!(area->is_type & IS_LEVEL_2)) { @@ -1137,10 +1338,7 @@ isis_run_spf_l2 (struct thread *thread) zlog_debug ("ISIS-Spf (%s) L2 SPF needed, periodic SPF", area->area_tag); if (area->ip_circuits) - retval = isis_run_spf (area, 2, AF_INET); - - THREAD_TIMER_ON (master, area->spftree[1]->t_spf, isis_run_spf_l2, area, - isis_jitter (PERIODIC_SPF_INTERVAL, 10)); + retval = isis_run_spf (area, 2, AF_INET, isis->sysid); return retval; } @@ -1148,53 +1346,40 @@ isis_run_spf_l2 (struct thread *thread) int isis_spf_schedule (struct isis_area *area, int level) { - int retval = ISIS_OK; struct isis_spftree *spftree = area->spftree[level - 1]; - time_t diff, now = time (NULL); - - if (spftree->pending) - return retval; + time_t now = time (NULL); + int diff = now - spftree->last_run_timestamp; - diff = now - spftree->lastrun; + assert (diff >= 0); + assert (area->is_type & level); - /* FIXME: let's wait a minute before doing the SPF */ - if (now - isis->uptime < 60 || isis->uptime == 0) - { - if (level == 1) - THREAD_TIMER_ON (master, spftree->t_spf, isis_run_spf_l1, area, 60); - else - THREAD_TIMER_ON (master, spftree->t_spf, isis_run_spf_l2, area, 60); + if (isis->debugs & DEBUG_SPF_EVENTS) + zlog_debug ("ISIS-Spf (%s) L%d SPF schedule called, lastrun %d sec ago", + area->area_tag, level, diff); - spftree->pending = 1; - return retval; - } + if (spftree->pending) + return ISIS_OK; THREAD_TIMER_OFF (spftree->t_spf); - if (diff < MINIMUM_SPF_INTERVAL) - { - if (level == 1) - THREAD_TIMER_ON (master, spftree->t_spf, isis_run_spf_l1, area, - MINIMUM_SPF_INTERVAL - diff); - else - THREAD_TIMER_ON (master, spftree->t_spf, isis_run_spf_l2, area, - MINIMUM_SPF_INTERVAL - diff); + /* wait configured min_spf_interval before doing the SPF */ + if (diff >= area->min_spf_interval[level-1]) + return isis_run_spf (area, level, AF_INET, isis->sysid); - spftree->pending = 1; - } + if (level == 1) + THREAD_TIMER_ON (master, spftree->t_spf, isis_run_spf_l1, area, + area->min_spf_interval[0] - diff); else - { - spftree->pending = 0; - retval = isis_run_spf (area, level, AF_INET); - if (level == 1) - THREAD_TIMER_ON (master, spftree->t_spf, isis_run_spf_l1, area, - isis_jitter (PERIODIC_SPF_INTERVAL, 10)); - else - THREAD_TIMER_ON (master, spftree->t_spf, isis_run_spf_l2, area, - isis_jitter (PERIODIC_SPF_INTERVAL, 10)); - } + THREAD_TIMER_ON (master, spftree->t_spf, isis_run_spf_l2, area, + area->min_spf_interval[1] - diff); - return retval; + if (isis->debugs & DEBUG_SPF_EVENTS) + zlog_debug ("ISIS-Spf (%s) L%d SPF scheduled %d sec from now", + area->area_tag, level, area->min_spf_interval[level-1] - diff); + + spftree->pending = 1; + + return ISIS_OK; } #ifdef HAVE_IPV6 @@ -1208,11 +1393,12 @@ isis_run_spf6_l1 (struct thread *thread) assert (area); area->spftree6[0]->t_spf = NULL; + area->spftree6[0]->pending = 0; if (!(area->is_type & IS_LEVEL_1)) { if (isis->debugs & DEBUG_SPF_EVENTS) - zlog_warn ("ISIS-SPF (%s) area does not share level", area->area_tag); + zlog_warn ("ISIS-SPF (%s) area does not share level", area->area_tag); return ISIS_WARNING; } @@ -1220,10 +1406,7 @@ isis_run_spf6_l1 (struct thread *thread) zlog_debug ("ISIS-Spf (%s) L1 SPF needed, periodic SPF", area->area_tag); if (area->ipv6_circuits) - retval = isis_run_spf (area, 1, AF_INET6); - - THREAD_TIMER_ON (master, area->spftree6[0]->t_spf, isis_run_spf6_l1, area, - isis_jitter (PERIODIC_SPF_INTERVAL, 10)); + retval = isis_run_spf (area, 1, AF_INET6, isis->sysid); return retval; } @@ -1238,6 +1421,7 @@ isis_run_spf6_l2 (struct thread *thread) assert (area); area->spftree6[1]->t_spf = NULL; + area->spftree6[1]->pending = 0; if (!(area->is_type & IS_LEVEL_2)) { @@ -1250,10 +1434,7 @@ isis_run_spf6_l2 (struct thread *thread) zlog_debug ("ISIS-Spf (%s) L2 SPF needed, periodic SPF.", area->area_tag); if (area->ipv6_circuits) - retval = isis_run_spf (area, 2, AF_INET6); - - THREAD_TIMER_ON (master, area->spftree6[1]->t_spf, isis_run_spf6_l2, area, - isis_jitter (PERIODIC_SPF_INTERVAL, 10)); + retval = isis_run_spf (area, 2, AF_INET6, isis->sysid); return retval; } @@ -1263,104 +1444,111 @@ isis_spf_schedule6 (struct isis_area *area, int level) { int retval = ISIS_OK; struct isis_spftree *spftree = area->spftree6[level - 1]; - time_t diff, now = time (NULL); + time_t now = time (NULL); + time_t diff = now - spftree->last_run_timestamp; - if (spftree->pending) - return retval; + assert (diff >= 0); + assert (area->is_type & level); - diff = now - spftree->lastrun; + if (isis->debugs & DEBUG_SPF_EVENTS) + zlog_debug ("ISIS-Spf (%s) L%d SPF schedule called, lastrun %lld sec ago", + area->area_tag, level, (long long)diff); - /* FIXME: let's wait a minute before doing the SPF */ - if (now - isis->uptime < 60 || isis->uptime == 0) - { - if (level == 1) - THREAD_TIMER_ON (master, spftree->t_spf, isis_run_spf6_l1, area, 60); - else - THREAD_TIMER_ON (master, spftree->t_spf, isis_run_spf6_l2, area, 60); + if (spftree->pending) + return ISIS_OK; - spftree->pending = 1; - return retval; - } - THREAD_TIMER_OFF (spftree->t_spf); - if (diff < MINIMUM_SPF_INTERVAL) - { - if (level == 1) - THREAD_TIMER_ON (master, spftree->t_spf, isis_run_spf6_l1, area, - MINIMUM_SPF_INTERVAL - diff); - else - THREAD_TIMER_ON (master, spftree->t_spf, isis_run_spf6_l2, area, - MINIMUM_SPF_INTERVAL - diff); + /* wait configured min_spf_interval before doing the SPF */ + if (diff >= area->min_spf_interval[level-1]) + return isis_run_spf (area, level, AF_INET6, isis->sysid); - spftree->pending = 1; - } + if (level == 1) + THREAD_TIMER_ON (master, spftree->t_spf, isis_run_spf6_l1, area, + area->min_spf_interval[0] - diff); else - { - spftree->pending = 0; - retval = isis_run_spf (area, level, AF_INET6); + THREAD_TIMER_ON (master, spftree->t_spf, isis_run_spf6_l2, area, + area->min_spf_interval[1] - diff); - if (level == 1) - THREAD_TIMER_ON (master, spftree->t_spf, isis_run_spf6_l1, area, - isis_jitter (PERIODIC_SPF_INTERVAL, 10)); - else - THREAD_TIMER_ON (master, spftree->t_spf, isis_run_spf6_l2, area, - isis_jitter (PERIODIC_SPF_INTERVAL, 10)); - } + if (isis->debugs & DEBUG_SPF_EVENTS) + zlog_debug ("ISIS-Spf (%s) L%d SPF scheduled %lld sec from now", + area->area_tag, level, + (long long)(area->min_spf_interval[level-1] - diff)); + + spftree->pending = 1; return retval; } #endif static void -isis_print_paths (struct vty *vty, struct list *paths) +isis_print_paths (struct vty *vty, struct list *paths, u_char *root_sysid) { struct listnode *node; + struct listnode *anode; struct isis_vertex *vertex; - struct isis_dynhn *dyn, *nh_dyn = NULL; struct isis_adjacency *adj; -#if 0 - u_char buff[255]; -#endif /* 0 */ + u_char buff[BUFSIZ]; - vty_out (vty, "System Id Metric Next-Hop" - " Interface SNPA%s", VTY_NEWLINE); + vty_out (vty, "Vertex Type Metric " + "Next-Hop Interface Parent%s", VTY_NEWLINE); + + for (ALL_LIST_ELEMENTS_RO (paths, node, vertex)) { + if (memcmp (vertex->N.id, root_sysid, ISIS_SYS_ID_LEN) == 0) { + vty_out (vty, "%-20s %-12s %-6s", print_sys_hostname (root_sysid), + "", ""); + vty_out (vty, "%-30s", ""); + } else { + int rows = 0; + vty_out (vty, "%-20s %-12s %-6u ", vid2string (vertex, buff), + vtype2string (vertex->type), vertex->d_N); + for (ALL_LIST_ELEMENTS_RO (vertex->Adj_N, anode, adj)) { + if (adj) { + if (rows) { + vty_out (vty, "%s", VTY_NEWLINE); + vty_out (vty, "%-20s %-12s %-6s ", "", "", ""); + } + vty_out (vty, "%-20s %-9s ", + print_sys_hostname (adj->sysid), + adj->circuit->interface->name); + ++rows; + } + } + if (rows == 0) + vty_out (vty, "%-30s ", ""); + } - for (ALL_LIST_ELEMENTS_RO (paths, node, vertex)) - { - if (vertex->type != VTYPE_NONPSEUDO_IS) - continue; - if (memcmp (vertex->N.id, isis->sysid, ISIS_SYS_ID_LEN) == 0) - { - vty_out (vty, "%s --%s", host.name?host.name:"", - VTY_NEWLINE); + /* Print list of parents for the ECMP DAG */ + if (listcount (vertex->parents) > 0) { + struct listnode *pnode; + struct isis_vertex *pvertex; + int rows = 0; + for (ALL_LIST_ELEMENTS_RO (vertex->parents, pnode, pvertex)) { + if (rows) { + vty_out (vty, "%s", VTY_NEWLINE); + vty_out (vty, "%-72s", ""); + } + vty_out (vty, "%s(%d)", + vid2string (pvertex, buff), pvertex->type); + ++rows; } - else - { - dyn = dynhn_find_by_id ((u_char *) vertex->N.id); - adj = listgetdata (listhead (vertex->Adj_N)); - if (adj) - { - nh_dyn = dynhn_find_by_id (adj->sysid); - vty_out (vty, "%-20s %-10u %-20s %-11s %-5s%s", - (dyn != NULL) ? dyn->name.name : - (const u_char *)rawlspid_print ((u_char *) vertex->N.id), - vertex->d_N, (nh_dyn != NULL) ? nh_dyn->name.name : - (const u_char *)rawlspid_print (adj->sysid), - adj->circuit->interface->name, - snpa_print (adj->snpa), VTY_NEWLINE); - } - else - { - vty_out (vty, "%s %u %s", dyn ? dyn->name.name : - (const u_char *) rawlspid_print (vertex->N.id), - vertex->d_N, VTY_NEWLINE); + } else { + vty_out (vty, " NULL "); + } + +#if 0 + if (listcount (vertex->children) > 0) { + struct listnode *cnode; + struct isis_vertex *cvertex; + for (ALL_LIST_ELEMENTS_RO (vertex->children, cnode, cvertex)) { + vty_out (vty, "%s", VTY_NEWLINE); + vty_out (vty, "%-72s", ""); + vty_out (vty, "%s(%d) ", + vid2string (cvertex, buff), cvertex->type); } } -#if 0 - vty_out (vty, "%s %s %u %s", vtype2string (vertex->type), - vid2string (vertex, buff), vertex->d_N, VTY_NEWLINE); #endif + vty_out (vty, "%s", VTY_NEWLINE); } } @@ -1390,7 +1578,8 @@ DEFUN (show_isis_topology, { vty_out (vty, "IS-IS paths to level-%d routers that speak IP%s", level + 1, VTY_NEWLINE); - isis_print_paths (vty, area->spftree[level]->paths); + isis_print_paths (vty, area->spftree[level]->paths, isis->sysid); + vty_out (vty, "%s", VTY_NEWLINE); } #ifdef HAVE_IPV6 if (area->ipv6_circuits > 0 && area->spftree6[level] @@ -1399,10 +1588,13 @@ DEFUN (show_isis_topology, vty_out (vty, "IS-IS paths to level-%d routers that speak IPv6%s", level + 1, VTY_NEWLINE); - isis_print_paths (vty, area->spftree6[level]->paths); + isis_print_paths (vty, area->spftree6[level]->paths, isis->sysid); + vty_out (vty, "%s", VTY_NEWLINE); } #endif /* HAVE_IPV6 */ } + + vty_out (vty, "%s", VTY_NEWLINE); } return CMD_SUCCESS; @@ -1432,7 +1624,8 @@ DEFUN (show_isis_topology_l1, { vty_out (vty, "IS-IS paths to level-1 routers that speak IP%s", VTY_NEWLINE); - isis_print_paths (vty, area->spftree[0]->paths); + isis_print_paths (vty, area->spftree[0]->paths, isis->sysid); + vty_out (vty, "%s", VTY_NEWLINE); } #ifdef HAVE_IPV6 if (area->ipv6_circuits > 0 && area->spftree6[0] @@ -1440,9 +1633,11 @@ DEFUN (show_isis_topology_l1, { vty_out (vty, "IS-IS paths to level-1 routers that speak IPv6%s", VTY_NEWLINE); - isis_print_paths (vty, area->spftree6[0]->paths); + isis_print_paths (vty, area->spftree6[0]->paths, isis->sysid); + vty_out (vty, "%s", VTY_NEWLINE); } #endif /* HAVE_IPV6 */ + vty_out (vty, "%s", VTY_NEWLINE); } return CMD_SUCCESS; @@ -1472,7 +1667,8 @@ DEFUN (show_isis_topology_l2, { vty_out (vty, "IS-IS paths to level-2 routers that speak IP%s", VTY_NEWLINE); - isis_print_paths (vty, area->spftree[1]->paths); + isis_print_paths (vty, area->spftree[1]->paths, isis->sysid); + vty_out (vty, "%s", VTY_NEWLINE); } #ifdef HAVE_IPV6 if (area->ipv6_circuits > 0 && area->spftree6[1] @@ -1480,9 +1676,11 @@ DEFUN (show_isis_topology_l2, { vty_out (vty, "IS-IS paths to level-2 routers that speak IPv6%s", VTY_NEWLINE); - isis_print_paths (vty, area->spftree6[1]->paths); + isis_print_paths (vty, area->spftree6[1]->paths, isis->sysid); + vty_out (vty, "%s", VTY_NEWLINE); } #endif /* HAVE_IPV6 */ + vty_out (vty, "%s", VTY_NEWLINE); } return CMD_SUCCESS; @@ -1494,8 +1692,4 @@ isis_spf_cmds_init () install_element (VIEW_NODE, &show_isis_topology_cmd); install_element (VIEW_NODE, &show_isis_topology_l1_cmd); install_element (VIEW_NODE, &show_isis_topology_l2_cmd); - - install_element (ENABLE_NODE, &show_isis_topology_cmd); - install_element (ENABLE_NODE, &show_isis_topology_l1_cmd); - install_element (ENABLE_NODE, &show_isis_topology_l2_cmd); } diff --git a/isisd/isis_spf.h b/isisd/isis_spf.h index 6bdab2da6..aa543b705 100644 --- a/isisd/isis_spf.h +++ b/isisd/isis_spf.h @@ -54,25 +54,33 @@ struct isis_vertex struct prefix prefix; } N; - struct isis_lsp *lsp; u_int32_t d_N; /* d(N) Distance from this IS */ u_int16_t depth; /* The depth in the imaginary tree */ - - struct list *Adj_N; /* {Adj(N)} */ + struct list *Adj_N; /* {Adj(N)} next hop or neighbor list */ + struct list *parents; /* list of parents for ECMP */ + struct list *children; /* list of children used for tree dump */ }; struct isis_spftree { struct thread *t_spf; /* spf threads */ - time_t lastrun; /* for scheduling */ - int pending; /* already scheduled */ struct list *paths; /* the SPT */ struct list *tents; /* TENT */ - - u_int32_t timerun; /* statistics */ + struct isis_area *area; /* back pointer to area */ + int pending; /* already scheduled */ + unsigned int runcount; /* number of runs since uptime */ + time_t last_run_timestamp; /* last run timestamp for scheduling */ + time_t last_run_duration; /* last run duration in msec */ }; +struct isis_spftree * isis_spftree_new (struct isis_area *area); +void isis_spftree_del (struct isis_spftree *spftree); +void isis_spftree_adj_del (struct isis_spftree *spftree, + struct isis_adjacency *adj); void spftree_area_init (struct isis_area *area); +void spftree_area_del (struct isis_area *area); +void spftree_area_adj_del (struct isis_area *area, + struct isis_adjacency *adj); int isis_spf_schedule (struct isis_area *area, int level); void isis_spf_cmds_init (void); #ifdef HAVE_IPV6 diff --git a/isisd/isis_te.c b/isisd/isis_te.c new file mode 100644 index 000000000..6cb855143 --- /dev/null +++ b/isisd/isis_te.c @@ -0,0 +1,1370 @@ +/* + * IS-IS Rout(e)ing protocol - isis_te.c + * + * This is an implementation of RFC5305 + * + * Copyright (C) 2014 Orange Labs + * http://www.orange.com + * + * 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 "linklist.h" +#include "thread.h" +#include "vty.h" +#include "stream.h" +#include "memory.h" +#include "log.h" +#include "prefix.h" +#include "command.h" +#include "hash.h" +#include "if.h" +#include "checksum.h" +#include "md5.h" +#include "sockunion.h" +#include "network.h" + +#include "isisd/dict.h" +#include "isisd/isis_constants.h" +#include "isisd/isis_common.h" +#include "isisd/isis_flags.h" +#include "isisd/isis_circuit.h" +#include "isisd/isisd.h" +#include "isisd/isis_tlv.h" +#include "isisd/isis_lsp.h" +#include "isisd/isis_pdu.h" +#include "isisd/isis_dynhn.h" +#include "isisd/isis_misc.h" +#include "isisd/isis_csm.h" +#include "isisd/isis_adjacency.h" +#include "isisd/isis_spf.h" +#include "isisd/isis_te.h" + +/* Global varial for MPLS TE management */ +struct isis_mpls_te isisMplsTE; + +const char *mode2text[] = { "Disable", "Area", "AS", "Emulate" }; + +/*------------------------------------------------------------------------* + * Followings are control functions for MPLS-TE parameters management. + *------------------------------------------------------------------------*/ + +/* Search MPLS TE Circuit context from Interface */ +static struct mpls_te_circuit * +lookup_mpls_params_by_ifp (struct interface *ifp) +{ + struct isis_circuit *circuit; + + if ((circuit = circuit_scan_by_ifp (ifp)) == NULL) + return NULL; + + return circuit->mtc; +} + +/* Create new MPLS TE Circuit context */ +struct mpls_te_circuit * +mpls_te_circuit_new() +{ + struct mpls_te_circuit *mtc; + + zlog_debug ("ISIS MPLS-TE: Create new MPLS TE Circuit context"); + + mtc = XCALLOC(MTYPE_ISIS_MPLS_TE, sizeof (struct mpls_te_circuit)); + + if (mtc == NULL) + return NULL; + + mtc->status = disable; + mtc->type = STD_TE; + mtc->length = 0; + + return mtc; + +} + +/* Copy SUB TLVs parameters into a buffer - No space verification are performed */ +/* Caller must verify before that there is enough free space in the buffer */ +u_char +add_te_subtlvs(u_char *buf, struct mpls_te_circuit *mtc) +{ + u_char size, *tlvs = buf; + + zlog_debug ("ISIS MPLS-TE: Add TE Sub TLVs to buffer"); + + if (mtc == NULL) + { + zlog_debug("ISIS MPLS-TE: Abort! No MPLS TE Circuit available has been specified"); + return 0; + } + + /* Create buffer if not provided */ + if (buf == NULL) + { + zlog_debug("ISIS MPLS-TE: Abort! No Buffer has been specified"); + return 0; + } + + /* TE_SUBTLV_ADMIN_GRP */ + if (SUBTLV_TYPE(mtc->admin_grp) != 0) + { + size = SUBTLV_SIZE (&(mtc->admin_grp.header)); + memcpy(tlvs, &(mtc->admin_grp), size); + tlvs += size; + } + + /* TE_SUBTLV_LLRI */ + if (SUBTLV_TYPE(mtc->llri) != 0) + { + size = SUBTLV_SIZE (&(mtc->llri.header)); + memcpy(tlvs, &(mtc->llri), size); + tlvs += size; + } + + /* TE_SUBTLV_LCLIF_IPADDR */ + if (SUBTLV_TYPE(mtc->local_ipaddr) != 0) + { + size = SUBTLV_SIZE (&(mtc->local_ipaddr.header)); + memcpy(tlvs, &(mtc->local_ipaddr), size); + tlvs += size; + } + + /* TE_SUBTLV_RMTIF_IPADDR */ + if (SUBTLV_TYPE(mtc->rmt_ipaddr) != 0) + { + size = SUBTLV_SIZE (&(mtc->rmt_ipaddr.header)); + memcpy(tlvs, &(mtc->rmt_ipaddr), size); + tlvs += size; + } + + /* TE_SUBTLV_MAX_BW */ + if (SUBTLV_TYPE(mtc->max_bw) != 0) + { + size = SUBTLV_SIZE (&(mtc->max_bw.header)); + memcpy(tlvs, &(mtc->max_bw), size); + tlvs += size; + } + + /* TE_SUBTLV_MAX_RSV_BW */ + if (SUBTLV_TYPE(mtc->max_rsv_bw) != 0) + { + size = SUBTLV_SIZE (&(mtc->max_rsv_bw.header)); + memcpy(tlvs, &(mtc->max_rsv_bw), size); + tlvs += size; + } + + /* TE_SUBTLV_UNRSV_BW */ + if (SUBTLV_TYPE(mtc->unrsv_bw) != 0) + { + size = SUBTLV_SIZE (&(mtc->unrsv_bw.header)); + memcpy(tlvs, &(mtc->unrsv_bw), size); + tlvs += size; + } + + /* TE_SUBTLV_TE_METRIC */ + if (SUBTLV_TYPE(mtc->te_metric) != 0) + { + size = SUBTLV_SIZE (&(mtc->te_metric.header)); + memcpy(tlvs, &(mtc->te_metric), size); + tlvs += size; + } + + /* TE_SUBTLV_AV_DELAY */ + if (SUBTLV_TYPE(mtc->av_delay) != 0) + { + size = SUBTLV_SIZE (&(mtc->av_delay.header)); + memcpy(tlvs, &(mtc->av_delay), size); + tlvs += size; + } + + /* TE_SUBTLV_MM_DELAY */ + if (SUBTLV_TYPE(mtc->mm_delay) != 0) + { + size = SUBTLV_SIZE (&(mtc->mm_delay.header)); + memcpy(tlvs, &(mtc->mm_delay), size); + tlvs += size; + } + + /* TE_SUBTLV_DELAY_VAR */ + if (SUBTLV_TYPE(mtc->delay_var) != 0) + { + size = SUBTLV_SIZE (&(mtc->delay_var.header)); + memcpy(tlvs, &(mtc->delay_var), size); + tlvs += size; + } + + /* TE_SUBTLV_PKT_LOSS */ + if (SUBTLV_TYPE(mtc->pkt_loss) != 0) + { + size = SUBTLV_SIZE (&(mtc->pkt_loss.header)); + memcpy(tlvs, &(mtc->pkt_loss), size); + tlvs += size; + } + + /* TE_SUBTLV_RES_BW */ + if (SUBTLV_TYPE(mtc->res_bw) != 0) + { + size = SUBTLV_SIZE (&(mtc->res_bw.header)); + memcpy(tlvs, &(mtc->res_bw), size); + tlvs += size; + } + + /* TE_SUBTLV_AVA_BW */ + if (SUBTLV_TYPE(mtc->ava_bw) != 0) + { + size = SUBTLV_SIZE (&(mtc->ava_bw.header)); + memcpy(tlvs, &(mtc->ava_bw), size); + tlvs += size; + } + + /* TE_SUBTLV_USE_BW */ + if (SUBTLV_TYPE(mtc->use_bw) != 0) + { + size = SUBTLV_SIZE (&(mtc->use_bw.header)); + memcpy(tlvs, &(mtc->use_bw), size); + tlvs += size; + } + + /* Update SubTLVs length */ + mtc->length = subtlvs_len(mtc); + + zlog_debug("ISIS MPLS-TE: Add %d bytes length SubTLVs", mtc->length); + + return mtc->length; +} + +/* Compute total Sub-TLVs size */ +u_char +subtlvs_len (struct mpls_te_circuit *mtc) +{ + int length = 0; + + /* Sanity Check */ + if (mtc == NULL) + return 0; + + /* TE_SUBTLV_ADMIN_GRP */ + if (SUBTLV_TYPE(mtc->admin_grp) != 0) + length += SUBTLV_SIZE (&(mtc->admin_grp.header)); + + /* TE_SUBTLV_LLRI */ + if (SUBTLV_TYPE(mtc->llri) != 0) + length += SUBTLV_SIZE (&mtc->llri.header); + + /* TE_SUBTLV_LCLIF_IPADDR */ + if (SUBTLV_TYPE(mtc->local_ipaddr) != 0) + length += SUBTLV_SIZE (&mtc->local_ipaddr.header); + + /* TE_SUBTLV_RMTIF_IPADDR */ + if (SUBTLV_TYPE(mtc->rmt_ipaddr) != 0) + length += SUBTLV_SIZE (&mtc->rmt_ipaddr.header); + + /* TE_SUBTLV_MAX_BW */ + if (SUBTLV_TYPE(mtc->max_bw) != 0) + length += SUBTLV_SIZE (&mtc->max_bw.header); + + /* TE_SUBTLV_MAX_RSV_BW */ + if (SUBTLV_TYPE(mtc->max_rsv_bw) != 0) + length += SUBTLV_SIZE (&mtc->max_rsv_bw.header); + + /* TE_SUBTLV_UNRSV_BW */ + if (SUBTLV_TYPE(mtc->unrsv_bw) != 0) + length += SUBTLV_SIZE (&mtc->unrsv_bw.header); + + /* TE_SUBTLV_TE_METRIC */ + if (SUBTLV_TYPE(mtc->te_metric) != 0) + length += SUBTLV_SIZE (&mtc->te_metric.header); + + /* TE_SUBTLV_AV_DELAY */ + if (SUBTLV_TYPE(mtc->av_delay) != 0) + length += SUBTLV_SIZE (&mtc->av_delay.header); + + /* TE_SUBTLV_MM_DELAY */ + if (SUBTLV_TYPE(mtc->mm_delay) != 0) + length += SUBTLV_SIZE (&mtc->mm_delay.header); + + /* TE_SUBTLV_DELAY_VAR */ + if (SUBTLV_TYPE(mtc->delay_var) != 0) + length += SUBTLV_SIZE (&mtc->delay_var.header); + + /* TE_SUBTLV_PKT_LOSS */ + if (SUBTLV_TYPE(mtc->pkt_loss) != 0) + length += SUBTLV_SIZE (&mtc->pkt_loss.header); + + /* TE_SUBTLV_RES_BW */ + if (SUBTLV_TYPE(mtc->res_bw) != 0) + length += SUBTLV_SIZE (&mtc->res_bw.header); + + /* TE_SUBTLV_AVA_BW */ + if (SUBTLV_TYPE(mtc->ava_bw) != 0) + length += SUBTLV_SIZE (&mtc->ava_bw.header); + + /* TE_SUBTLV_USE_BW */ + if (SUBTLV_TYPE(mtc->use_bw) != 0) + length += SUBTLV_SIZE (&mtc->use_bw.header); + + /* Check that length is lower than the MAXIMUM SUBTLV size i.e. 256 */ + if (length > MAX_SUBTLV_SIZE) + { + mtc->length = 0; + return 0; + } + + mtc->length = (u_char)length; + + return mtc->length; +} + +/* Following are various functions to set MPLS TE parameters */ +static void +set_circuitparams_admin_grp (struct mpls_te_circuit *mtc, u_int32_t admingrp) +{ + SUBTLV_TYPE(mtc->admin_grp) = TE_SUBTLV_ADMIN_GRP; + SUBTLV_LEN(mtc->admin_grp) = SUBTLV_DEF_SIZE; + mtc->admin_grp.value = htonl(admingrp); + return; +} + +static void __attribute__ ((unused)) +set_circuitparams_llri (struct mpls_te_circuit *mtc, u_int32_t local, u_int32_t remote) +{ + SUBTLV_TYPE(mtc->llri) = TE_SUBTLV_LLRI; + SUBTLV_LEN(mtc->llri) = TE_SUBTLV_LLRI_SIZE; + mtc->llri.local = htonl(local); + mtc->llri.remote = htonl(remote); +} + +void +set_circuitparams_local_ipaddr (struct mpls_te_circuit *mtc, struct in_addr addr) +{ + + SUBTLV_TYPE(mtc->local_ipaddr) = TE_SUBTLV_LOCAL_IPADDR; + SUBTLV_LEN(mtc->local_ipaddr) = SUBTLV_DEF_SIZE; + mtc->local_ipaddr.value.s_addr = addr.s_addr; + return; +} + +void +set_circuitparams_rmt_ipaddr (struct mpls_te_circuit *mtc, struct in_addr addr) +{ + + SUBTLV_TYPE(mtc->rmt_ipaddr) = TE_SUBTLV_RMT_IPADDR; + SUBTLV_LEN(mtc->rmt_ipaddr) = SUBTLV_DEF_SIZE; + mtc->rmt_ipaddr.value.s_addr = addr.s_addr; + return; +} + +static void +set_circuitparams_max_bw (struct mpls_te_circuit *mtc, float fp) +{ + SUBTLV_TYPE(mtc->max_bw) = TE_SUBTLV_MAX_BW; + SUBTLV_LEN(mtc->max_bw) = SUBTLV_DEF_SIZE; + mtc->max_bw.value = htonf(fp); + return; +} + +static void +set_circuitparams_max_rsv_bw (struct mpls_te_circuit *mtc, float fp) +{ + SUBTLV_TYPE(mtc->max_rsv_bw) = TE_SUBTLV_MAX_RSV_BW; + SUBTLV_LEN(mtc->max_rsv_bw) = SUBTLV_DEF_SIZE; + mtc->max_rsv_bw.value = htonf(fp); + return; +} + +static void +set_circuitparams_unrsv_bw (struct mpls_te_circuit *mtc, int priority, float fp) +{ + /* Note that TLV-length field is the size of array. */ + SUBTLV_TYPE(mtc->unrsv_bw) = TE_SUBTLV_UNRSV_BW; + SUBTLV_LEN(mtc->unrsv_bw) = TE_SUBTLV_UNRSV_SIZE; + mtc->unrsv_bw.value[priority] = htonf(fp); + return; +} + +static void +set_circuitparams_te_metric (struct mpls_te_circuit *mtc, u_int32_t te_metric) +{ + SUBTLV_TYPE(mtc->te_metric) = TE_SUBTLV_TE_METRIC; + SUBTLV_LEN(mtc->te_metric) = TE_SUBTLV_TE_METRIC_SIZE; + mtc->te_metric.value[0] = (te_metric >> 16) & 0xFF; + mtc->te_metric.value[1] = (te_metric >> 8) & 0xFF; + mtc->te_metric.value[2] = te_metric & 0xFF; + return; +} + +static void +set_circuitparams_inter_as (struct mpls_te_circuit *mtc, struct in_addr addr, u_int32_t as) +{ + + /* Set the Remote ASBR IP address and then the associated AS number */ + SUBTLV_TYPE(mtc->rip) = TE_SUBTLV_RIP; + SUBTLV_LEN(mtc->rip) = SUBTLV_DEF_SIZE; + mtc->rip.value.s_addr = addr.s_addr; + + SUBTLV_TYPE(mtc->ras) = TE_SUBTLV_RAS; + SUBTLV_LEN(mtc->ras) = SUBTLV_DEF_SIZE; + mtc->ras.value = htonl(as); +} + +static void +unset_circuitparams_inter_as (struct mpls_te_circuit *mtc) +{ + + /* Reset the Remote ASBR IP address and then the associated AS number */ + SUBTLV_TYPE(mtc->rip) = 0; + SUBTLV_LEN(mtc->rip) = 0; + mtc->rip.value.s_addr = 0; + + SUBTLV_TYPE(mtc->ras) = 0; + SUBTLV_LEN(mtc->ras) = 0; + mtc->ras.value = 0; +} + +static void +set_circuitparams_av_delay (struct mpls_te_circuit *mtc, u_int32_t delay, u_char anormal) +{ + u_int32_t tmp; + /* Note that TLV-length field is the size of array. */ + SUBTLV_TYPE(mtc->av_delay) = TE_SUBTLV_AV_DELAY; + SUBTLV_LEN(mtc->av_delay) = SUBTLV_DEF_SIZE; + tmp = delay & TE_EXT_MASK; + if (anormal) + tmp |= TE_EXT_ANORMAL; + mtc->av_delay.value = htonl(tmp); + return; +} + +static void +set_circuitparams_mm_delay (struct mpls_te_circuit *mtc, u_int32_t low, u_int32_t high, u_char anormal) +{ + u_int32_t tmp; + /* Note that TLV-length field is the size of array. */ + SUBTLV_TYPE(mtc->mm_delay) = TE_SUBTLV_MM_DELAY; + SUBTLV_LEN(mtc->mm_delay) = TE_SUBTLV_MM_DELAY_SIZE; + tmp = low & TE_EXT_MASK; + if (anormal) + tmp |= TE_EXT_ANORMAL; + mtc->mm_delay.low = htonl(tmp); + mtc->mm_delay.high = htonl(high); + return; +} + +static void +set_circuitparams_delay_var (struct mpls_te_circuit *mtc, u_int32_t jitter) +{ + /* Note that TLV-length field is the size of array. */ + SUBTLV_TYPE(mtc->delay_var) = TE_SUBTLV_DELAY_VAR; + SUBTLV_LEN(mtc->delay_var) = SUBTLV_DEF_SIZE; + mtc->delay_var.value = htonl(jitter & TE_EXT_MASK); + return; +} + +static void +set_circuitparams_pkt_loss (struct mpls_te_circuit *mtc, u_int32_t loss, u_char anormal) +{ + u_int32_t tmp; + /* Note that TLV-length field is the size of array. */ + SUBTLV_TYPE(mtc->pkt_loss) = TE_SUBTLV_PKT_LOSS; + SUBTLV_LEN(mtc->pkt_loss) = SUBTLV_DEF_SIZE; + tmp = loss & TE_EXT_MASK; + if (anormal) + tmp |= TE_EXT_ANORMAL; + mtc->pkt_loss.value = htonl(tmp); + return; +} + +static void +set_circuitparams_res_bw (struct mpls_te_circuit *mtc, float fp) +{ + /* Note that TLV-length field is the size of array. */ + SUBTLV_TYPE(mtc->res_bw) = TE_SUBTLV_RES_BW; + SUBTLV_LEN(mtc->res_bw) = SUBTLV_DEF_SIZE; + mtc->res_bw.value = htonf(fp); + return; +} + +static void +set_circuitparams_ava_bw (struct mpls_te_circuit *mtc, float fp) +{ + /* Note that TLV-length field is the size of array. */ + SUBTLV_TYPE(mtc->ava_bw) = TE_SUBTLV_AVA_BW; + SUBTLV_LEN(mtc->ava_bw) = SUBTLV_DEF_SIZE; + mtc->ava_bw.value = htonf(fp); + return; +} + +static void +set_circuitparams_use_bw (struct mpls_te_circuit *mtc, float fp) +{ + /* Note that TLV-length field is the size of array. */ + SUBTLV_TYPE(mtc->use_bw) = TE_SUBTLV_USE_BW; + SUBTLV_LEN(mtc->use_bw) = SUBTLV_DEF_SIZE; + mtc->use_bw.value = htonf(fp); + return; +} + +/* Main initialization / update function of the MPLS TE Circuit context */ +/* Call when interface TE Link parameters are modified */ +void +isis_link_params_update (struct isis_circuit *circuit, struct interface *ifp) +{ + int i; + struct prefix_ipv4 *addr; + struct mpls_te_circuit *mtc; + + /* Sanity Check */ + if ((circuit == NULL) || (ifp == NULL)) + return; + + zlog_info ("MPLS-TE: Initialize circuit parameters for interface %s", ifp->name); + + /* Check if MPLS TE Circuit context has not been already created */ + if (circuit->mtc == NULL) + circuit->mtc = mpls_te_circuit_new(); + + mtc = circuit->mtc; + + /* Fulfil MTC TLV from ifp TE Link parameters */ + if (HAS_LINK_PARAMS(ifp)) + { + mtc->status = enable; + /* STD_TE metrics */ + if (IS_PARAM_SET(ifp->link_params, LP_ADM_GRP)) + set_circuitparams_admin_grp (mtc, ifp->link_params->admin_grp); + else + SUBTLV_TYPE(mtc->admin_grp) = 0; + + /* If not already set, register local IP addr from ip_addr list if it exists */ + if (SUBTLV_TYPE(mtc->local_ipaddr) == 0) + { + if (circuit->ip_addrs != NULL && listcount(circuit->ip_addrs) != 0) + { + addr = (struct prefix_ipv4 *)listgetdata ((struct listnode *)listhead (circuit->ip_addrs)); + set_circuitparams_local_ipaddr (mtc, addr->prefix); + } + } + + /* If not already set, try to determine Remote IP addr if circuit is P2P */ + if ((SUBTLV_TYPE(mtc->rmt_ipaddr) == 0) && (circuit->circ_type == CIRCUIT_T_P2P)) + { + struct isis_adjacency *adj = circuit->u.p2p.neighbor; + if (adj->ipv4_addrs != NULL && listcount(adj->ipv4_addrs) != 0) + { + struct in_addr *ip_addr; + ip_addr = (struct in_addr *)listgetdata ((struct listnode *)listhead (adj->ipv4_addrs)); + set_circuitparams_rmt_ipaddr (mtc, *ip_addr); + } + } + + if (IS_PARAM_SET(ifp->link_params, LP_MAX_BW)) + set_circuitparams_max_bw (mtc, ifp->link_params->max_bw); + else + SUBTLV_TYPE(mtc->max_bw) = 0; + + if (IS_PARAM_SET(ifp->link_params, LP_MAX_RSV_BW)) + set_circuitparams_max_rsv_bw (mtc, ifp->link_params->max_rsv_bw); + else + SUBTLV_TYPE(mtc->max_rsv_bw) = 0; + + if (IS_PARAM_SET(ifp->link_params, LP_UNRSV_BW)) + for (i = 0; i < MAX_CLASS_TYPE; i++) + set_circuitparams_unrsv_bw (mtc, i, ifp->link_params->unrsv_bw[i]); + else + SUBTLV_TYPE(mtc->unrsv_bw) = 0; + + if (IS_PARAM_SET(ifp->link_params, LP_TE)) + set_circuitparams_te_metric(mtc, ifp->link_params->te_metric); + else + SUBTLV_TYPE(mtc->te_metric) = 0; + + /* TE metric Extensions */ + if (IS_PARAM_SET(ifp->link_params, LP_DELAY)) + set_circuitparams_av_delay(mtc, ifp->link_params->av_delay, 0); + else + SUBTLV_TYPE(mtc->av_delay) = 0; + + if (IS_PARAM_SET(ifp->link_params, LP_MM_DELAY)) + set_circuitparams_mm_delay(mtc, ifp->link_params->min_delay, ifp->link_params->max_delay, 0); + else + SUBTLV_TYPE(mtc->mm_delay) = 0; + + if (IS_PARAM_SET(ifp->link_params, LP_DELAY_VAR)) + set_circuitparams_delay_var(mtc, ifp->link_params->delay_var); + else + SUBTLV_TYPE(mtc->delay_var) = 0; + + if (IS_PARAM_SET(ifp->link_params, LP_PKT_LOSS)) + set_circuitparams_pkt_loss(mtc, ifp->link_params->pkt_loss, 0); + else + SUBTLV_TYPE(mtc->pkt_loss) = 0; + + if (IS_PARAM_SET(ifp->link_params, LP_RES_BW)) + set_circuitparams_res_bw(mtc, ifp->link_params->res_bw); + else + SUBTLV_TYPE(mtc->res_bw) = 0; + + if (IS_PARAM_SET(ifp->link_params, LP_AVA_BW)) + set_circuitparams_ava_bw(mtc, ifp->link_params->ava_bw); + else + SUBTLV_TYPE(mtc->ava_bw) = 0; + + if (IS_PARAM_SET(ifp->link_params, LP_USE_BW)) + set_circuitparams_use_bw(mtc, ifp->link_params->use_bw); + else + SUBTLV_TYPE(mtc->use_bw) = 0; + + /* INTER_AS */ + if (IS_PARAM_SET(ifp->link_params, LP_RMT_AS)) + set_circuitparams_inter_as(mtc, ifp->link_params->rmt_ip, ifp->link_params->rmt_as); + else + /* reset inter-as TE params */ + unset_circuitparams_inter_as (mtc); + + /* Compute total length of SUB TLVs */ + mtc->length = subtlvs_len(mtc); + + } + else + mtc->status = disable; + + /* Finally Update LSP */ +#if 0 + if (IS_MPLS_TE(isisMplsTE) && circuit->area) + lsp_regenerate_schedule (circuit->area, circuit->is_type, 0); +#endif + return; +} + +void +isis_mpls_te_update (struct interface *ifp) +{ + struct isis_circuit *circuit; + + /* Sanity Check */ + if (ifp == NULL) + return; + + /* Get circuit context from interface */ + if ((circuit = circuit_scan_by_ifp(ifp)) == NULL) + return; + + /* Update TE TLVs ... */ + isis_link_params_update(circuit, ifp); + + /* ... and LSP */ + if (IS_MPLS_TE(isisMplsTE) && circuit->area) + lsp_regenerate_schedule (circuit->area, circuit->is_type, 0); + + return; +} + +/*------------------------------------------------------------------------* + * Followings are vty session control functions. + *------------------------------------------------------------------------*/ + +static u_char +show_vty_subtlv_admin_grp (struct vty *vty, struct te_subtlv_admin_grp *tlv) +{ + + if (vty != NULL) + vty_out (vty, " Administrative Group: 0x%x%s", + (u_int32_t) ntohl (tlv->value), VTY_NEWLINE); + else + zlog_debug (" Administrative Group: 0x%x", + (u_int32_t) ntohl (tlv->value)); + + return (SUBTLV_HDR_SIZE + SUBTLV_DEF_SIZE); +} + +static u_char +show_vty_subtlv_llri (struct vty *vty, struct te_subtlv_llri *tlv) +{ + if (vty != NULL) + { + vty_out (vty, " Link Local ID: %d%s", (u_int32_t) ntohl (tlv->local), + VTY_NEWLINE); + vty_out (vty, " Link Remote ID: %d%s", (u_int32_t) ntohl (tlv->remote), + VTY_NEWLINE); + } + else + { + zlog_debug (" Link Local ID: %d", (u_int32_t) ntohl (tlv->local)); + zlog_debug (" Link Remote ID: %d", (u_int32_t) ntohl (tlv->remote)); + } + + return (SUBTLV_HDR_SIZE + TE_SUBTLV_LLRI_SIZE); +} + +static u_char +show_vty_subtlv_local_ipaddr (struct vty *vty, struct te_subtlv_local_ipaddr *tlv) +{ + if (vty != NULL) + vty_out (vty, " Local Interface IP Address(es): %s%s", inet_ntoa (tlv->value), VTY_NEWLINE); + else + zlog_debug (" Local Interface IP Address(es): %s", inet_ntoa (tlv->value)); + + return (SUBTLV_HDR_SIZE + SUBTLV_DEF_SIZE); +} + +static u_char +show_vty_subtlv_rmt_ipaddr (struct vty *vty, struct te_subtlv_rmt_ipaddr *tlv) +{ + if (vty != NULL) + vty_out (vty, " Remote Interface IP Address(es): %s%s", inet_ntoa (tlv->value), VTY_NEWLINE); + else + zlog_debug (" Remote Interface IP Address(es): %s", inet_ntoa (tlv->value)); + + return (SUBTLV_HDR_SIZE + SUBTLV_DEF_SIZE); +} + +static u_char +show_vty_subtlv_max_bw (struct vty *vty, struct te_subtlv_max_bw *tlv) +{ + float fval; + + fval = ntohf (tlv->value); + + if (vty != NULL) + vty_out (vty, " Maximum Bandwidth: %g (Bytes/sec)%s", fval, VTY_NEWLINE); + else + zlog_debug (" Maximum Bandwidth: %g (Bytes/sec)", fval); + + return (SUBTLV_HDR_SIZE + SUBTLV_DEF_SIZE); +} + +static u_char +show_vty_subtlv_max_rsv_bw (struct vty *vty, struct te_subtlv_max_rsv_bw *tlv) +{ + float fval; + + fval = ntohf (tlv->value); + + if (vty != NULL) + vty_out (vty, " Maximum Reservable Bandwidth: %g (Bytes/sec)%s", fval, + VTY_NEWLINE); + else + zlog_debug (" Maximum Reservable Bandwidth: %g (Bytes/sec)", fval); + + return (SUBTLV_HDR_SIZE + SUBTLV_DEF_SIZE); +} + +static u_char +show_vty_subtlv_unrsv_bw (struct vty *vty, struct te_subtlv_unrsv_bw *tlv) +{ + float fval1, fval2; + int i; + + if (vty != NULL) + vty_out (vty, " Unreserved Bandwidth:%s",VTY_NEWLINE); + else + zlog_debug (" Unreserved Bandwidth:"); + + for (i = 0; i < MAX_CLASS_TYPE; i+=2) + { + fval1 = ntohf (tlv->value[i]); + fval2 = ntohf (tlv->value[i+1]); + if (vty != NULL) + vty_out (vty, " [%d]: %g (Bytes/sec),\t[%d]: %g (Bytes/sec)%s", i, fval1, i+1, fval2, VTY_NEWLINE); + else + zlog_debug (" [%d]: %g (Bytes/sec),\t[%d]: %g (Bytes/sec)", i, fval1, i+1, fval2); + } + + return (SUBTLV_HDR_SIZE + TE_SUBTLV_UNRSV_SIZE); +} + +static u_char +show_vty_subtlv_te_metric (struct vty *vty, struct te_subtlv_te_metric *tlv) +{ + u_int32_t te_metric; + + te_metric = tlv->value[2] | tlv->value[1] << 8 | tlv->value[0] << 16; + if (vty != NULL) + vty_out (vty, " Traffic Engineering Metric: %u%s", te_metric, VTY_NEWLINE); + else + zlog_debug (" Traffic Engineering Metric: %u", te_metric); + + return (SUBTLV_HDR_SIZE + SUBTLV_DEF_SIZE); +} + +static u_char +show_vty_subtlv_ras (struct vty *vty, struct te_subtlv_ras *tlv) +{ + if (vty != NULL) + vty_out (vty, " Inter-AS TE Remote AS number: %u%s", ntohl (tlv->value), VTY_NEWLINE); + else + zlog_debug (" Inter-AS TE Remote AS number: %u", ntohl (tlv->value)); + + return (SUBTLV_HDR_SIZE + SUBTLV_DEF_SIZE); +} + +static u_char +show_vty_subtlv_rip (struct vty *vty, struct te_subtlv_rip *tlv) +{ + if (vty != NULL) + vty_out (vty, " Inter-AS TE Remote ASBR IP address: %s%s", inet_ntoa (tlv->value), VTY_NEWLINE); + else + zlog_debug (" Inter-AS TE Remote ASBR IP address: %s", inet_ntoa (tlv->value)); + + return (SUBTLV_HDR_SIZE + SUBTLV_DEF_SIZE); +} + +static u_char +show_vty_subtlv_av_delay (struct vty *vty, struct te_subtlv_av_delay *tlv) +{ + u_int32_t delay; + u_int32_t A; + + delay = (u_int32_t) ntohl (tlv->value) & TE_EXT_MASK; + A = (u_int32_t) ntohl (tlv->value) & TE_EXT_ANORMAL; + + if (vty != NULL) + vty_out (vty, " %s Average Link Delay: %d (micro-sec)%s", A ? "Anomalous" : "Normal", delay, VTY_NEWLINE); + else + zlog_debug (" %s Average Link Delay: %d (micro-sec)", A ? "Anomalous" : "Normal", delay); + + return (SUBTLV_HDR_SIZE + SUBTLV_DEF_SIZE); +} + +static u_char +show_vty_subtlv_mm_delay (struct vty *vty, struct te_subtlv_mm_delay *tlv) +{ + u_int32_t low, high; + u_int32_t A; + + low = (u_int32_t) ntohl (tlv->low) & TE_EXT_MASK; + A = (u_int32_t) ntohl (tlv->low) & TE_EXT_ANORMAL; + high = (u_int32_t) ntohl (tlv->high) & TE_EXT_MASK; + + if (vty != NULL) + vty_out (vty, " %s Min/Max Link Delay: %d / %d (micro-sec)%s", A ? "Anomalous" : "Normal", low, high, VTY_NEWLINE); + else + zlog_debug (" %s Min/Max Link Delay: %d / %d (micro-sec)", A ? "Anomalous" : "Normal", low, high); + + return (SUBTLV_HDR_SIZE + SUBTLV_DEF_SIZE); +} + +static u_char +show_vty_subtlv_delay_var (struct vty *vty, struct te_subtlv_delay_var *tlv) +{ + u_int32_t jitter; + + jitter = (u_int32_t) ntohl (tlv->value) & TE_EXT_MASK; + + if (vty != NULL) + vty_out (vty, " Delay Variation: %d (micro-sec)%s", jitter, VTY_NEWLINE); + else + zlog_debug (" Delay Variation: %d (micro-sec)", jitter); + + return (SUBTLV_HDR_SIZE + SUBTLV_DEF_SIZE); +} + +static u_char +show_vty_subtlv_pkt_loss (struct vty *vty, struct te_subtlv_pkt_loss *tlv) +{ + u_int32_t loss; + u_int32_t A; + float fval; + + loss = (u_int32_t) ntohl (tlv->value) & TE_EXT_MASK; + fval = (float) (loss * LOSS_PRECISION); + A = (u_int32_t) ntohl (tlv->value) & TE_EXT_ANORMAL; + + if (vty != NULL) + vty_out (vty, " %s Link Packet Loss: %g (%%)%s", A ? "Anomalous" : "Normal", fval, VTY_NEWLINE); + else + zlog_debug (" %s Link Packet Loss: %g (%%)", A ? "Anomalous" : "Normal", fval); + + return (SUBTLV_HDR_SIZE + SUBTLV_DEF_SIZE); +} + +static u_char +show_vty_subtlv_res_bw (struct vty *vty, struct te_subtlv_res_bw *tlv) +{ + float fval; + + fval = ntohf(tlv->value); + + if (vty != NULL) + vty_out (vty, " Unidirectional Residual Bandwidth: %g (Bytes/sec)%s", fval, VTY_NEWLINE); + else + zlog_debug (" Unidirectional Residual Bandwidth: %g (Bytes/sec)", fval); + + return (SUBTLV_HDR_SIZE + SUBTLV_DEF_SIZE); +} + +static u_char +show_vty_subtlv_ava_bw (struct vty *vty, struct te_subtlv_ava_bw *tlv) +{ + float fval; + + fval = ntohf (tlv->value); + + if (vty != NULL) + vty_out (vty, " Unidirectional Available Bandwidth: %g (Bytes/sec)%s", fval, VTY_NEWLINE); + else + zlog_debug (" Unidirectional Available Bandwidth: %g (Bytes/sec)", fval); + + return (SUBTLV_HDR_SIZE + SUBTLV_DEF_SIZE); +} + +static u_char +show_vty_subtlv_use_bw (struct vty *vty, struct te_subtlv_use_bw *tlv) +{ + float fval; + + fval = ntohf (tlv->value); + + if (vty != NULL) + vty_out (vty, " Unidirectional Utilized Bandwidth: %g (Bytes/sec)%s", fval, VTY_NEWLINE); + else + zlog_debug (" Unidirectional Utilized Bandwidth: %g (Bytes/sec)", fval); + + return (SUBTLV_HDR_SIZE + SUBTLV_DEF_SIZE); +} + +static u_char +show_vty_unknown_tlv (struct vty *vty, struct subtlv_header *tlvh) +{ + int i, rtn = 1; + u_char *v = (u_char *)tlvh; + + if (vty != NULL) + { + if (tlvh->length != 0) + { + vty_out (vty, " Unknown TLV: [type(%#.2x), length(%#.2x)]%s", + tlvh->type, tlvh->length, VTY_NEWLINE); + vty_out(vty, " Dump: [00]"); + rtn = 1; /* initialize end of line counter */ + for (i = 0; i < tlvh->length; i++) + { + vty_out (vty, " %#.2x", v[i]); + if (rtn == 8) + { + vty_out (vty, "%s [%.2x]", VTY_NEWLINE, i + 1); + rtn = 1; + } + else + rtn++; + } + vty_out (vty, "%s", VTY_NEWLINE); + } + else + vty_out (vty, " Unknown TLV: [type(%#.2x), length(%#.2x)]%s", + tlvh->type, tlvh->length, VTY_NEWLINE); + } + else + { + zlog_debug (" Unknown TLV: [type(%#.2x), length(%#.2x)]", + tlvh->type, tlvh->length); + } + + return SUBTLV_SIZE(tlvh); +} + +/* Main Show function */ +void +mpls_te_print_detail(struct vty *vty, struct te_is_neigh *te) +{ + struct subtlv_header *tlvh, *next; + u_int16_t sum = 0; + + zlog_debug ("ISIS MPLS-TE: Show database TE detail"); + + if (te->sub_tlvs == NULL) + return; + + tlvh = (struct subtlv_header *)te->sub_tlvs; + + for (; sum < te->sub_tlvs_length; tlvh = (next ? next : SUBTLV_HDR_NEXT (tlvh))) + { + next = NULL; + + switch (tlvh->type) + { + case TE_SUBTLV_ADMIN_GRP: + sum += show_vty_subtlv_admin_grp (vty, (struct te_subtlv_admin_grp *)tlvh); + break; + case TE_SUBTLV_LLRI: + sum += show_vty_subtlv_llri (vty, (struct te_subtlv_llri *)tlvh); + break; + case TE_SUBTLV_LOCAL_IPADDR: + sum += show_vty_subtlv_local_ipaddr (vty, (struct te_subtlv_local_ipaddr *)tlvh); + break; + case TE_SUBTLV_RMT_IPADDR: + sum += show_vty_subtlv_rmt_ipaddr (vty, (struct te_subtlv_rmt_ipaddr *)tlvh); + break; + case TE_SUBTLV_MAX_BW: + sum += show_vty_subtlv_max_bw (vty, (struct te_subtlv_max_bw *)tlvh); + break; + case TE_SUBTLV_MAX_RSV_BW: + sum += show_vty_subtlv_max_rsv_bw (vty, (struct te_subtlv_max_rsv_bw *)tlvh); + break; + case TE_SUBTLV_UNRSV_BW: + sum += show_vty_subtlv_unrsv_bw (vty, (struct te_subtlv_unrsv_bw *)tlvh); + break; + case TE_SUBTLV_TE_METRIC: + sum += show_vty_subtlv_te_metric (vty, (struct te_subtlv_te_metric *)tlvh); + break; + case TE_SUBTLV_RAS: + sum += show_vty_subtlv_ras (vty, (struct te_subtlv_ras *)tlvh); + break; + case TE_SUBTLV_RIP: + sum += show_vty_subtlv_rip (vty, (struct te_subtlv_rip *)tlvh); + break; + case TE_SUBTLV_AV_DELAY: + sum += show_vty_subtlv_av_delay (vty, (struct te_subtlv_av_delay *)tlvh); + break; + case TE_SUBTLV_MM_DELAY: + sum += show_vty_subtlv_mm_delay (vty, (struct te_subtlv_mm_delay *)tlvh); + break; + case TE_SUBTLV_DELAY_VAR: + sum += show_vty_subtlv_delay_var (vty, (struct te_subtlv_delay_var *)tlvh); + break; + case TE_SUBTLV_PKT_LOSS: + sum += show_vty_subtlv_pkt_loss (vty, (struct te_subtlv_pkt_loss *)tlvh); + break; + case TE_SUBTLV_RES_BW: + sum += show_vty_subtlv_res_bw (vty, (struct te_subtlv_res_bw *)tlvh); + break; + case TE_SUBTLV_AVA_BW: + sum += show_vty_subtlv_ava_bw (vty, (struct te_subtlv_ava_bw *)tlvh); + break; + case TE_SUBTLV_USE_BW: + sum += show_vty_subtlv_use_bw (vty, (struct te_subtlv_use_bw *)tlvh); + break; + default: + sum += show_vty_unknown_tlv (vty, tlvh); + break; + } + } + return; +} + +/* Specific MPLS TE router parameters write function */ +void +isis_mpls_te_config_write_router (struct vty *vty) +{ + + zlog_debug ("ISIS MPLS-TE: Write ISIS router configuration"); + + if (IS_MPLS_TE(isisMplsTE)) + { + vty_out (vty, " mpls-te on%s", VTY_NEWLINE); + vty_out (vty, " mpls-te router-address %s%s", + inet_ntoa (isisMplsTE.router_id), VTY_NEWLINE); + } + + return; +} + + +/*------------------------------------------------------------------------* + * Followings are vty command functions. + *------------------------------------------------------------------------*/ + +DEFUN (isis_mpls_te_on, + isis_mpls_te_on_cmd, + "mpls-te on", + MPLS_TE_STR + "Enable MPLS-TE functionality\n") +{ + struct listnode *node; + struct isis_circuit *circuit; + + if (IS_MPLS_TE(isisMplsTE)) + return CMD_SUCCESS; + + if (IS_DEBUG_ISIS(DEBUG_TE)) + zlog_debug ("ISIS MPLS-TE: OFF -> ON"); + + isisMplsTE.status = enable; + + /* + * Following code is intended to handle two cases; + * + * 1) MPLS-TE was disabled at startup time, but now become enabled. + * In this case, we must enable MPLS-TE Circuit regarding interface MPLS_TE flag + * 2) MPLS-TE was once enabled then disabled, and now enabled again. + */ + for (ALL_LIST_ELEMENTS_RO (isisMplsTE.cir_list, node, circuit)) + { + if (circuit->mtc == NULL || IS_FLOOD_AS (circuit->mtc->type)) + continue; + + if ((circuit->mtc->status == disable) + && HAS_LINK_PARAMS(circuit->interface)) + circuit->mtc->status = enable; + else + continue; + + /* Reoriginate STD_TE & GMPLS circuits */ + if (circuit->area) + lsp_regenerate_schedule (circuit->area, circuit->is_type, 0); + } + + return CMD_SUCCESS; +} + +DEFUN (no_isis_mpls_te_on, + no_isis_mpls_te_on_cmd, + "no mpls-te", + NO_STR + "Disable the MPLS-TE functionality\n") +{ + struct listnode *node; + struct isis_circuit *circuit; + + if (isisMplsTE.status == disable) + return CMD_SUCCESS; + + if (IS_DEBUG_ISIS(DEBUG_TE)) + zlog_debug ("ISIS MPLS-TE: ON -> OFF"); + + isisMplsTE.status = disable; + + /* Flush LSP if circuit engage */ + for (ALL_LIST_ELEMENTS_RO (isisMplsTE.cir_list, node, circuit)) + { + if (circuit->mtc == NULL || (circuit->mtc->status == disable)) + continue; + + /* disable MPLS_TE Circuit */ + circuit->mtc->status = disable; + + /* Re-originate circuit without STD_TE & GMPLS parameters */ + if (circuit->area) + lsp_regenerate_schedule (circuit->area, circuit->is_type, 0); + } + + return CMD_SUCCESS; +} + +DEFUN (isis_mpls_te_router_addr, + isis_mpls_te_router_addr_cmd, + "mpls-te router-address A.B.C.D", + MPLS_TE_STR + "Stable IP address of the advertising router\n" + "MPLS-TE router address in IPv4 address format\n") +{ + struct in_addr value; + struct listnode *node; + struct isis_area *area; + + if (! inet_aton (argv[0], &value)) + { + vty_out (vty, "Please specify Router-Addr by A.B.C.D%s", VTY_NEWLINE); + return CMD_WARNING; + } + + isisMplsTE.router_id.s_addr = value.s_addr; + + if (isisMplsTE.status == disable) + return CMD_SUCCESS; + + /* Update main Router ID in isis global structure */ + isis->router_id = value.s_addr; + /* And re-schedule LSP update */ + for (ALL_LIST_ELEMENTS_RO (isis->area_list, node, area)) + if (listcount (area->area_addrs) > 0) + lsp_regenerate_schedule (area, area->is_type, 0); + + return CMD_SUCCESS; +} + +DEFUN (isis_mpls_te_inter_as, + isis_mpls_te_inter_as_cmd, + "mpls-te inter-as (level-1|level-1-2|level-2-only)", + MPLS_TE_STR + "Configure MPLS-TE Inter-AS support\n" + "AREA native mode self originate INTER-AS LSP with L1 only flooding scope)\n" + "AREA native mode self originate INTER-AS LSP with L1 and L2 flooding scope)\n" + "AS native mode self originate INTER-AS LSP with L2 only flooding scope\n") +{ + vty_out (vty, "Not yet supported%s", VTY_NEWLINE); + return CMD_SUCCESS; +} + +DEFUN (no_isis_mpls_te_inter_as, + no_isis_mpls_te_inter_as_cmd, + "no mpls-te inter-as", + NO_STR + "Disable the MPLS-TE functionality\n" + "Disable MPLS-TE Inter-AS support\n") +{ + + vty_out (vty, "Not yet supported%s", VTY_NEWLINE); + return CMD_SUCCESS; +} + +DEFUN (show_isis_mpls_te_router, + show_isis_mpls_te_router_cmd, + "show isis mpls-te router", + SHOW_STR + ISIS_STR + MPLS_TE_STR + "Router information\n") +{ + if (IS_MPLS_TE(isisMplsTE)) + { + vty_out (vty, "--- MPLS-TE router parameters ---%s", VTY_NEWLINE); + + if (vty != NULL) + { + if (ntohs (isisMplsTE.router_id.s_addr) != 0) + vty_out (vty, " Router-Address: %s%s", inet_ntoa (isisMplsTE.router_id), VTY_NEWLINE); + else + vty_out (vty, " N/A%s", VTY_NEWLINE); + } + } + else + vty_out (vty, " MPLS-TE is disable on this router%s", VTY_NEWLINE); + + return CMD_SUCCESS; +} + +static void +show_mpls_te_sub (struct vty *vty, struct interface *ifp) +{ + struct mpls_te_circuit *mtc; + + if ((IS_MPLS_TE(isisMplsTE)) + && ((mtc = lookup_mpls_params_by_ifp (ifp)) != NULL)) + { + /* Continue only if interface is not passive or support Inter-AS TEv2 */ + if (mtc->status != enable) + { + if (IS_INTER_AS(mtc->type)) + { + vty_out (vty, "-- Inter-AS TEv2 link parameters for %s --%s", + ifp->name, VTY_NEWLINE); + } + else + { + /* MPLS-TE is not activate on this interface */ + /* or this interface is passive and Inter-AS TEv2 is not activate */ + vty_out (vty, " %s: MPLS-TE is disabled on this interface%s", + ifp->name, VTY_NEWLINE); + return; + } + } + else + { + vty_out (vty, "-- MPLS-TE link parameters for %s --%s", + ifp->name, VTY_NEWLINE); + } + + show_vty_subtlv_admin_grp (vty, &mtc->admin_grp); + + if (SUBTLV_TYPE(mtc->local_ipaddr) != 0) + show_vty_subtlv_local_ipaddr (vty, &mtc->local_ipaddr); + if (SUBTLV_TYPE(mtc->rmt_ipaddr) != 0) + show_vty_subtlv_rmt_ipaddr (vty, &mtc->rmt_ipaddr); + + show_vty_subtlv_max_bw (vty, &mtc->max_bw); + show_vty_subtlv_max_rsv_bw (vty, &mtc->max_rsv_bw); + show_vty_subtlv_unrsv_bw (vty, &mtc->unrsv_bw); + show_vty_subtlv_te_metric (vty, &mtc->te_metric); + + if (IS_INTER_AS(mtc->type)) + { + if (SUBTLV_TYPE(mtc->ras) != 0) + show_vty_subtlv_ras (vty, &mtc->ras); + if (SUBTLV_TYPE(mtc->rip) != 0) + show_vty_subtlv_rip (vty, &mtc->rip); + } + + show_vty_subtlv_av_delay (vty, &mtc->av_delay); + show_vty_subtlv_mm_delay (vty, &mtc->mm_delay); + show_vty_subtlv_delay_var (vty, &mtc->delay_var); + show_vty_subtlv_pkt_loss (vty, &mtc->pkt_loss); + show_vty_subtlv_res_bw (vty, &mtc->res_bw); + show_vty_subtlv_ava_bw (vty, &mtc->ava_bw); + show_vty_subtlv_use_bw (vty, &mtc->use_bw); + vty_out (vty, "---------------%s%s", VTY_NEWLINE, VTY_NEWLINE); + } + else + { + vty_out (vty, " %s: MPLS-TE is disabled on this interface%s", + ifp->name, VTY_NEWLINE); + } + + return; +} + +DEFUN (show_isis_mpls_te_interface, + show_isis_mpls_te_interface_cmd, + "show isis mpls-te interface [INTERFACE]", + SHOW_STR + ISIS_STR + MPLS_TE_STR + "Interface information\n" + "Interface name\n") +{ + struct interface *ifp; + struct listnode *node; + + /* Show All Interfaces. */ + if (argc == 0) + { + for (ALL_LIST_ELEMENTS_RO (iflist, node, ifp)) + show_mpls_te_sub (vty, ifp); + } + /* Interface name is specified. */ + else + { + if ((ifp = if_lookup_by_name (argv[0])) == NULL) + vty_out (vty, "No such interface name%s", VTY_NEWLINE); + else + show_mpls_te_sub (vty, ifp); + } + + return CMD_SUCCESS; +} + +/* Initialize MPLS_TE */ +void +isis_mpls_te_init (void) +{ + + zlog_debug("ISIS MPLS-TE: Initialize"); + + /* Initialize MPLS_TE structure */ + isisMplsTE.status = disable; + isisMplsTE.level = 0; + isisMplsTE.inter_as = off; + isisMplsTE.interas_areaid.s_addr = 0; + isisMplsTE.cir_list = list_new(); + isisMplsTE.router_id.s_addr = 0; + + /* Register new VTY commands */ + install_element (VIEW_NODE, &show_isis_mpls_te_router_cmd); + install_element (VIEW_NODE, &show_isis_mpls_te_interface_cmd); + + install_element (ISIS_NODE, &isis_mpls_te_on_cmd); + install_element (ISIS_NODE, &no_isis_mpls_te_on_cmd); + install_element (ISIS_NODE, &isis_mpls_te_router_addr_cmd); + install_element (ISIS_NODE, &isis_mpls_te_inter_as_cmd); + install_element (ISIS_NODE, &no_isis_mpls_te_inter_as_cmd); + + return; +} + diff --git a/isisd/isis_te.h b/isisd/isis_te.h new file mode 100644 index 000000000..4f29fdb9c --- /dev/null +++ b/isisd/isis_te.h @@ -0,0 +1,331 @@ +/* + * IS-IS Rout(e)ing protocol - isis_te.c + * + * This is an implementation of RFC5305, RFC 5307 and draft-ietf-isis-te-metric-extensions-11 + * + * Copyright (C) 2014 Orange Labs + * http://www.orange.com + * + * 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_ISIS_MPLS_TE_H +#define _ZEBRA_ISIS_MPLS_TE_H + +/* + * Traffic Engineering information are transport through LSP: + * - Extended IS Reachability TLV = 22 + * - Traffic Engineering Router ID TLV = 134 + * - Extended IP Reachability TLV = 135 + * - Inter-AS Reachability Information TLV = 141 + * + * and support following sub-TLV: + * + * Name Value Status + * _________________________________________________ + * Administartive group (color) 3 RFC5305 + * Link Local/Remote Identifiers 4 RFC5307 + * IPv4 interface address 6 RFC5305 + * IPv4 neighbor address 8 RFC5305 + * Maximum link bandwidth 9 RFC5305 + * Reservable link bandwidth 10 RFC5305 + * Unreserved bandwidth 11 RFC5305 + * TE Default metric 18 RFC5305 + * Link Protection Type 20 RFC5307 + * Interface Switching Capability 21 RFC5307 + * Remote AS number 24 RFC5316 + * IPv4 Remote ASBR identifier 25 RFC5316 + * + */ + +/* NOTE: RFC5316 is not yet supported in this version */ + +/* Following define the type of TE link regarding the various RFC */ +#define STD_TE 0x01 +#define GMPLS 0x02 +#define INTER_AS 0x04 +#define FLOOD_L1 0x10 +#define FLOOD_L2 0x20 +#define FLOOD_AS 0x40 +#define EMULATED 0x80 + +#define IS_STD_TE(x) (x & STD_TE) +#define IS_INTER_AS(x) (x & INTER_AS) +#define IS_EMULATED(x) (x & EMULATED) +#define IS_FLOOD_L1(x) (x & FLOOD_L1) +#define IS_FLOOD_L2(x) (x & FLOOD_L2) +#define IS_FLOOD_AS(x) (x & FLOOD_AS) +#define IS_INTER_AS_EMU(x) (x & INTER_AS & EMULATED) +#define IS_INTER_AS_AS(x) (x & INTER_AS & FLOOD_AS) + +/* + * Following section defines subTLV (tag, length, value) structures, + * used for Traffic Engineering. + */ +struct subtlv_header +{ + u_char type; /* sub_TLV_XXX type (see above) */ + u_char length; /* Value portion only, in byte */ +}; + +#define SUBTLV_HDR_SIZE 2 /* (sizeof (struct sub_tlv_header)) */ + +#define SUBTLV_SIZE(stlvh) (SUBTLV_HDR_SIZE + (stlvh)->length) + +#define SUBTLV_HDR_TOP(lsph) (struct subtlv_header *)((char *)(lsph) + ISIS_LSP_HEADER_SIZE) + +#define SUBTLV_HDR_NEXT(stlvh) (struct subtlv_header *)((char *)(stlvh) + SUBTLV_SIZE(stlvh)) + +#define SUBTLV_TYPE(stlvh) stlvh.header.type +#define SUBTLV_LEN(stlvh) stlvh.header.length +#define SUBTLV_VAL(stlvh) stlvh.value +#define SUBTLV_DATA(stlvh) stlvh + SUBTLV_HDR_SIZE + +#define SUBTLV_DEF_SIZE 4 + +/* Link Sub-TLV: Resource Class/Color - RFC 5305 */ +#define TE_SUBTLV_ADMIN_GRP 3 +struct te_subtlv_admin_grp +{ + struct subtlv_header header; /* Value length is 4 octets. */ + u_int32_t value; /* Admin. group membership. */ +} __attribute__((__packed__)); + +/* Link Local/Remote Identifiers - RFC 5307 */ +#define TE_SUBTLV_LLRI 4 +#define TE_SUBTLV_LLRI_SIZE 8 +struct te_subtlv_llri +{ + struct subtlv_header header; /* Value length is 8 octets. */ + u_int32_t local; /* Link Local Identifier */ + u_int32_t remote; /* Link Remote Identifier */ +} __attribute__((__packed__)); + +/* Link Sub-TLV: Local Interface IP Address - RFC 5305 */ +#define TE_SUBTLV_LOCAL_IPADDR 6 +struct te_subtlv_local_ipaddr +{ + struct subtlv_header header; /* Value length is 4 x N octets. */ + struct in_addr value; /* Local IP address(es). */ +} __attribute__((__packed__)); + +/* Link Sub-TLV: Neighbor Interface IP Address - RFC 5305 */ +#define TE_SUBTLV_RMT_IPADDR 8 +struct te_subtlv_rmt_ipaddr +{ + struct subtlv_header header; /* Value length is 4 x N octets. */ + struct in_addr value; /* Neighbor's IP address(es). */ +} __attribute__((__packed__)); + +/* Link Sub-TLV: Maximum Bandwidth - RFC 5305 */ +#define TE_SUBTLV_MAX_BW 9 +struct te_subtlv_max_bw +{ + struct subtlv_header header; /* Value length is 4 octets. */ + float value; /* bytes/sec */ +} __attribute__((__packed__)); + +/* Link Sub-TLV: Maximum Reservable Bandwidth - RFC 5305 */ +#define TE_SUBTLV_MAX_RSV_BW 10 +struct te_subtlv_max_rsv_bw +{ + struct subtlv_header header; /* Value length is 4 octets. */ + float value; /* bytes/sec */ +} __attribute__((__packed__)); + +/* Link Sub-TLV: Unreserved Bandwidth - RFC 5305 */ +#define TE_SUBTLV_UNRSV_BW 11 +#define TE_SUBTLV_UNRSV_SIZE 32 +struct te_subtlv_unrsv_bw +{ + struct subtlv_header header; /* Value length is 32 octets. */ + float value[8]; /* One for each priority level. */ +} __attribute__((__packed__)); + +/* Link Sub-TLV: Traffic Engineering Metric - RFC 5305 */ +#define TE_SUBTLV_TE_METRIC 18 +#define TE_SUBTLV_TE_METRIC_SIZE 3 +struct te_subtlv_te_metric +{ + struct subtlv_header header; /* Value length is 4 octets. */ + u_char value[3]; /* Link metric for TE purpose. */ +} __attribute__((__packed__)); + +/* Remote AS Number sub-TLV - RFC5316 */ +#define TE_SUBTLV_RAS 24 +struct te_subtlv_ras +{ + struct subtlv_header header; /* Value length is 4 octets. */ + u_int32_t value; /* Remote AS number */ +} __attribute__((__packed__)); + +/* IPv4 Remote ASBR ID Sub-TLV - RFC5316 */ +#define TE_SUBTLV_RIP 25 +struct te_subtlv_rip +{ + struct subtlv_header header; /* Value length is 4 octets. */ + struct in_addr value; /* Remote ASBR IP address */ +} __attribute__((__packed__)); + + +/* draft-ietf-isis-te-metric-extensions-11.txt */ +/* Link Sub-TLV: Average Link Delay */ +#define TE_SUBTLV_AV_DELAY 33 +struct te_subtlv_av_delay +{ + struct subtlv_header header; /* Value length is 4 bytes. */ + u_int32_t value; /* Average delay in micro-seconds only 24 bits => 0 ... 16777215 + with Anomalous Bit (A) as Upper most bit */ +} __attribute__((__packed__)); + +/* Link Sub-TLV: Low/High Link Delay */ +#define TE_SUBTLV_MM_DELAY 34 +#define TE_SUBTLV_MM_DELAY_SIZE 8 +struct te_subtlv_mm_delay +{ + struct subtlv_header header; /* Value length is 8 bytes. */ + u_int32_t low; /* low delay in micro-seconds only 24 bits => 0 ... 16777215 + with Anomalous Bit (A) as Upper most bit */ + u_int32_t high; /* high delay in micro-seconds only 24 bits => 0 ... 16777215 */ +} __attribute__((__packed__)); + +/* Link Sub-TLV: Link Delay Variation i.e. Jitter */ +#define TE_SUBTLV_DELAY_VAR 35 +struct te_subtlv_delay_var +{ + struct subtlv_header header; /* Value length is 4 bytes. */ + u_int32_t value; /* interval in micro-seconds only 24 bits => 0 ... 16777215 */ +} __attribute__((__packed__)); + +/* Link Sub-TLV: Routine Unidirectional Link Packet Loss */ +#define TE_SUBTLV_PKT_LOSS 36 +struct te_subtlv_pkt_loss +{ + struct subtlv_header header; /* Value length is 4 bytes. */ + u_int32_t value; /* in percentage of total traffic only 24 bits (2^24 - 2) + with Anomalous Bit (A) as Upper most bit */ +} __attribute__((__packed__)); + +/* Link Sub-TLV: Unidirectional Residual Bandwidth */ /* Optional */ +#define TE_SUBTLV_RES_BW 37 +struct te_subtlv_res_bw +{ + struct subtlv_header header; /* Value length is 4 bytes. */ + float value; /* bandwidth in IEEE floating point format with units in bytes per second */ +} __attribute__((__packed__)); + +/* Link Sub-TLV: Unidirectional Available Bandwidth */ /* Optional */ +#define TE_SUBTLV_AVA_BW 38 +struct te_subtlv_ava_bw +{ + struct subtlv_header header; /* Value length is 4 octets. */ + float value; /* bandwidth in IEEE floating point format with units in bytes per second */ +} __attribute__((__packed__)); + +/* Link Sub-TLV: Unidirectional Utilized Bandwidth */ /* Optional */ +#define TE_SUBTLV_USE_BW 39 +struct te_subtlv_use_bw +{ + struct subtlv_header header; /* Value length is 4 octets. */ + float value; /* bandwidth in IEEE floating point format with units in bytes per second */ +} __attribute__((__packed__)); + +#define TE_SUBTLV_MAX 40 /* Last SUBTLV + 1 */ + +/* Following declaration concerns the MPLS-TE and LINk-TE management */ +typedef enum _status_t { disable, enable, learn } status_t; + +/* Mode for Inter-AS LSP */ /* TODO: Check how if LSP is flooded in RFC5316 */ +typedef enum _interas_mode_t { off, region, as, emulate } interas_mode_t; + +#define IS_MPLS_TE(m) (m.status == enable) +#define IS_CIRCUIT_TE(c) (c->status == enable) + +/* Following structure are internal use only. */ +struct isis_mpls_te +{ + /* Status of MPLS-TE: enable or disable */ + status_t status; + + /* L1, L1-L2, L2-Only */ + u_int8_t level; + + /* RFC5316 */ + interas_mode_t inter_as; + struct in_addr interas_areaid; + + /* Circuit list on which TE are enable */ + struct list *cir_list; + + /* MPLS_TE router ID */ + struct in_addr router_id; +}; + +extern struct isis_mpls_te isisMplsTE; + +struct mpls_te_circuit +{ + + /* Status of MPLS-TE on this interface */ + status_t status; + + /* Type of MPLS-TE circuit: STD_TE(RFC5305), INTER_AS(RFC5316), INTER_AS_EMU(RFC5316 emulated) */ + u_int8_t type; + + /* Total size of sub_tlvs */ + u_char length; + + /* Store subTLV in network byte order. */ + /* RFC5305 */ + struct te_subtlv_admin_grp admin_grp; + /* RFC5307 */ + struct te_subtlv_llri llri; + /* RFC5305 */ + struct te_subtlv_local_ipaddr local_ipaddr; + struct te_subtlv_rmt_ipaddr rmt_ipaddr; + struct te_subtlv_max_bw max_bw; + struct te_subtlv_max_rsv_bw max_rsv_bw; + struct te_subtlv_unrsv_bw unrsv_bw; + struct te_subtlv_te_metric te_metric; + /* RFC5316 */ + struct te_subtlv_ras ras; + struct te_subtlv_rip rip; + /* draft-ietf-isis-te-metric-extension */ + struct te_subtlv_av_delay av_delay; + struct te_subtlv_mm_delay mm_delay; + struct te_subtlv_delay_var delay_var; + struct te_subtlv_pkt_loss pkt_loss; + struct te_subtlv_res_bw res_bw; + struct te_subtlv_ava_bw ava_bw; + struct te_subtlv_use_bw use_bw; +}; + +/* Prototypes. */ +void isis_mpls_te_init (void); +struct mpls_te_circuit *mpls_te_circuit_new(void); +void mpls_te_print_detail(struct vty *, struct te_is_neigh *); +void set_circuitparams_local_ipaddr (struct mpls_te_circuit *, struct in_addr); +void set_circuitparams_rmt_ipaddr (struct mpls_te_circuit *, struct in_addr); +u_char subtlvs_len (struct mpls_te_circuit *); +u_char add_te_subtlvs(u_char *, struct mpls_te_circuit *); +u_char build_te_subtlvs(u_char *, struct isis_circuit *); +void isis_link_params_update(struct isis_circuit *, struct interface *); +void isis_mpls_te_update(struct interface *); +void isis_mpls_te_config_write_router (struct vty *); + +#endif /* _ZEBRA_ISIS_MPLS_TE_H */ diff --git a/isisd/isis_tlv.c b/isisd/isis_tlv.c index 3fc717e34..1d29d7828 100644 --- a/isisd/isis_tlv.c +++ b/isisd/isis_tlv.c @@ -42,13 +42,7 @@ #include "isisd/isis_misc.h" #include "isisd/isis_pdu.h" #include "isisd/isis_lsp.h" - -extern struct isis *isis; - -/* - * Prototypes. - */ -int add_tlv (u_char, u_char, u_char *, struct stream *); +#include "isisd/isis_te.h" void free_tlv (void *val) @@ -75,10 +69,10 @@ free_tlvs (struct tlvs *tlvs) list_delete (tlvs->es_neighs); if (tlvs->lsp_entries) list_delete (tlvs->lsp_entries); - if (tlvs->lan_neighs) - list_delete (tlvs->lan_neighs); if (tlvs->prefix_neighs) list_delete (tlvs->prefix_neighs); + if (tlvs->lan_neighs) + list_delete (tlvs->lan_neighs); if (tlvs->ipv4_addrs) list_delete (tlvs->ipv4_addrs); if (tlvs->ipv4_int_reachs) @@ -93,7 +87,9 @@ free_tlvs (struct tlvs *tlvs) if (tlvs->ipv6_reachs) list_delete (tlvs->ipv6_reachs); #endif /* HAVE_IPV6 */ - + + memset (tlvs, 0, sizeof (struct tlvs)); + return; } @@ -103,7 +99,7 @@ free_tlvs (struct tlvs *tlvs) */ int parse_tlvs (char *areatag, u_char * stream, int size, u_int32_t * expected, - u_int32_t * found, struct tlvs *tlvs) + u_int32_t * found, struct tlvs *tlvs, u_int32_t *auth_tlv_offset) { u_char type, length; struct lan_neigh *lan_nei; @@ -120,9 +116,8 @@ parse_tlvs (char *areatag, u_char * stream, int size, u_int32_t * expected, struct ipv6_reachability *ipv6_reach; int prefix_octets; #endif /* HAVE_IPV6 */ - u_char virtual; int value_len, retval = ISIS_OK; - u_char *pnt = stream; + u_char *start = stream, *pnt = stream, *endpnt; *found = 0; memset (tlvs, 0, sizeof (struct tlvs)); @@ -184,7 +179,6 @@ parse_tlvs (char *areatag, u_char * stream, int size, u_int32_t * expected, * | Virtual Flag | * +-------+-------+-------+-------+-------+-------+-------+-------+ */ - virtual = *pnt; /* FIXME: what is the use for this? */ pnt++; value_len++; /* +-------+-------+-------+-------+-------+-------+-------+-------+ @@ -236,9 +230,23 @@ parse_tlvs (char *areatag, u_char * stream, int size, u_int32_t * expected, while (length > value_len) { te_is_nei = (struct te_is_neigh *) pnt; - value_len += 11; - pnt += 11; - /* FIXME - subtlvs are handled here, for now we skip */ + value_len += IS_NEIGHBOURS_LEN; + pnt += IS_NEIGHBOURS_LEN; + /* FIXME - subtlvs are handled here, for now we skip */ + /* FIXME: All TE SubTLVs are not necessary present in LSP PDU. */ + /* So, it must be copied in a new te_is_neigh structure */ + /* rather than just initialize pointer to the original LSP PDU */ + /* to avoid consider the rest of lspdu as subTLVs or buffer overflow */ + if (IS_MPLS_TE(isisMplsTE)) + { + struct te_is_neigh *new = XCALLOC(MTYPE_ISIS_TLV, sizeof(struct te_is_neigh)); + memcpy(new->neigh_id, te_is_nei->neigh_id, ISIS_SYS_ID_LEN + 1); + memcpy(new->te_metric, te_is_nei->te_metric, 3); + new->sub_tlvs_length = te_is_nei->sub_tlvs_length; + memcpy(new->sub_tlvs, pnt, te_is_nei->sub_tlvs_length); + te_is_nei = new; + } + /* Skip SUB TLVs payload */ value_len += te_is_nei->sub_tlvs_length; pnt += te_is_nei->sub_tlvs_length; @@ -443,14 +451,22 @@ parse_tlvs (char *areatag, u_char * stream, int size, u_int32_t * expected, if (*expected & TLVFLAG_AUTH_INFO) { tlvs->auth_info.type = *pnt; - tlvs->auth_info.len = length-1; + if (length == 0) + { + zlog_warn ("ISIS-TLV (%s): TLV (type %d, length %d) " + "incorrect.", areatag, type, length); + return ISIS_WARNING; + } + --length; + tlvs->auth_info.len = length; pnt++; - memcpy (tlvs->auth_info.passwd, pnt, length - 1); - /* Fill authentication with 0 for later computation - * of MD5 (RFC 5304, 2) - */ - memset (pnt, 0, length - 1); - pnt += length - 1; + memcpy (tlvs->auth_info.passwd, pnt, length); + /* Return the authentication tlv pos for later computation + * of MD5 (RFC 5304, 2) + */ + if (auth_tlv_offset) + *auth_tlv_offset += (pnt - start - 3); + pnt += length; } else { @@ -581,11 +597,20 @@ parse_tlvs (char *areatag, u_char * stream, int size, u_int32_t * expected, zlog_debug ("ISIS-TLV (%s): IPv4 extended Reachability length %d", areatag, length); #endif /* EXTREME_TLV_DEBUG */ + endpnt = pnt + length; if (*expected & TLVFLAG_TE_IPV4_REACHABILITY) { while (length > value_len) { te_ipv4_reach = (struct te_ipv4_reachability *) pnt; + if ((te_ipv4_reach->control & 0x3F) > IPV4_MAX_BITLEN) + { + zlog_warn ("ISIS-TLV (%s): invalid IPv4 extended reach" + "ability prefix length %d", areatag, + te_ipv4_reach->control & 0x3F); + retval = ISIS_WARNING; + break; + } if (!tlvs->te_ipv4_reachs) tlvs->te_ipv4_reachs = list_new (); listnode_add (tlvs->te_ipv4_reachs, te_ipv4_reach); @@ -597,10 +622,8 @@ parse_tlvs (char *areatag, u_char * stream, int size, u_int32_t * expected, ((((te_ipv4_reach->control & 0x3F) - 1) >> 3) + 1) : 0); } } - else - { - pnt += length; - } + + pnt = endpnt; break; #ifdef HAVE_IPV6 @@ -645,11 +668,22 @@ parse_tlvs (char *areatag, u_char * stream, int size, u_int32_t * expected, * +---------------------------------------------------------------+ */ *found |= TLVFLAG_IPV6_REACHABILITY; + endpnt = pnt + length; + if (*expected & TLVFLAG_IPV6_REACHABILITY) { while (length > value_len) { ipv6_reach = (struct ipv6_reachability *) pnt; + if (ipv6_reach->prefix_len > IPV6_MAX_BITLEN) + { + zlog_warn ("ISIS-TLV (%s): invalid IPv6 extended reach" + "ability prefix length %d", areatag, + ipv6_reach->prefix_len); + retval = ISIS_WARNING; + break; + } + prefix_octets = ((ipv6_reach->prefix_len + 7) / 8); value_len += prefix_octets + 6; pnt += prefix_octets + 6; @@ -659,10 +693,8 @@ parse_tlvs (char *areatag, u_char * stream, int size, u_int32_t * expected, listnode_add (tlvs->ipv6_reachs, ipv6_reach); } } - else - { - pnt += length; - } + + pnt = endpnt; break; #endif /* HAVE_IPV6 */ @@ -693,6 +725,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 @@ -722,7 +755,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; } @@ -734,10 +766,14 @@ parse_tlvs (char *areatag, u_char * stream, int size, u_int32_t * expected, int add_tlv (u_char tag, u_char len, u_char * value, struct stream *stream) { - - if (STREAM_SIZE (stream) - stream_get_endp (stream) < (unsigned) len + 2) + if ((stream_get_size (stream) - stream_get_endp (stream)) < + (((unsigned)len) + 2)) { - zlog_warn ("No room for TLV of type %d", tag); + zlog_warn ("No room for TLV of type %d " + "(total size %d available %d required %d)", + tag, (int)stream_get_size (stream), + (int)(stream_get_size (stream) - stream_get_endp (stream)), + len+2); return ISIS_WARNING; } @@ -745,7 +781,7 @@ add_tlv (u_char tag, u_char len, u_char * value, struct stream *stream) stream_putc (stream, len); /* LENGTH */ stream_put (stream, value, (int) len); /* VALUE */ -#ifdef EXTREME_TLV_DEBUG +#ifdef EXTREME_DEBUG zlog_debug ("Added TLV %d len %d", tag, len); #endif /* EXTREME DEBUG */ return ISIS_OK; @@ -824,8 +860,8 @@ tlv_add_te_is_neighs (struct list *te_is_neighs, struct stream *stream) for (ALL_LIST_ELEMENTS_RO (te_is_neighs, node, te_is_neigh)) { - /* FIXME: This will be wrong if we are going to add TE sub TLVs. */ - if (pos - value + IS_NEIGHBOURS_LEN > 255) + /* FIXME: Check if Total SubTLVs size doesn't exceed 255 */ + if (pos - value + IS_NEIGHBOURS_LEN + te_is_neigh->sub_tlvs_length > 255) { retval = add_tlv (TE_IS_NEIGHBOURS, pos - value, value, stream); if (retval != ISIS_OK) @@ -837,9 +873,15 @@ tlv_add_te_is_neighs (struct list *te_is_neighs, struct stream *stream) pos += ISIS_SYS_ID_LEN + 1; memcpy (pos, te_is_neigh->te_metric, 3); pos += 3; - /* Sub TLVs length. */ - *pos = 0; + /* Set the total size of Sub TLVs */ + *pos = te_is_neigh->sub_tlvs_length; pos++; + /* Copy Sub TLVs if any */ + if (te_is_neigh->sub_tlvs_length > 0) + { + memcpy (pos, te_is_neigh->sub_tlvs, te_is_neigh->sub_tlvs_length); + pos += te_is_neigh->sub_tlvs_length; + } } return add_tlv (TE_IS_NEIGHBOURS, pos - value, value, stream); @@ -877,7 +919,7 @@ tlv_add_nlpid (struct nlpids *nlpids, struct stream *stream) } int -tlv_add_authinfo (char auth_type, char auth_len, u_char *auth_value, +tlv_add_authinfo (u_char auth_type, u_char auth_len, u_char *auth_value, struct stream *stream) { u_char value[255]; @@ -903,16 +945,14 @@ tlv_add_ip_addrs (struct list *ip_addrs, struct stream *stream) struct prefix_ipv4 *ipv4; u_char value[255]; u_char *pos = value; - int retval; for (ALL_LIST_ELEMENTS_RO (ip_addrs, node, ipv4)) { 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; @@ -973,8 +1013,8 @@ tlv_add_lsp_entries (struct list *lsps, struct stream *stream) return add_tlv (LSP_ENTRIES, pos - value, value, stream); } -int -tlv_add_ipv4_reachs (struct list *ipv4_reachs, struct stream *stream) +static int +tlv_add_ipv4_reachs (u_char tag, struct list *ipv4_reachs, struct stream *stream) { struct listnode *node; struct ipv4_reachability *reach; @@ -987,7 +1027,7 @@ tlv_add_ipv4_reachs (struct list *ipv4_reachs, struct stream *stream) if (pos - value + IPV4_REACH_LEN > 255) { retval = - add_tlv (IPV4_INT_REACHABILITY, pos - value, value, stream); + add_tlv (tag, pos - value, value, stream); if (retval != ISIS_OK) return retval; pos = value; @@ -1006,10 +1046,22 @@ tlv_add_ipv4_reachs (struct list *ipv4_reachs, struct stream *stream) pos += IPV4_MAX_BYTELEN; } + return add_tlv (tag, pos - value, value, stream); +} - return add_tlv (IPV4_INT_REACHABILITY, pos - value, value, stream); +int +tlv_add_ipv4_int_reachs (struct list *ipv4_reachs, struct stream *stream) +{ + return tlv_add_ipv4_reachs(IPV4_INT_REACHABILITY, ipv4_reachs, stream); } +int +tlv_add_ipv4_ext_reachs (struct list *ipv4_reachs, struct stream *stream) +{ + return tlv_add_ipv4_reachs(IPV4_EXT_REACHABILITY, ipv4_reachs, stream); +} + + int tlv_add_te_ipv4_reachs (struct list *te_ipv4_reachs, struct stream *stream) { @@ -1027,7 +1079,7 @@ tlv_add_te_ipv4_reachs (struct list *te_ipv4_reachs, struct stream *stream) if (pos - value + (5 + prefix_size) > 255) { retval = - add_tlv (IPV4_INT_REACHABILITY, pos - value, value, stream); + add_tlv (TE_IPV4_REACHABILITY, pos - value, value, stream); if (retval != ISIS_OK) return retval; pos = value; @@ -1110,7 +1162,7 @@ tlv_add_padding (struct stream *stream) /* * How many times can we add full padding ? */ - fullpads = (STREAM_SIZE (stream) - stream_get_endp (stream)) / 257; + fullpads = (stream_get_size (stream) - stream_get_endp (stream)) / 257; for (i = 0; i < fullpads; i++) { if (!stream_putc (stream, (u_char) PADDING)) /* TAG */ @@ -1120,7 +1172,7 @@ tlv_add_padding (struct stream *stream) stream_put (stream, NULL, 255); /* zero padding */ } - left = STREAM_SIZE (stream) - stream_get_endp (stream); + left = stream_get_size (stream) - stream_get_endp (stream); if (left < 2) return ISIS_OK; diff --git a/isisd/isis_tlv.h b/isisd/isis_tlv.h index fc9f35f8a..5a39d564d 100644 --- a/isisd/isis_tlv.h +++ b/isisd/isis_tlv.h @@ -30,7 +30,7 @@ * Name Value IIH LSP SNP Status * LAN * ____________________________________________________________________________ - * + * * Area Addresses 1 y y n ISO10589 * IIS Neighbors 2 n y n ISO10589 * ES Neighbors 3 n y n ISO10589 @@ -39,52 +39,55 @@ * LSP Entries 9 n n y ISO10589 * Authentication 10 y y y ISO10589, RFC3567 * Checksum 12 y n y RFC3358 - * TE IS Reachability 22 n y n RFC3784 + * Extended IS Reachability 22 n y n RFC5305 * IS Alias 24 n y n RFC3786 * IP Int. Reachability 128 n y n RFC1195 * Protocols Supported 129 y y n RFC1195 * IP Ext. Reachability 130 n y n RFC1195 * IDRPI 131 n y y RFC1195 * IP Interface Address 132 y y n RFC1195 - * TE Router ID 134 n y n RFC3784 - * Extended IP Reachability 135 n y n RFC3784 + * TE Router ID 134 n y n RFC5305 + * Extended IP Reachability 135 n y n RFC5305 * Dynamic Hostname 137 n y n RFC2763 - * Shared Risk Link Group 138 n y y draft-ietf-isis-gmpls-extensions + * Shared Risk Link Group 138 n y y RFC5307 + * Inter-AS Reachability 141 n y n RFC5316 * Restart TLV 211 y n n RFC3847 - * MT IS Reachability 222 n y n draft-ietf-isis-wg-multi-topology - * MT Supported 229 y y n draft-ietf-isis-wg-multi-topology - * IPv6 Interface Address 232 y y n draft-ietf-isis_ipv6 - * MT IP Reachability 235 n y n draft-ietf-isis-wg-multi-topology - * IPv6 IP Reachability 236 n y n draft-ietf-isis_ipv6 - * MT IPv6 IP Reachability 237 n y n draft-ietf-isis-wg-multi-topology + * MT IS Reachability 222 n y n RFC5120 + * MT Supported 229 y y n RFC5120 + * IPv6 Interface Address 232 y y n RFC5308 + * MT IP Reachability 235 n y n RFC5120 + * IPv6 IP Reachability 236 n y n RFC5308 + * MT IPv6 IP Reachability 237 n y n RFC5120 * P2P Adjacency State 240 y n n RFC3373 * IIH Sequence Number 241 y n n draft-shen-isis-iih-sequence - * Router Capability 242 - - - draft-ietf-isis-caps + * Router Capability 242 n y n RFC4971 + * * - * - * IS Reachability sub-TLVs we (should) support. + * IS Reachability sub-TLVs we support (See isis_te.[c,h]) * ____________________________________________________________________________ * Name Value Status * ____________________________________________________________________________ - * Administartive group (color) 3 RFC3784 - * Link Local/Remote Identifiers 4 draft-ietf-isis-gmpls-extensions - * IPv4 interface address 6 RFC3784 - * IPv4 neighbor address 8 RFC3784 - * Maximum link bandwidth 9 RFC3784 - * Reservable link bandwidth 10 RFC3784 - * Unreserved bandwidth 11 RFC3784 - * TE Default metric 18 RFC3784 - * Link Protection Type 20 draft-ietf-isis-gmpls-extensions - * Interface Switching Capability 21 draft-ietf-isis-gmpls-extensions + * Administartive group (color) 3 RFC5305 + * Link Local/Remote Identifiers 4 RFC5307 + * IPv4 interface address 6 RFC5305 + * IPv4 neighbor address 8 RFC5305 + * Maximum link bandwidth 9 RFC5305 + * Reservable link bandwidth 10 RFC5305 + * Unreserved bandwidth 11 RFC5305 + * TE Default metric 18 RFC5305 + * Link Protection Type 20 RFC5307 + * Interface Switching Capability 21 RFC5307 + * Remote AS number 24 RFC5316 + * IPv4 Remote ASBR identifier 25 RFC5316 + * * - * * IP Reachability sub-TLVs we (should) support. * ____________________________________________________________________________ * Name Value Status * ____________________________________________________________________________ - * 32bit administrative tag 1 draft-ietf-isis-admin-tags - * 64bit administrative tag 2 draft-ietf-isis-admin-tags - * Management prefix color 117 draft-ietf-isis-wg-multi-topology + * 32bit administrative tag 1 RFC5130 + * 64bit administrative tag 2 RFC5130 + * Management prefix color 117 RFC5120 */ #define AREA_ADDRESSES 1 @@ -109,12 +112,20 @@ #define IPV6_ADDR 232 #define IPV6_REACHABILITY 236 #define WAY3_HELLO 240 +#define ROUTER_INFORMATION 242 + +#define AUTH_INFO_HDRLEN 3 + +#define MAX_TLV_LEN 255 #define IS_NEIGHBOURS_LEN (ISIS_SYS_ID_LEN + 5) #define LAN_NEIGHBOURS_LEN 6 #define LSP_ENTRIES_LEN (10 + ISIS_SYS_ID_LEN) /* FIXME: should be entry */ #define IPV4_REACH_LEN 12 #define IPV6_REACH_LEN 22 +#define TE_IPV4_REACH_LEN 9 + +#define MAX_SUBTLV_SIZE 256 /* struct for neighbor */ struct is_neigh @@ -123,14 +134,29 @@ struct is_neigh u_char neigh_id[ISIS_SYS_ID_LEN + 1]; }; -/* struct for te is neighbor */ +/* struct for te metric */ struct te_is_neigh { u_char neigh_id[ISIS_SYS_ID_LEN + 1]; u_char te_metric[3]; u_char sub_tlvs_length; + /* Theorical Maximum SubTLVs is 256 because the sub_tlvs_length is 8 bits */ + /* Practically, 118 bytes are necessary to store all supported TE parameters */ + /* FIXME: A pointer will use less memory, but need to be free */ + /* which is hard to fix, especially within free_tlvs() function */ + /* and malloc() / free() as a CPU cost compared to the memory usage */ + u_char sub_tlvs[MAX_SUBTLV_SIZE]; /* SUB TLVs storage */ }; +/* Decode and encode three-octet metric into host byte order integer */ +#define GET_TE_METRIC(t) \ + (((unsigned)(t)->te_metric[0]<<16) | ((t)->te_metric[1]<<8) | \ + (t)->te_metric[2]) +#define SET_TE_METRIC(t, m) \ + (((t)->te_metric[0] = (m) >> 16), \ + ((t)->te_metric[1] = (m) >> 8), \ + ((t)->te_metric[2] = (m))) + /* struct for es neighbors */ struct es_neigh { @@ -213,22 +239,28 @@ struct ipv6_reachability u_char prefix_len; u_char prefix[16]; }; -#endif /* HAVE_IPV6 */ /* bits in control_info */ #define CTRL_INFO_DIRECTION 0x80 -#define DIRECTION_UP 0 -#define DIRECTION_DOWN 1 +#define DIRECTION_UP 0x00 +#define DIRECTION_DOWN 0x80 + #define CTRL_INFO_DISTRIBUTION 0x40 -#define DISTRIBUTION_INTERNAL 0 -#define DISTRIBUTION_EXTERNAL 1 +#define DISTRIBUTION_INTERNAL 0x00 +#define DISTRIBUTION_EXTERNAL 0x40 + #define CTRL_INFO_SUBTLVS 0x20 +#endif /* HAVE_IPV6 */ /* * Pointer to each tlv type, filled by parse_tlvs() */ struct tlvs { + struct checksum *checksum; + struct hostname *hostname; + struct nlpids *nlpids; + struct te_router_id *router_id; struct list *area_addrs; struct list *is_neighs; struct list *te_is_neighs; @@ -236,14 +268,10 @@ struct tlvs struct list *lsp_entries; struct list *prefix_neighs; struct list *lan_neighs; - struct checksum *checksum; - struct nlpids *nlpids; struct list *ipv4_addrs; struct list *ipv4_int_reachs; struct list *ipv4_ext_reachs; struct list *te_ipv4_reachs; - struct hostname *hostname; - struct te_router_id *router_id; #ifdef HAVE_IPV6 struct list *ipv6_addrs; struct list *ipv6_reachs; @@ -281,7 +309,9 @@ struct tlvs void init_tlvs (struct tlvs *tlvs, uint32_t expected); void free_tlvs (struct tlvs *tlvs); int parse_tlvs (char *areatag, u_char * stream, int size, - u_int32_t * expected, u_int32_t * found, struct tlvs *tlvs); + u_int32_t * expected, u_int32_t * found, struct tlvs *tlvs, + u_int32_t * auth_tlv_offset); +int add_tlv (u_char, u_char, u_char *, struct stream *); void free_tlv (void *val); int tlv_add_area_addrs (struct list *area_addrs, struct stream *stream); @@ -290,14 +320,15 @@ int tlv_add_te_is_neighs (struct list *te_is_neighs, struct stream *stream); int tlv_add_lan_neighs (struct list *lan_neighs, struct stream *stream); int tlv_add_nlpid (struct nlpids *nlpids, struct stream *stream); int tlv_add_checksum (struct checksum *checksum, struct stream *stream); -int tlv_add_authinfo (char auth_type, char authlen, u_char *auth_value, +int tlv_add_authinfo (u_char auth_type, u_char authlen, u_char *auth_value, struct stream *stream); int tlv_add_ip_addrs (struct list *ip_addrs, struct stream *stream); int tlv_add_in_addr (struct in_addr *, struct stream *stream, u_char tag); int tlv_add_dynamic_hostname (struct hostname *hostname, struct stream *stream); int tlv_add_lsp_entries (struct list *lsps, struct stream *stream); -int tlv_add_ipv4_reachs (struct list *ipv4_reachs, struct stream *stream); +int tlv_add_ipv4_int_reachs (struct list *ipv4_reachs, struct stream *stream); +int tlv_add_ipv4_ext_reachs (struct list *ipv4_reachs, struct stream *stream); int tlv_add_te_ipv4_reachs (struct list *te_ipv4_reachs, struct stream *stream); #ifdef HAVE_IPV6 int tlv_add_ipv6_addrs (struct list *ipv6_addrs, struct stream *stream); diff --git a/isisd/isis_vty.c b/isisd/isis_vty.c new file mode 100644 index 000000000..4148eb55b --- /dev/null +++ b/isisd/isis_vty.c @@ -0,0 +1,2428 @@ +/* + * IS-IS Rout(e)ing protocol - isis_circuit.h + * + * Copyright (C) 2001,2002 Sampo Saaristo + * Tampere University of Technology + * Institute of Communications Engineering + * Copyright (C) 2016 David Lamparter, for NetDEF, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public Licenseas 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. + */ + +#include +#include + +#include "isis_circuit.h" +#include "isis_csm.h" +#include "isis_misc.h" +#include "isisd.h" + +static struct isis_circuit * +isis_circuit_lookup (struct vty *vty) +{ + struct interface *ifp; + struct isis_circuit *circuit; + + ifp = (struct interface *) vty->index; + if (!ifp) + { + vty_out (vty, "Invalid interface %s", VTY_NEWLINE); + return NULL; + } + + circuit = circuit_scan_by_ifp (ifp); + if (!circuit) + { + vty_out (vty, "ISIS is not enabled on circuit %s%s", + ifp->name, VTY_NEWLINE); + return NULL; + } + + return circuit; +} + +DEFUN (ip_router_isis, + ip_router_isis_cmd, + "(ip|ipv6) router isis WORD", + "Interface Internet Protocol config commands\n" + "IP router interface commands\n" + "IS-IS Routing for IP\n" + "Routing process tag\n") +{ + struct interface *ifp; + struct isis_circuit *circuit; + struct isis_area *area; + const char *af = argv[0]; + const char *area_tag = argv[1]; + + ifp = (struct interface *) vty->index; + assert (ifp); + + /* Prevent more than one area per circuit */ + circuit = circuit_scan_by_ifp (ifp); + if (circuit && circuit->area) + { + if (strcmp (circuit->area->area_tag, area_tag)) + { + vty_out (vty, "ISIS circuit is already defined on %s%s", + circuit->area->area_tag, VTY_NEWLINE); + return CMD_ERR_NOTHING_TODO; + } + } + + area = isis_area_lookup (area_tag); + if (!area) + area = isis_area_create (area_tag); + + if (!circuit || !circuit->area) { + circuit = isis_circuit_create (area, ifp); + + if (circuit->state != C_STATE_CONF && circuit->state != C_STATE_UP) + { + vty_out(vty, "Couldn't bring up interface, please check log.%s", VTY_NEWLINE); + return CMD_WARNING; + } + } + + bool ip = circuit->ip_router, ipv6 = circuit->ipv6_router; + if (af[2] != '\0') + ipv6 = true; + else + ip = true; + + isis_circuit_af_set (circuit, ip, ipv6); + return CMD_SUCCESS; +} + +DEFUN (no_ip_router_isis, + no_ip_router_isis_cmd, + "no (ip|ipv6) router isis WORD", + NO_STR + "Interface Internet Protocol config commands\n" + "IP router interface commands\n" + "IS-IS Routing for IP\n" + "Routing process tag\n") +{ + struct interface *ifp; + struct isis_area *area; + struct isis_circuit *circuit; + const char *af = argv[0]; + const char *area_tag = argv[1]; + + ifp = (struct interface *) vty->index; + if (!ifp) + { + vty_out (vty, "Invalid interface %s", VTY_NEWLINE); + return CMD_ERR_NO_MATCH; + } + + area = isis_area_lookup (area_tag); + if (!area) + { + vty_out (vty, "Can't find ISIS instance %s%s", + argv[0], VTY_NEWLINE); + return CMD_ERR_NO_MATCH; + } + + circuit = circuit_lookup_by_ifp (ifp, area->circuit_list); + if (!circuit) + { + vty_out (vty, "ISIS is not enabled on circuit %s%s", + ifp->name, VTY_NEWLINE); + return CMD_ERR_NO_MATCH; + } + + bool ip = circuit->ip_router, ipv6 = circuit->ipv6_router; + if (af[2] != '\0') + ipv6 = false; + else + ip = false; + + isis_circuit_af_set (circuit, ip, ipv6); + return CMD_SUCCESS; +} + +DEFUN (isis_passive, + isis_passive_cmd, + "isis passive", + "IS-IS commands\n" + "Configure the passive mode for interface\n") +{ + struct isis_circuit *circuit = isis_circuit_lookup (vty); + if (!circuit) + return CMD_ERR_NO_MATCH; + + isis_circuit_passive_set (circuit, 1); + return CMD_SUCCESS; +} + +DEFUN (no_isis_passive, + no_isis_passive_cmd, + "no isis passive", + NO_STR + "IS-IS commands\n" + "Configure the passive mode for interface\n") +{ + struct isis_circuit *circuit = isis_circuit_lookup (vty); + if (!circuit) + return CMD_ERR_NO_MATCH; + + if (if_is_loopback (circuit->interface)) + { + vty_out (vty, "Can't set no passive for loopback interface%s", + VTY_NEWLINE); + return CMD_ERR_AMBIGUOUS; + } + + isis_circuit_passive_set (circuit, 0); + return CMD_SUCCESS; +} + +DEFUN (isis_circuit_type, + isis_circuit_type_cmd, + "isis circuit-type (level-1|level-1-2|level-2-only)", + "IS-IS commands\n" + "Configure circuit type for interface\n" + "Level-1 only adjacencies are formed\n" + "Level-1-2 adjacencies are formed\n" + "Level-2 only adjacencies are formed\n") +{ + int is_type; + struct isis_circuit *circuit = isis_circuit_lookup (vty); + if (!circuit) + return CMD_ERR_NO_MATCH; + + is_type = string2circuit_t (argv[0]); + if (!is_type) + { + vty_out (vty, "Unknown circuit-type %s", VTY_NEWLINE); + return CMD_ERR_AMBIGUOUS; + } + + if (circuit->state == C_STATE_UP && + circuit->area->is_type != IS_LEVEL_1_AND_2 && + circuit->area->is_type != is_type) + { + vty_out (vty, "Invalid circuit level for area %s.%s", + circuit->area->area_tag, VTY_NEWLINE); + return CMD_ERR_AMBIGUOUS; + } + isis_circuit_is_type_set (circuit, is_type); + + return CMD_SUCCESS; +} + +DEFUN (no_isis_circuit_type, + no_isis_circuit_type_cmd, + "no isis circuit-type (level-1|level-1-2|level-2-only)", + NO_STR + "IS-IS commands\n" + "Configure circuit type for interface\n" + "Level-1 only adjacencies are formed\n" + "Level-1-2 adjacencies are formed\n" + "Level-2 only adjacencies are formed\n") +{ + int is_type; + struct isis_circuit *circuit = isis_circuit_lookup (vty); + if (!circuit) + return CMD_ERR_NO_MATCH; + + /* + * Set the circuits level to its default value + */ + if (circuit->state == C_STATE_UP) + is_type = circuit->area->is_type; + else + is_type = IS_LEVEL_1_AND_2; + isis_circuit_is_type_set (circuit, is_type); + + return CMD_SUCCESS; +} + +DEFUN (isis_network, + isis_network_cmd, + "isis network point-to-point", + "IS-IS commands\n" + "Set network type\n" + "point-to-point network type\n") +{ + struct isis_circuit *circuit = isis_circuit_lookup (vty); + if (!circuit) + return CMD_ERR_NO_MATCH; + + if (isis_circuit_circ_type_set(circuit, CIRCUIT_T_P2P)) + { + vty_out (vty, "isis network point-to-point " + "is valid only on broadcast interfaces%s", + VTY_NEWLINE); + return CMD_ERR_AMBIGUOUS; + } + + return CMD_SUCCESS; +} + +DEFUN (no_isis_network, + no_isis_network_cmd, + "no isis network point-to-point", + NO_STR + "IS-IS commands\n" + "Set network type for circuit\n" + "point-to-point network type\n") +{ + struct isis_circuit *circuit = isis_circuit_lookup (vty); + if (!circuit) + return CMD_ERR_NO_MATCH; + + if (isis_circuit_circ_type_set(circuit, CIRCUIT_T_BROADCAST)) + { + vty_out (vty, "isis network point-to-point " + "is valid only on broadcast interfaces%s", + VTY_NEWLINE); + return CMD_ERR_AMBIGUOUS; + } + + return CMD_SUCCESS; +} + +DEFUN (isis_passwd, + isis_passwd_cmd, + "isis password (md5|clear) WORD", + "IS-IS commands\n" + "Configure the authentication password for a circuit\n" + "HMAC-MD5 authentication\n" + "Cleartext password\n" + "Circuit password\n") +{ + struct isis_circuit *circuit = isis_circuit_lookup (vty); + int rv; + if (!circuit) + return CMD_ERR_NO_MATCH; + + if (argv[0][0] == 'm') + rv = isis_circuit_passwd_hmac_md5_set(circuit, argv[1]); + else + rv = isis_circuit_passwd_cleartext_set(circuit, argv[1]); + if (rv) + { + vty_out (vty, "Too long circuit password (>254)%s", VTY_NEWLINE); + return CMD_ERR_AMBIGUOUS; + } + + return CMD_SUCCESS; +} + +DEFUN (no_isis_passwd, + no_isis_passwd_cmd, + "no isis password", + NO_STR + "IS-IS commands\n" + "Configure the authentication password for a circuit\n") +{ + struct isis_circuit *circuit = isis_circuit_lookup (vty); + if (!circuit) + return CMD_ERR_NO_MATCH; + + isis_circuit_passwd_unset(circuit); + + return CMD_SUCCESS; +} + +ALIAS (no_isis_passwd, + no_isis_passwd_arg_cmd, + "no isis password (md5|clear) WORD", + NO_STR + "IS-IS commands\n" + "Configure the authentication password for a circuit\n" + "HMAC-MD5 authentication\n" + "Cleartext password\n" + "Circuit password\n") + +DEFUN (isis_priority, + isis_priority_cmd, + "isis priority <0-127>", + "IS-IS commands\n" + "Set priority for Designated Router election\n" + "Priority value\n") +{ + int prio; + struct isis_circuit *circuit = isis_circuit_lookup (vty); + if (!circuit) + return CMD_ERR_NO_MATCH; + + prio = atoi (argv[0]); + if (prio < MIN_PRIORITY || prio > MAX_PRIORITY) + { + vty_out (vty, "Invalid priority %d - should be <0-127>%s", + prio, VTY_NEWLINE); + return CMD_ERR_AMBIGUOUS; + } + + circuit->priority[0] = prio; + circuit->priority[1] = prio; + + return CMD_SUCCESS; +} + +DEFUN (no_isis_priority, + no_isis_priority_cmd, + "no isis priority", + NO_STR + "IS-IS commands\n" + "Set priority for Designated Router election\n") +{ + struct isis_circuit *circuit = isis_circuit_lookup (vty); + if (!circuit) + return CMD_ERR_NO_MATCH; + + circuit->priority[0] = DEFAULT_PRIORITY; + circuit->priority[1] = DEFAULT_PRIORITY; + + return CMD_SUCCESS; +} + +ALIAS (no_isis_priority, + no_isis_priority_arg_cmd, + "no isis priority <0-127>", + NO_STR + "IS-IS commands\n" + "Set priority for Designated Router election\n" + "Priority value\n") + +DEFUN (isis_priority_l1, + isis_priority_l1_cmd, + "isis priority <0-127> level-1", + "IS-IS commands\n" + "Set priority for Designated Router election\n" + "Priority value\n" + "Specify priority for level-1 routing\n") +{ + int prio; + struct isis_circuit *circuit = isis_circuit_lookup (vty); + if (!circuit) + return CMD_ERR_NO_MATCH; + + prio = atoi (argv[0]); + if (prio < MIN_PRIORITY || prio > MAX_PRIORITY) + { + vty_out (vty, "Invalid priority %d - should be <0-127>%s", + prio, VTY_NEWLINE); + return CMD_ERR_AMBIGUOUS; + } + + circuit->priority[0] = prio; + + return CMD_SUCCESS; +} + +DEFUN (no_isis_priority_l1, + no_isis_priority_l1_cmd, + "no isis priority level-1", + NO_STR + "IS-IS commands\n" + "Set priority for Designated Router election\n" + "Specify priority for level-1 routing\n") +{ + struct isis_circuit *circuit = isis_circuit_lookup (vty); + if (!circuit) + return CMD_ERR_NO_MATCH; + + circuit->priority[0] = DEFAULT_PRIORITY; + + return CMD_SUCCESS; +} + +ALIAS (no_isis_priority_l1, + no_isis_priority_l1_arg_cmd, + "no isis priority <0-127> level-1", + NO_STR + "IS-IS commands\n" + "Set priority for Designated Router election\n" + "Priority value\n" + "Specify priority for level-1 routing\n") + +DEFUN (isis_priority_l2, + isis_priority_l2_cmd, + "isis priority <0-127> level-2", + "IS-IS commands\n" + "Set priority for Designated Router election\n" + "Priority value\n" + "Specify priority for level-2 routing\n") +{ + int prio; + struct isis_circuit *circuit = isis_circuit_lookup (vty); + if (!circuit) + return CMD_ERR_NO_MATCH; + + prio = atoi (argv[0]); + if (prio < MIN_PRIORITY || prio > MAX_PRIORITY) + { + vty_out (vty, "Invalid priority %d - should be <0-127>%s", + prio, VTY_NEWLINE); + return CMD_ERR_AMBIGUOUS; + } + + circuit->priority[1] = prio; + + return CMD_SUCCESS; +} + +DEFUN (no_isis_priority_l2, + no_isis_priority_l2_cmd, + "no isis priority level-2", + NO_STR + "IS-IS commands\n" + "Set priority for Designated Router election\n" + "Specify priority for level-2 routing\n") +{ + struct isis_circuit *circuit = isis_circuit_lookup (vty); + if (!circuit) + return CMD_ERR_NO_MATCH; + + circuit->priority[1] = DEFAULT_PRIORITY; + + return CMD_SUCCESS; +} + +ALIAS (no_isis_priority_l2, + no_isis_priority_l2_arg_cmd, + "no isis priority <0-127> level-2", + NO_STR + "IS-IS commands\n" + "Set priority for Designated Router election\n" + "Priority value\n" + "Specify priority for level-2 routing\n") + +/* Metric command */ +DEFUN (isis_metric, + isis_metric_cmd, + "isis metric <0-16777215>", + "IS-IS commands\n" + "Set default metric for circuit\n" + "Default metric value\n") +{ + int met; + struct isis_circuit *circuit = isis_circuit_lookup (vty); + if (!circuit) + return CMD_ERR_NO_MATCH; + + met = atoi (argv[0]); + + /* RFC3787 section 5.1 */ + if (circuit->area && circuit->area->oldmetric == 1 && + met > MAX_NARROW_LINK_METRIC) + { + vty_out (vty, "Invalid metric %d - should be <0-63> " + "when narrow metric type enabled%s", + met, VTY_NEWLINE); + return CMD_ERR_AMBIGUOUS; + } + + /* RFC4444 */ + if (circuit->area && circuit->area->newmetric == 1 && + met > MAX_WIDE_LINK_METRIC) + { + vty_out (vty, "Invalid metric %d - should be <0-16777215> " + "when wide metric type enabled%s", + met, VTY_NEWLINE); + return CMD_ERR_AMBIGUOUS; + } + + isis_circuit_metric_set (circuit, IS_LEVEL_1, met); + isis_circuit_metric_set (circuit, IS_LEVEL_2, met); + return CMD_SUCCESS; +} + +DEFUN (no_isis_metric, + no_isis_metric_cmd, + "no isis metric", + NO_STR + "IS-IS commands\n" + "Set default metric for circuit\n") +{ + struct isis_circuit *circuit = isis_circuit_lookup (vty); + if (!circuit) + return CMD_ERR_NO_MATCH; + + isis_circuit_metric_set (circuit, IS_LEVEL_1, DEFAULT_CIRCUIT_METRIC); + isis_circuit_metric_set (circuit, IS_LEVEL_2, DEFAULT_CIRCUIT_METRIC); + return CMD_SUCCESS; +} + +ALIAS (no_isis_metric, + no_isis_metric_arg_cmd, + "no isis metric <0-16777215>", + NO_STR + "IS-IS commands\n" + "Set default metric for circuit\n" + "Default metric value\n") + +DEFUN (isis_metric_l1, + isis_metric_l1_cmd, + "isis metric <0-16777215> level-1", + "IS-IS commands\n" + "Set default metric for circuit\n" + "Default metric value\n" + "Specify metric for level-1 routing\n") +{ + int met; + struct isis_circuit *circuit = isis_circuit_lookup (vty); + if (!circuit) + return CMD_ERR_NO_MATCH; + + met = atoi (argv[0]); + + /* RFC3787 section 5.1 */ + if (circuit->area && circuit->area->oldmetric == 1 && + met > MAX_NARROW_LINK_METRIC) + { + vty_out (vty, "Invalid metric %d - should be <0-63> " + "when narrow metric type enabled%s", + met, VTY_NEWLINE); + return CMD_ERR_AMBIGUOUS; + } + + /* RFC4444 */ + if (circuit->area && circuit->area->newmetric == 1 && + met > MAX_WIDE_LINK_METRIC) + { + vty_out (vty, "Invalid metric %d - should be <0-16777215> " + "when wide metric type enabled%s", + met, VTY_NEWLINE); + return CMD_ERR_AMBIGUOUS; + } + + isis_circuit_metric_set (circuit, IS_LEVEL_1, met); + return CMD_SUCCESS; +} + +DEFUN (no_isis_metric_l1, + no_isis_metric_l1_cmd, + "no isis metric level-1", + NO_STR + "IS-IS commands\n" + "Set default metric for circuit\n" + "Specify metric for level-1 routing\n") +{ + struct isis_circuit *circuit = isis_circuit_lookup (vty); + if (!circuit) + return CMD_ERR_NO_MATCH; + + isis_circuit_metric_set (circuit, IS_LEVEL_1, DEFAULT_CIRCUIT_METRIC); + return CMD_SUCCESS; +} + +ALIAS (no_isis_metric_l1, + no_isis_metric_l1_arg_cmd, + "no isis metric <0-16777215> level-1", + NO_STR + "IS-IS commands\n" + "Set default metric for circuit\n" + "Default metric value\n" + "Specify metric for level-1 routing\n") + +DEFUN (isis_metric_l2, + isis_metric_l2_cmd, + "isis metric <0-16777215> level-2", + "IS-IS commands\n" + "Set default metric for circuit\n" + "Default metric value\n" + "Specify metric for level-2 routing\n") +{ + int met; + struct isis_circuit *circuit = isis_circuit_lookup (vty); + if (!circuit) + return CMD_ERR_NO_MATCH; + + met = atoi (argv[0]); + + /* RFC3787 section 5.1 */ + if (circuit->area && circuit->area->oldmetric == 1 && + met > MAX_NARROW_LINK_METRIC) + { + vty_out (vty, "Invalid metric %d - should be <0-63> " + "when narrow metric type enabled%s", + met, VTY_NEWLINE); + return CMD_ERR_AMBIGUOUS; + } + + /* RFC4444 */ + if (circuit->area && circuit->area->newmetric == 1 && + met > MAX_WIDE_LINK_METRIC) + { + vty_out (vty, "Invalid metric %d - should be <0-16777215> " + "when wide metric type enabled%s", + met, VTY_NEWLINE); + return CMD_ERR_AMBIGUOUS; + } + + isis_circuit_metric_set (circuit, IS_LEVEL_2, met); + return CMD_SUCCESS; +} + +DEFUN (no_isis_metric_l2, + no_isis_metric_l2_cmd, + "no isis metric level-2", + NO_STR + "IS-IS commands\n" + "Set default metric for circuit\n" + "Specify metric for level-2 routing\n") +{ + struct isis_circuit *circuit = isis_circuit_lookup (vty); + if (!circuit) + return CMD_ERR_NO_MATCH; + + isis_circuit_metric_set (circuit, IS_LEVEL_2, DEFAULT_CIRCUIT_METRIC); + return CMD_SUCCESS; +} + +ALIAS (no_isis_metric_l2, + no_isis_metric_l2_arg_cmd, + "no isis metric <0-16777215> level-2", + NO_STR + "IS-IS commands\n" + "Set default metric for circuit\n" + "Default metric value\n" + "Specify metric for level-2 routing\n") +/* end of metrics */ + +DEFUN (isis_hello_interval, + isis_hello_interval_cmd, + "isis hello-interval <1-600>", + "IS-IS commands\n" + "Set Hello interval\n" + "Hello interval value\n" + "Holdtime 1 seconds, interval depends on multiplier\n") +{ + int interval; + struct isis_circuit *circuit = isis_circuit_lookup (vty); + if (!circuit) + return CMD_ERR_NO_MATCH; + + interval = atoi (argv[0]); + if (interval < MIN_HELLO_INTERVAL || interval > MAX_HELLO_INTERVAL) + { + vty_out (vty, "Invalid hello-interval %d - should be <1-600>%s", + interval, VTY_NEWLINE); + return CMD_ERR_AMBIGUOUS; + } + + circuit->hello_interval[0] = (u_int16_t) interval; + circuit->hello_interval[1] = (u_int16_t) interval; + + return CMD_SUCCESS; +} + +DEFUN (no_isis_hello_interval, + no_isis_hello_interval_cmd, + "no isis hello-interval", + NO_STR + "IS-IS commands\n" + "Set Hello interval\n") +{ + struct isis_circuit *circuit = isis_circuit_lookup (vty); + if (!circuit) + return CMD_ERR_NO_MATCH; + + circuit->hello_interval[0] = DEFAULT_HELLO_INTERVAL; + circuit->hello_interval[1] = DEFAULT_HELLO_INTERVAL; + + return CMD_SUCCESS; +} + +ALIAS (no_isis_hello_interval, + no_isis_hello_interval_arg_cmd, + "no isis hello-interval <1-600>", + NO_STR + "IS-IS commands\n" + "Set Hello interval\n" + "Hello interval value\n" + "Holdtime 1 second, interval depends on multiplier\n") + +DEFUN (isis_hello_interval_l1, + isis_hello_interval_l1_cmd, + "isis hello-interval <1-600> level-1", + "IS-IS commands\n" + "Set Hello interval\n" + "Hello interval value\n" + "Holdtime 1 second, interval depends on multiplier\n" + "Specify hello-interval for level-1 IIHs\n") +{ + long interval; + struct isis_circuit *circuit = isis_circuit_lookup (vty); + if (!circuit) + return CMD_ERR_NO_MATCH; + + interval = atoi (argv[0]); + if (interval < MIN_HELLO_INTERVAL || interval > MAX_HELLO_INTERVAL) + { + vty_out (vty, "Invalid hello-interval %ld - should be <1-600>%s", + interval, VTY_NEWLINE); + return CMD_ERR_AMBIGUOUS; + } + + circuit->hello_interval[0] = (u_int16_t) interval; + + return CMD_SUCCESS; +} + +DEFUN (no_isis_hello_interval_l1, + no_isis_hello_interval_l1_cmd, + "no isis hello-interval level-1", + NO_STR + "IS-IS commands\n" + "Set Hello interval\n" + "Specify hello-interval for level-1 IIHs\n") +{ + struct isis_circuit *circuit = isis_circuit_lookup (vty); + if (!circuit) + return CMD_ERR_NO_MATCH; + + circuit->hello_interval[0] = DEFAULT_HELLO_INTERVAL; + + return CMD_SUCCESS; +} + +ALIAS (no_isis_hello_interval_l1, + no_isis_hello_interval_l1_arg_cmd, + "no isis hello-interval <1-600> level-1", + NO_STR + "IS-IS commands\n" + "Set Hello interval\n" + "Hello interval value\n" + "Holdtime 1 second, interval depends on multiplier\n" + "Specify hello-interval for level-1 IIHs\n") + +DEFUN (isis_hello_interval_l2, + isis_hello_interval_l2_cmd, + "isis hello-interval <1-600> level-2", + "IS-IS commands\n" + "Set Hello interval\n" + "Hello interval value\n" + "Holdtime 1 second, interval depends on multiplier\n" + "Specify hello-interval for level-2 IIHs\n") +{ + long interval; + struct isis_circuit *circuit = isis_circuit_lookup (vty); + if (!circuit) + return CMD_ERR_NO_MATCH; + + interval = atoi (argv[0]); + if (interval < MIN_HELLO_INTERVAL || interval > MAX_HELLO_INTERVAL) + { + vty_out (vty, "Invalid hello-interval %ld - should be <1-600>%s", + interval, VTY_NEWLINE); + return CMD_ERR_AMBIGUOUS; + } + + circuit->hello_interval[1] = (u_int16_t) interval; + + return CMD_SUCCESS; +} + +DEFUN (no_isis_hello_interval_l2, + no_isis_hello_interval_l2_cmd, + "no isis hello-interval level-2", + NO_STR + "IS-IS commands\n" + "Set Hello interval\n" + "Specify hello-interval for level-2 IIHs\n") +{ + struct isis_circuit *circuit = isis_circuit_lookup (vty); + if (!circuit) + return CMD_ERR_NO_MATCH; + + circuit->hello_interval[1] = DEFAULT_HELLO_INTERVAL; + + return CMD_SUCCESS; +} + +ALIAS (no_isis_hello_interval_l2, + no_isis_hello_interval_l2_arg_cmd, + "no isis hello-interval <1-600> level-2", + NO_STR + "IS-IS commands\n" + "Set Hello interval\n" + "Hello interval value\n" + "Holdtime 1 second, interval depends on multiplier\n" + "Specify hello-interval for level-2 IIHs\n") + +DEFUN (isis_hello_multiplier, + isis_hello_multiplier_cmd, + "isis hello-multiplier <2-100>", + "IS-IS commands\n" + "Set multiplier for Hello holding time\n" + "Hello multiplier value\n") +{ + int mult; + struct isis_circuit *circuit = isis_circuit_lookup (vty); + if (!circuit) + return CMD_ERR_NO_MATCH; + + mult = atoi (argv[0]); + if (mult < MIN_HELLO_MULTIPLIER || mult > MAX_HELLO_MULTIPLIER) + { + vty_out (vty, "Invalid hello-multiplier %d - should be <2-100>%s", + mult, VTY_NEWLINE); + return CMD_ERR_AMBIGUOUS; + } + + circuit->hello_multiplier[0] = (u_int16_t) mult; + circuit->hello_multiplier[1] = (u_int16_t) mult; + + return CMD_SUCCESS; +} + +DEFUN (no_isis_hello_multiplier, + no_isis_hello_multiplier_cmd, + "no isis hello-multiplier", + NO_STR + "IS-IS commands\n" + "Set multiplier for Hello holding time\n") +{ + struct isis_circuit *circuit = isis_circuit_lookup (vty); + if (!circuit) + return CMD_ERR_NO_MATCH; + + circuit->hello_multiplier[0] = DEFAULT_HELLO_MULTIPLIER; + circuit->hello_multiplier[1] = DEFAULT_HELLO_MULTIPLIER; + + return CMD_SUCCESS; +} + +ALIAS (no_isis_hello_multiplier, + no_isis_hello_multiplier_arg_cmd, + "no isis hello-multiplier <2-100>", + NO_STR + "IS-IS commands\n" + "Set multiplier for Hello holding time\n" + "Hello multiplier value\n") + +DEFUN (isis_hello_multiplier_l1, + isis_hello_multiplier_l1_cmd, + "isis hello-multiplier <2-100> level-1", + "IS-IS commands\n" + "Set multiplier for Hello holding time\n" + "Hello multiplier value\n" + "Specify hello multiplier for level-1 IIHs\n") +{ + int mult; + struct isis_circuit *circuit = isis_circuit_lookup (vty); + if (!circuit) + return CMD_ERR_NO_MATCH; + + mult = atoi (argv[0]); + if (mult < MIN_HELLO_MULTIPLIER || mult > MAX_HELLO_MULTIPLIER) + { + vty_out (vty, "Invalid hello-multiplier %d - should be <2-100>%s", + mult, VTY_NEWLINE); + return CMD_ERR_AMBIGUOUS; + } + + circuit->hello_multiplier[0] = (u_int16_t) mult; + + return CMD_SUCCESS; +} + +DEFUN (no_isis_hello_multiplier_l1, + no_isis_hello_multiplier_l1_cmd, + "no isis hello-multiplier level-1", + NO_STR + "IS-IS commands\n" + "Set multiplier for Hello holding time\n" + "Specify hello multiplier for level-1 IIHs\n") +{ + struct isis_circuit *circuit = isis_circuit_lookup (vty); + if (!circuit) + return CMD_ERR_NO_MATCH; + + circuit->hello_multiplier[0] = DEFAULT_HELLO_MULTIPLIER; + + return CMD_SUCCESS; +} + +ALIAS (no_isis_hello_multiplier_l1, + no_isis_hello_multiplier_l1_arg_cmd, + "no isis hello-multiplier <2-100> level-1", + NO_STR + "IS-IS commands\n" + "Set multiplier for Hello holding time\n" + "Hello multiplier value\n" + "Specify hello multiplier for level-1 IIHs\n") + +DEFUN (isis_hello_multiplier_l2, + isis_hello_multiplier_l2_cmd, + "isis hello-multiplier <2-100> level-2", + "IS-IS commands\n" + "Set multiplier for Hello holding time\n" + "Hello multiplier value\n" + "Specify hello multiplier for level-2 IIHs\n") +{ + int mult; + struct isis_circuit *circuit = isis_circuit_lookup (vty); + if (!circuit) + return CMD_ERR_NO_MATCH; + + mult = atoi (argv[0]); + if (mult < MIN_HELLO_MULTIPLIER || mult > MAX_HELLO_MULTIPLIER) + { + vty_out (vty, "Invalid hello-multiplier %d - should be <2-100>%s", + mult, VTY_NEWLINE); + return CMD_ERR_AMBIGUOUS; + } + + circuit->hello_multiplier[1] = (u_int16_t) mult; + + return CMD_SUCCESS; +} + +DEFUN (no_isis_hello_multiplier_l2, + no_isis_hello_multiplier_l2_cmd, + "no isis hello-multiplier level-2", + NO_STR + "IS-IS commands\n" + "Set multiplier for Hello holding time\n" + "Specify hello multiplier for level-2 IIHs\n") +{ + struct isis_circuit *circuit = isis_circuit_lookup (vty); + if (!circuit) + return CMD_ERR_NO_MATCH; + + circuit->hello_multiplier[1] = DEFAULT_HELLO_MULTIPLIER; + + return CMD_SUCCESS; +} + +ALIAS (no_isis_hello_multiplier_l2, + no_isis_hello_multiplier_l2_arg_cmd, + "no isis hello-multiplier <2-100> level-2", + NO_STR + "IS-IS commands\n" + "Set multiplier for Hello holding time\n" + "Hello multiplier value\n" + "Specify hello multiplier for level-2 IIHs\n") + +DEFUN (isis_hello_padding, + isis_hello_padding_cmd, + "isis hello padding", + "IS-IS commands\n" + "Add padding to IS-IS hello packets\n" + "Pad hello packets\n" + "\n") +{ + struct isis_circuit *circuit = isis_circuit_lookup (vty); + if (!circuit) + return CMD_ERR_NO_MATCH; + + circuit->pad_hellos = 1; + + return CMD_SUCCESS; +} + +DEFUN (no_isis_hello_padding, + no_isis_hello_padding_cmd, + "no isis hello padding", + NO_STR + "IS-IS commands\n" + "Add padding to IS-IS hello packets\n" + "Pad hello packets\n" + "\n") +{ + struct isis_circuit *circuit = isis_circuit_lookup (vty); + if (!circuit) + return CMD_ERR_NO_MATCH; + + circuit->pad_hellos = 0; + + return CMD_SUCCESS; +} + +DEFUN (csnp_interval, + csnp_interval_cmd, + "isis csnp-interval <1-600>", + "IS-IS commands\n" + "Set CSNP interval in seconds\n" + "CSNP interval value\n") +{ + unsigned long interval; + struct isis_circuit *circuit = isis_circuit_lookup (vty); + if (!circuit) + return CMD_ERR_NO_MATCH; + + interval = atol (argv[0]); + if (interval < MIN_CSNP_INTERVAL || interval > MAX_CSNP_INTERVAL) + { + vty_out (vty, "Invalid csnp-interval %lu - should be <1-600>%s", + interval, VTY_NEWLINE); + return CMD_ERR_AMBIGUOUS; + } + + circuit->csnp_interval[0] = (u_int16_t) interval; + circuit->csnp_interval[1] = (u_int16_t) interval; + + return CMD_SUCCESS; +} + +DEFUN (no_csnp_interval, + no_csnp_interval_cmd, + "no isis csnp-interval", + NO_STR + "IS-IS commands\n" + "Set CSNP interval in seconds\n") +{ + struct isis_circuit *circuit = isis_circuit_lookup (vty); + if (!circuit) + return CMD_ERR_NO_MATCH; + + circuit->csnp_interval[0] = DEFAULT_CSNP_INTERVAL; + circuit->csnp_interval[1] = DEFAULT_CSNP_INTERVAL; + + return CMD_SUCCESS; +} + +ALIAS (no_csnp_interval, + no_csnp_interval_arg_cmd, + "no isis csnp-interval <1-600>", + NO_STR + "IS-IS commands\n" + "Set CSNP interval in seconds\n" + "CSNP interval value\n") + +DEFUN (csnp_interval_l1, + csnp_interval_l1_cmd, + "isis csnp-interval <1-600> level-1", + "IS-IS commands\n" + "Set CSNP interval in seconds\n" + "CSNP interval value\n" + "Specify interval for level-1 CSNPs\n") +{ + unsigned long interval; + struct isis_circuit *circuit = isis_circuit_lookup (vty); + if (!circuit) + return CMD_ERR_NO_MATCH; + + interval = atol (argv[0]); + if (interval < MIN_CSNP_INTERVAL || interval > MAX_CSNP_INTERVAL) + { + vty_out (vty, "Invalid csnp-interval %lu - should be <1-600>%s", + interval, VTY_NEWLINE); + return CMD_ERR_AMBIGUOUS; + } + + circuit->csnp_interval[0] = (u_int16_t) interval; + + return CMD_SUCCESS; +} + +DEFUN (no_csnp_interval_l1, + no_csnp_interval_l1_cmd, + "no isis csnp-interval level-1", + NO_STR + "IS-IS commands\n" + "Set CSNP interval in seconds\n" + "Specify interval for level-1 CSNPs\n") +{ + struct isis_circuit *circuit = isis_circuit_lookup (vty); + if (!circuit) + return CMD_ERR_NO_MATCH; + + circuit->csnp_interval[0] = DEFAULT_CSNP_INTERVAL; + + return CMD_SUCCESS; +} + +ALIAS (no_csnp_interval_l1, + no_csnp_interval_l1_arg_cmd, + "no isis csnp-interval <1-600> level-1", + NO_STR + "IS-IS commands\n" + "Set CSNP interval in seconds\n" + "CSNP interval value\n" + "Specify interval for level-1 CSNPs\n") + +DEFUN (csnp_interval_l2, + csnp_interval_l2_cmd, + "isis csnp-interval <1-600> level-2", + "IS-IS commands\n" + "Set CSNP interval in seconds\n" + "CSNP interval value\n" + "Specify interval for level-2 CSNPs\n") +{ + unsigned long interval; + struct isis_circuit *circuit = isis_circuit_lookup (vty); + if (!circuit) + return CMD_ERR_NO_MATCH; + + interval = atol (argv[0]); + if (interval < MIN_CSNP_INTERVAL || interval > MAX_CSNP_INTERVAL) + { + vty_out (vty, "Invalid csnp-interval %lu - should be <1-600>%s", + interval, VTY_NEWLINE); + return CMD_ERR_AMBIGUOUS; + } + + circuit->csnp_interval[1] = (u_int16_t) interval; + + return CMD_SUCCESS; +} + +DEFUN (no_csnp_interval_l2, + no_csnp_interval_l2_cmd, + "no isis csnp-interval level-2", + NO_STR + "IS-IS commands\n" + "Set CSNP interval in seconds\n" + "Specify interval for level-2 CSNPs\n") +{ + struct isis_circuit *circuit = isis_circuit_lookup (vty); + if (!circuit) + return CMD_ERR_NO_MATCH; + + circuit->csnp_interval[1] = DEFAULT_CSNP_INTERVAL; + + return CMD_SUCCESS; +} + +ALIAS (no_csnp_interval_l2, + no_csnp_interval_l2_arg_cmd, + "no isis csnp-interval <1-600> level-2", + NO_STR + "IS-IS commands\n" + "Set CSNP interval in seconds\n" + "CSNP interval value\n" + "Specify interval for level-2 CSNPs\n") + +DEFUN (psnp_interval, + psnp_interval_cmd, + "isis psnp-interval <1-120>", + "IS-IS commands\n" + "Set PSNP interval in seconds\n" + "PSNP interval value\n") +{ + unsigned long interval; + struct isis_circuit *circuit = isis_circuit_lookup (vty); + if (!circuit) + return CMD_ERR_NO_MATCH; + + interval = atol (argv[0]); + if (interval < MIN_PSNP_INTERVAL || interval > MAX_PSNP_INTERVAL) + { + vty_out (vty, "Invalid psnp-interval %lu - should be <1-120>%s", + interval, VTY_NEWLINE); + return CMD_ERR_AMBIGUOUS; + } + + circuit->psnp_interval[0] = (u_int16_t) interval; + circuit->psnp_interval[1] = (u_int16_t) interval; + + return CMD_SUCCESS; +} + +DEFUN (no_psnp_interval, + no_psnp_interval_cmd, + "no isis psnp-interval", + NO_STR + "IS-IS commands\n" + "Set PSNP interval in seconds\n") +{ + struct isis_circuit *circuit = isis_circuit_lookup (vty); + if (!circuit) + return CMD_ERR_NO_MATCH; + + circuit->psnp_interval[0] = DEFAULT_PSNP_INTERVAL; + circuit->psnp_interval[1] = DEFAULT_PSNP_INTERVAL; + + return CMD_SUCCESS; +} + +ALIAS (no_psnp_interval, + no_psnp_interval_arg_cmd, + "no isis psnp-interval <1-120>", + NO_STR + "IS-IS commands\n" + "Set PSNP interval in seconds\n" + "PSNP interval value\n") + +DEFUN (psnp_interval_l1, + psnp_interval_l1_cmd, + "isis psnp-interval <1-120> level-1", + "IS-IS commands\n" + "Set PSNP interval in seconds\n" + "PSNP interval value\n" + "Specify interval for level-1 PSNPs\n") +{ + unsigned long interval; + struct isis_circuit *circuit = isis_circuit_lookup (vty); + if (!circuit) + return CMD_ERR_NO_MATCH; + + interval = atol (argv[0]); + if (interval < MIN_PSNP_INTERVAL || interval > MAX_PSNP_INTERVAL) + { + vty_out (vty, "Invalid psnp-interval %lu - should be <1-120>%s", + interval, VTY_NEWLINE); + return CMD_ERR_AMBIGUOUS; + } + + circuit->psnp_interval[0] = (u_int16_t) interval; + + return CMD_SUCCESS; +} + +DEFUN (no_psnp_interval_l1, + no_psnp_interval_l1_cmd, + "no isis psnp-interval level-1", + NO_STR + "IS-IS commands\n" + "Set PSNP interval in seconds\n" + "Specify interval for level-1 PSNPs\n") +{ + struct isis_circuit *circuit = isis_circuit_lookup (vty); + if (!circuit) + return CMD_ERR_NO_MATCH; + + circuit->psnp_interval[0] = DEFAULT_PSNP_INTERVAL; + + return CMD_SUCCESS; +} + +ALIAS (no_psnp_interval_l1, + no_psnp_interval_l1_arg_cmd, + "no isis psnp-interval <1-120> level-1", + NO_STR + "IS-IS commands\n" + "Set PSNP interval in seconds\n" + "PSNP interval value\n" + "Specify interval for level-1 PSNPs\n") + +DEFUN (psnp_interval_l2, + psnp_interval_l2_cmd, + "isis psnp-interval <1-120> level-2", + "IS-IS commands\n" + "Set PSNP interval in seconds\n" + "PSNP interval value\n" + "Specify interval for level-2 PSNPs\n") +{ + unsigned long interval; + struct isis_circuit *circuit = isis_circuit_lookup (vty); + if (!circuit) + return CMD_ERR_NO_MATCH; + + interval = atol (argv[0]); + if (interval < MIN_PSNP_INTERVAL || interval > MAX_PSNP_INTERVAL) + { + vty_out (vty, "Invalid psnp-interval %lu - should be <1-120>%s", + interval, VTY_NEWLINE); + return CMD_ERR_AMBIGUOUS; + } + + circuit->psnp_interval[1] = (u_int16_t) interval; + + return CMD_SUCCESS; +} + +DEFUN (no_psnp_interval_l2, + no_psnp_interval_l2_cmd, + "no isis psnp-interval level-2", + NO_STR + "IS-IS commands\n" + "Set PSNP interval in seconds\n" + "Specify interval for level-2 PSNPs\n") +{ + struct isis_circuit *circuit = isis_circuit_lookup (vty); + if (!circuit) + return CMD_ERR_NO_MATCH; + + circuit->psnp_interval[1] = DEFAULT_PSNP_INTERVAL; + + return CMD_SUCCESS; +} + +ALIAS (no_psnp_interval_l2, + no_psnp_interval_l2_arg_cmd, + "no isis psnp-interval <1-120> level-2", + NO_STR + "IS-IS commands\n" + "Set PSNP interval in seconds\n" + "PSNP interval value\n" + "Specify interval for level-2 PSNPs\n") + +static int +validate_metric_style_narrow (struct vty *vty, struct isis_area *area) +{ + struct isis_circuit *circuit; + struct listnode *node; + + if (! vty) + return CMD_ERR_AMBIGUOUS; + + if (! area) + { + vty_out (vty, "ISIS area is invalid%s", VTY_NEWLINE); + return CMD_ERR_AMBIGUOUS; + } + + for (ALL_LIST_ELEMENTS_RO (area->circuit_list, node, circuit)) + { + if ((area->is_type & IS_LEVEL_1) && + (circuit->is_type & IS_LEVEL_1) && + (circuit->te_metric[0] > MAX_NARROW_LINK_METRIC)) + { + vty_out (vty, "ISIS circuit %s metric is invalid%s", + circuit->interface->name, VTY_NEWLINE); + return CMD_ERR_AMBIGUOUS; + } + if ((area->is_type & IS_LEVEL_2) && + (circuit->is_type & IS_LEVEL_2) && + (circuit->te_metric[1] > MAX_NARROW_LINK_METRIC)) + { + vty_out (vty, "ISIS circuit %s metric is invalid%s", + circuit->interface->name, VTY_NEWLINE); + return CMD_ERR_AMBIGUOUS; + } + } + + return CMD_SUCCESS; +} + +DEFUN (metric_style, + metric_style_cmd, + "metric-style (narrow|transition|wide)", + "Use old-style (ISO 10589) or new-style packet formats\n" + "Use old style of TLVs with narrow metric\n" + "Send and accept both styles of TLVs during transition\n" + "Use new style of TLVs to carry wider metric\n") +{ + struct isis_area *area = vty->index; + int ret; + + assert(area); + + if (strncmp (argv[0], "w", 1) == 0) + { + isis_area_metricstyle_set(area, false, true); + return CMD_SUCCESS; + } + + ret = validate_metric_style_narrow (vty, area); + if (ret != CMD_SUCCESS) + return ret; + + if (strncmp (argv[0], "t", 1) == 0) + isis_area_metricstyle_set(area, true, true); + else if (strncmp (argv[0], "n", 1) == 0) + isis_area_metricstyle_set(area, true, false); + return CMD_SUCCESS; + + return CMD_SUCCESS; +} + +DEFUN (no_metric_style, + no_metric_style_cmd, + "no metric-style", + NO_STR + "Use old-style (ISO 10589) or new-style packet formats\n") +{ + struct isis_area *area = vty->index; + int ret; + + assert (area); + ret = validate_metric_style_narrow (vty, area); + if (ret != CMD_SUCCESS) + return ret; + + isis_area_metricstyle_set(area, true, false); + return CMD_SUCCESS; +} + +DEFUN (set_overload_bit, + set_overload_bit_cmd, + "set-overload-bit", + "Set overload bit to avoid any transit traffic\n" + "Set overload bit\n") +{ + struct isis_area *area = vty->index; + assert (area); + + isis_area_overload_bit_set(area, true); + return CMD_SUCCESS; +} + +DEFUN (no_set_overload_bit, + no_set_overload_bit_cmd, + "no set-overload-bit", + "Reset overload bit to accept transit traffic\n" + "Reset overload bit\n") +{ + struct isis_area *area = vty->index; + assert (area); + + isis_area_overload_bit_set(area, false); + return CMD_SUCCESS; +} + +DEFUN (set_attached_bit, + set_attached_bit_cmd, + "set-attached-bit", + "Set attached bit to identify as L1/L2 router for inter-area traffic\n" + "Set attached bit\n") +{ + struct isis_area *area = vty->index; + assert (area); + + isis_area_attached_bit_set(area, true); + return CMD_SUCCESS; +} + +DEFUN (no_set_attached_bit, + no_set_attached_bit_cmd, + "no set-attached-bit", + "Reset attached bit\n") +{ + struct isis_area *area = vty->index; + assert (area); + + isis_area_attached_bit_set(area, false); + return CMD_SUCCESS; +} + +DEFUN (dynamic_hostname, + dynamic_hostname_cmd, + "hostname dynamic", + "Dynamic hostname for IS-IS\n" + "Dynamic hostname\n") +{ + struct isis_area *area = vty->index; + assert(area); + + isis_area_dynhostname_set(area, true); + return CMD_SUCCESS; +} + +DEFUN (no_dynamic_hostname, + no_dynamic_hostname_cmd, + "no hostname dynamic", + NO_STR + "Dynamic hostname for IS-IS\n" + "Dynamic hostname\n") +{ + struct isis_area *area = vty->index; + assert(area); + + isis_area_dynhostname_set(area, false); + return CMD_SUCCESS; +} + +static int area_lsp_mtu_set(struct vty *vty, unsigned int lsp_mtu) +{ + struct isis_area *area = vty->index; + struct listnode *node; + struct isis_circuit *circuit; + + if (!area) + { + vty_out (vty, "Can't find ISIS instance %s", VTY_NEWLINE); + return CMD_ERR_NO_MATCH; + } + + for (ALL_LIST_ELEMENTS_RO(area->circuit_list, node, circuit)) + { + if(circuit->state != C_STATE_INIT && circuit->state != C_STATE_UP) + continue; + if(lsp_mtu > isis_circuit_pdu_size(circuit)) + { + vty_out(vty, "ISIS area contains circuit %s, which has a maximum PDU size of %zu.%s", + circuit->interface->name, isis_circuit_pdu_size(circuit), + VTY_NEWLINE); + return CMD_ERR_AMBIGUOUS; + } + } + + isis_area_lsp_mtu_set(area, lsp_mtu); + return CMD_SUCCESS; +} + +DEFUN (area_lsp_mtu, + area_lsp_mtu_cmd, + "lsp-mtu <128-4352>", + "Configure the maximum size of generated LSPs\n" + "Maximum size of generated LSPs\n") +{ + unsigned int lsp_mtu; + + VTY_GET_INTEGER_RANGE("lsp-mtu", lsp_mtu, argv[0], 128, 4352); + + return area_lsp_mtu_set(vty, lsp_mtu); +} + +DEFUN(no_area_lsp_mtu, + no_area_lsp_mtu_cmd, + "no lsp-mtu", + NO_STR + "Configure the maximum size of generated LSPs\n") +{ + return area_lsp_mtu_set(vty, DEFAULT_LSP_MTU); +} + +ALIAS(no_area_lsp_mtu, + no_area_lsp_mtu_arg_cmd, + "no lsp-mtu <128-4352>", + NO_STR + "Configure the maximum size of generated LSPs\n" + "Maximum size of generated LSPs\n"); + +DEFUN (is_type, + is_type_cmd, + "is-type (level-1|level-1-2|level-2-only)", + "IS Level for this routing process (OSI only)\n" + "Act as a station router only\n" + "Act as both a station router and an area router\n" + "Act as an area router only\n") +{ + struct isis_area *area; + int type; + + area = vty->index; + + if (!area) + { + vty_out (vty, "Can't find IS-IS instance%s", VTY_NEWLINE); + return CMD_ERR_NO_MATCH; + } + + type = string2circuit_t (argv[0]); + if (!type) + { + vty_out (vty, "Unknown IS level %s", VTY_NEWLINE); + return CMD_SUCCESS; + } + + isis_area_is_type_set(area, type); + + return CMD_SUCCESS; +} + +DEFUN (no_is_type, + no_is_type_cmd, + "no is-type (level-1|level-1-2|level-2-only)", + NO_STR + "IS Level for this routing process (OSI only)\n" + "Act as a station router only\n" + "Act as both a station router and an area router\n" + "Act as an area router only\n") +{ + struct isis_area *area; + int type; + + area = vty->index; + assert (area); + + /* + * Put the is-type back to defaults: + * - level-1-2 on first area + * - level-1 for the rest + */ + if (listgetdata (listhead (isis->area_list)) == area) + type = IS_LEVEL_1_AND_2; + else + type = IS_LEVEL_1; + + isis_area_is_type_set(area, type); + + return CMD_SUCCESS; +} + +static int +set_lsp_gen_interval (struct vty *vty, struct isis_area *area, + uint16_t interval, int level) +{ + int lvl; + + for (lvl = IS_LEVEL_1; lvl <= IS_LEVEL_2; ++lvl) + { + if (!(lvl & level)) + continue; + + if (interval >= area->lsp_refresh[lvl-1]) + { + vty_out (vty, "LSP gen interval %us must be less than " + "the LSP refresh interval %us%s", + interval, area->lsp_refresh[lvl-1], VTY_NEWLINE); + return CMD_ERR_AMBIGUOUS; + } + } + + for (lvl = IS_LEVEL_1; lvl <= IS_LEVEL_2; ++lvl) + { + if (!(lvl & level)) + continue; + area->lsp_gen_interval[lvl-1] = interval; + } + + return CMD_SUCCESS; +} + +DEFUN (lsp_gen_interval, + lsp_gen_interval_cmd, + "lsp-gen-interval <1-120>", + "Minimum interval between regenerating same LSP\n" + "Minimum interval in seconds\n") +{ + struct isis_area *area; + uint16_t interval; + int level; + + area = vty->index; + interval = atoi (argv[0]); + level = IS_LEVEL_1 | IS_LEVEL_2; + return set_lsp_gen_interval (vty, area, interval, level); +} + +DEFUN (no_lsp_gen_interval, + no_lsp_gen_interval_cmd, + "no lsp-gen-interval", + NO_STR + "Minimum interval between regenerating same LSP\n") +{ + struct isis_area *area; + uint16_t interval; + int level; + + area = vty->index; + interval = DEFAULT_MIN_LSP_GEN_INTERVAL; + level = IS_LEVEL_1 | IS_LEVEL_2; + return set_lsp_gen_interval (vty, area, interval, level); +} + +ALIAS (no_lsp_gen_interval, + no_lsp_gen_interval_arg_cmd, + "no lsp-gen-interval <1-120>", + NO_STR + "Minimum interval between regenerating same LSP\n" + "Minimum interval in seconds\n") + +DEFUN (lsp_gen_interval_l1, + lsp_gen_interval_l1_cmd, + "lsp-gen-interval level-1 <1-120>", + "Minimum interval between regenerating same LSP\n" + "Set interval for level 1 only\n" + "Minimum interval in seconds\n") +{ + struct isis_area *area; + uint16_t interval; + int level; + + area = vty->index; + interval = atoi (argv[0]); + level = IS_LEVEL_1; + return set_lsp_gen_interval (vty, area, interval, level); +} + +DEFUN (no_lsp_gen_interval_l1, + no_lsp_gen_interval_l1_cmd, + "no lsp-gen-interval level-1", + NO_STR + "Minimum interval between regenerating same LSP\n" + "Set interval for level 1 only\n") +{ + struct isis_area *area; + uint16_t interval; + int level; + + area = vty->index; + interval = DEFAULT_MIN_LSP_GEN_INTERVAL; + level = IS_LEVEL_1; + return set_lsp_gen_interval (vty, area, interval, level); +} + +ALIAS (no_lsp_gen_interval_l1, + no_lsp_gen_interval_l1_arg_cmd, + "no lsp-gen-interval level-1 <1-120>", + NO_STR + "Minimum interval between regenerating same LSP\n" + "Set interval for level 1 only\n" + "Minimum interval in seconds\n") + +DEFUN (lsp_gen_interval_l2, + lsp_gen_interval_l2_cmd, + "lsp-gen-interval level-2 <1-120>", + "Minimum interval between regenerating same LSP\n" + "Set interval for level 2 only\n" + "Minimum interval in seconds\n") +{ + struct isis_area *area; + uint16_t interval; + int level; + + area = vty->index; + interval = atoi (argv[0]); + level = IS_LEVEL_2; + return set_lsp_gen_interval (vty, area, interval, level); +} + +DEFUN (no_lsp_gen_interval_l2, + no_lsp_gen_interval_l2_cmd, + "no lsp-gen-interval level-2", + NO_STR + "Minimum interval between regenerating same LSP\n" + "Set interval for level 2 only\n") +{ + struct isis_area *area; + uint16_t interval; + int level; + + area = vty->index; + interval = DEFAULT_MIN_LSP_GEN_INTERVAL; + level = IS_LEVEL_2; + return set_lsp_gen_interval (vty, area, interval, level); +} + +ALIAS (no_lsp_gen_interval_l2, + no_lsp_gen_interval_l2_arg_cmd, + "no lsp-gen-interval level-2 <1-120>", + NO_STR + "Minimum interval between regenerating same LSP\n" + "Set interval for level 2 only\n" + "Minimum interval in seconds\n") + +DEFUN (spf_interval, + spf_interval_cmd, + "spf-interval <1-120>", + "Minimum interval between SPF calculations\n" + "Minimum interval between consecutive SPFs in seconds\n") +{ + struct isis_area *area; + u_int16_t interval; + + area = vty->index; + interval = atoi (argv[0]); + area->min_spf_interval[0] = interval; + area->min_spf_interval[1] = interval; + + return CMD_SUCCESS; +} + +DEFUN (no_spf_interval, + no_spf_interval_cmd, + "no spf-interval", + NO_STR + "Minimum interval between SPF calculations\n") +{ + struct isis_area *area; + + area = vty->index; + + area->min_spf_interval[0] = MINIMUM_SPF_INTERVAL; + area->min_spf_interval[1] = MINIMUM_SPF_INTERVAL; + + return CMD_SUCCESS; +} + +ALIAS (no_spf_interval, + no_spf_interval_arg_cmd, + "no spf-interval <1-120>", + NO_STR + "Minimum interval between SPF calculations\n" + "Minimum interval between consecutive SPFs in seconds\n") + +DEFUN (spf_interval_l1, + spf_interval_l1_cmd, + "spf-interval level-1 <1-120>", + "Minimum interval between SPF calculations\n" + "Set interval for level 1 only\n" + "Minimum interval between consecutive SPFs in seconds\n") +{ + struct isis_area *area; + u_int16_t interval; + + area = vty->index; + interval = atoi (argv[0]); + area->min_spf_interval[0] = interval; + + return CMD_SUCCESS; +} + +DEFUN (no_spf_interval_l1, + no_spf_interval_l1_cmd, + "no spf-interval level-1", + NO_STR + "Minimum interval between SPF calculations\n" + "Set interval for level 1 only\n") +{ + struct isis_area *area; + + area = vty->index; + + area->min_spf_interval[0] = MINIMUM_SPF_INTERVAL; + + return CMD_SUCCESS; +} + +ALIAS (no_spf_interval, + no_spf_interval_l1_arg_cmd, + "no spf-interval level-1 <1-120>", + NO_STR + "Minimum interval between SPF calculations\n" + "Set interval for level 1 only\n" + "Minimum interval between consecutive SPFs in seconds\n") + +DEFUN (spf_interval_l2, + spf_interval_l2_cmd, + "spf-interval level-2 <1-120>", + "Minimum interval between SPF calculations\n" + "Set interval for level 2 only\n" + "Minimum interval between consecutive SPFs in seconds\n") +{ + struct isis_area *area; + u_int16_t interval; + + area = vty->index; + interval = atoi (argv[0]); + area->min_spf_interval[1] = interval; + + return CMD_SUCCESS; +} + +DEFUN (no_spf_interval_l2, + no_spf_interval_l2_cmd, + "no spf-interval level-2", + NO_STR + "Minimum interval between SPF calculations\n" + "Set interval for level 2 only\n") +{ + struct isis_area *area; + + area = vty->index; + + area->min_spf_interval[1] = MINIMUM_SPF_INTERVAL; + + return CMD_SUCCESS; +} + +ALIAS (no_spf_interval, + no_spf_interval_l2_arg_cmd, + "no spf-interval level-2 <1-120>", + NO_STR + "Minimum interval between SPF calculations\n" + "Set interval for level 2 only\n" + "Minimum interval between consecutive SPFs in seconds\n") + +static int +area_max_lsp_lifetime_set(struct vty *vty, int level, + uint16_t interval) +{ + struct isis_area *area = vty->index; + int lvl; + uint16_t refresh_interval = interval - 300; + int set_refresh_interval[ISIS_LEVELS] = {0, 0}; + + if (!area) + { + vty_out (vty, "Can't find ISIS instance %s", VTY_NEWLINE); + return CMD_ERR_NO_MATCH; + } + + for (lvl = IS_LEVEL_1; lvl <= IS_LEVEL_2; lvl++) + { + if (!(lvl & level)) + continue; + + if (refresh_interval < area->lsp_refresh[lvl-1]) + { + vty_out (vty, "Level %d Max LSP lifetime %us must be 300s greater than " + "the configured LSP refresh interval %us%s", + lvl, interval, area->lsp_refresh[lvl-1], VTY_NEWLINE); + vty_out (vty, "Automatically reducing level %d LSP refresh interval " + "to %us%s", lvl, refresh_interval, VTY_NEWLINE); + set_refresh_interval[lvl-1] = 1; + + if (refresh_interval <= area->lsp_gen_interval[lvl-1]) + { + vty_out (vty, "LSP refresh interval %us must be greater than " + "the configured LSP gen interval %us%s", + refresh_interval, area->lsp_gen_interval[lvl-1], + VTY_NEWLINE); + return CMD_ERR_AMBIGUOUS; + } + } + } + + for (lvl = IS_LEVEL_1; lvl <= IS_LEVEL_2; lvl++) + { + if (!(lvl & level)) + continue; + isis_area_max_lsp_lifetime_set(area, lvl, interval); + if (set_refresh_interval[lvl-1]) + isis_area_lsp_refresh_set(area, lvl, refresh_interval); + } + + return CMD_SUCCESS; +} + +DEFUN (max_lsp_lifetime, + max_lsp_lifetime_cmd, + "max-lsp-lifetime <350-65535>", + "Maximum LSP lifetime\n" + "LSP lifetime in seconds\n") +{ + return area_max_lsp_lifetime_set(vty, IS_LEVEL_1_AND_2, atoi(argv[0])); +} + +DEFUN (no_max_lsp_lifetime, + no_max_lsp_lifetime_cmd, + "no max-lsp-lifetime", + NO_STR + "LSP lifetime in seconds\n") +{ + return area_max_lsp_lifetime_set(vty, IS_LEVEL_1_AND_2, + DEFAULT_LSP_LIFETIME); +} + +ALIAS (no_max_lsp_lifetime, + no_max_lsp_lifetime_arg_cmd, + "no max-lsp-lifetime <350-65535>", + NO_STR + "Maximum LSP lifetime\n" + "LSP lifetime in seconds\n") + +DEFUN (max_lsp_lifetime_l1, + max_lsp_lifetime_l1_cmd, + "max-lsp-lifetime level-1 <350-65535>", + "Maximum LSP lifetime for Level 1 only\n" + "LSP lifetime for Level 1 only in seconds\n") +{ + return area_max_lsp_lifetime_set(vty, IS_LEVEL_1, atoi(argv[0])); +} + +DEFUN (no_max_lsp_lifetime_l1, + no_max_lsp_lifetime_l1_cmd, + "no max-lsp-lifetime level-1", + NO_STR + "LSP lifetime for Level 1 only in seconds\n") +{ + return area_max_lsp_lifetime_set(vty, IS_LEVEL_1, DEFAULT_LSP_LIFETIME); +} + +ALIAS (no_max_lsp_lifetime_l1, + no_max_lsp_lifetime_l1_arg_cmd, + "no max-lsp-lifetime level-1 <350-65535>", + NO_STR + "Maximum LSP lifetime for Level 1 only\n" + "LSP lifetime for Level 1 only in seconds\n") + +DEFUN (max_lsp_lifetime_l2, + max_lsp_lifetime_l2_cmd, + "max-lsp-lifetime level-2 <350-65535>", + "Maximum LSP lifetime for Level 2 only\n" + "LSP lifetime for Level 2 only in seconds\n") +{ + return area_max_lsp_lifetime_set(vty, IS_LEVEL_2, atoi(argv[0])); +} + +DEFUN (no_max_lsp_lifetime_l2, + no_max_lsp_lifetime_l2_cmd, + "no max-lsp-lifetime level-2", + NO_STR + "LSP lifetime for Level 2 only in seconds\n") +{ + return area_max_lsp_lifetime_set(vty, IS_LEVEL_2, DEFAULT_LSP_LIFETIME); +} + +ALIAS (no_max_lsp_lifetime_l2, + no_max_lsp_lifetime_l2_arg_cmd, + "no max-lsp-lifetime level-2 <350-65535>", + NO_STR + "Maximum LSP lifetime for Level 2 only\n" + "LSP lifetime for Level 2 only in seconds\n") + +static int +area_lsp_refresh_interval_set(struct vty *vty, int level, uint16_t interval) +{ + struct isis_area *area = vty->index; + int lvl; + + if (!area) + { + vty_out (vty, "Can't find ISIS instance %s", VTY_NEWLINE); + return CMD_ERR_NO_MATCH; + } + + for (lvl = IS_LEVEL_1; lvl <= IS_LEVEL_2; ++lvl) + { + if (!(lvl & level)) + continue; + if (interval <= area->lsp_gen_interval[lvl-1]) + { + vty_out (vty, "LSP refresh interval %us must be greater than " + "the configured LSP gen interval %us%s", + interval, area->lsp_gen_interval[lvl-1], + VTY_NEWLINE); + return CMD_ERR_AMBIGUOUS; + } + if (interval > (area->max_lsp_lifetime[lvl-1] - 300)) + { + vty_out (vty, "LSP refresh interval %us must be less than " + "the configured LSP lifetime %us less 300%s", + interval, area->max_lsp_lifetime[lvl-1], + VTY_NEWLINE); + return CMD_ERR_AMBIGUOUS; + } + } + + for (lvl = IS_LEVEL_1; lvl <= IS_LEVEL_2; ++lvl) + { + if (!(lvl & level)) + continue; + isis_area_lsp_refresh_set(area, lvl, interval); + } + + return CMD_SUCCESS; +} + +DEFUN (lsp_refresh_interval, + lsp_refresh_interval_cmd, + "lsp-refresh-interval <1-65235>", + "LSP refresh interval\n" + "LSP refresh interval in seconds\n") +{ + return area_lsp_refresh_interval_set(vty, IS_LEVEL_1_AND_2, atoi(argv[0])); +} + +DEFUN (no_lsp_refresh_interval, + no_lsp_refresh_interval_cmd, + "no lsp-refresh-interval", + NO_STR + "LSP refresh interval in seconds\n") +{ + return area_lsp_refresh_interval_set(vty, IS_LEVEL_1_AND_2, + DEFAULT_MAX_LSP_GEN_INTERVAL); +} + +ALIAS (no_lsp_refresh_interval, + no_lsp_refresh_interval_arg_cmd, + "no lsp-refresh-interval <1-65235>", + NO_STR + "LSP refresh interval\n" + "LSP refresh interval in seconds\n") + +DEFUN (lsp_refresh_interval_l1, + lsp_refresh_interval_l1_cmd, + "lsp-refresh-interval level-1 <1-65235>", + "LSP refresh interval for Level 1 only\n" + "LSP refresh interval for Level 1 only in seconds\n") +{ + return area_lsp_refresh_interval_set(vty, IS_LEVEL_1, atoi(argv[0])); +} + +DEFUN (no_lsp_refresh_interval_l1, + no_lsp_refresh_interval_l1_cmd, + "no lsp-refresh-interval level-1", + NO_STR + "LSP refresh interval for Level 1 only in seconds\n") +{ + return area_lsp_refresh_interval_set(vty, IS_LEVEL_1, + DEFAULT_MAX_LSP_GEN_INTERVAL); +} + +ALIAS (no_lsp_refresh_interval_l1, + no_lsp_refresh_interval_l1_arg_cmd, + "no lsp-refresh-interval level-1 <1-65235>", + NO_STR + "LSP refresh interval for Level 1 only\n" + "LSP refresh interval for Level 1 only in seconds\n") + +DEFUN (lsp_refresh_interval_l2, + lsp_refresh_interval_l2_cmd, + "lsp-refresh-interval level-2 <1-65235>", + "LSP refresh interval for Level 2 only\n" + "LSP refresh interval for Level 2 only in seconds\n") +{ + return area_lsp_refresh_interval_set(vty, IS_LEVEL_2, atoi(argv[0])); +} + +DEFUN (no_lsp_refresh_interval_l2, + no_lsp_refresh_interval_l2_cmd, + "no lsp-refresh-interval level-2", + NO_STR + "LSP refresh interval for Level 2 only in seconds\n") +{ + return area_lsp_refresh_interval_set(vty, IS_LEVEL_2, + DEFAULT_MAX_LSP_GEN_INTERVAL); +} + +ALIAS (no_lsp_refresh_interval_l2, + no_lsp_refresh_interval_l2_arg_cmd, + "no lsp-refresh-interval level-2 <1-65235>", + NO_STR + "LSP refresh interval for Level 2 only\n" + "LSP refresh interval for Level 2 only in seconds\n") + +static int +area_passwd_set(struct vty *vty, int level, + int (*type_set)(struct isis_area *area, int level, + const char *passwd, u_char snp_auth), + const char *passwd, u_char snp_auth) +{ + struct isis_area *area = vty->index; + + if (!area) + { + vty_out (vty, "Can't find IS-IS instance%s", VTY_NEWLINE); + return CMD_ERR_NO_MATCH; + } + + if (passwd && strlen(passwd) > 254) + { + vty_out (vty, "Too long area password (>254)%s", VTY_NEWLINE); + return CMD_ERR_AMBIGUOUS; + } + + type_set(area, level, passwd, snp_auth); + return CMD_SUCCESS; +} + +DEFUN (area_passwd_md5, + area_passwd_md5_cmd, + "(area-password|domain-password) md5 WORD", + "Configure the authentication password for an area\n" + "Set the authentication password for a routing domain\n" + "Authentication type\n" + "Level-wide password\n") +{ + u_char snp_auth = 0; + int level = (argv[0][0] == 'd') ? IS_LEVEL_2 : IS_LEVEL_1; + + if (argc > 2) + { + snp_auth = SNP_AUTH_SEND; + if (strncmp(argv[2], "v", 1) == 0) + snp_auth |= SNP_AUTH_RECV; + } + + return area_passwd_set(vty, level, isis_area_passwd_hmac_md5_set, + argv[1], snp_auth); +} + +ALIAS (area_passwd_md5, + area_passwd_md5_snpauth_cmd, + "(area-password|domain-password) md5 WORD authenticate snp (send-only|validate)", + "Configure the authentication password for an area\n" + "Set the authentication password for a routing domain\n" + "Authentication type\n" + "Level-wide password\n" + "Authentication\n" + "SNP PDUs\n" + "Send but do not check PDUs on receiving\n" + "Send and check PDUs on receiving\n") + +DEFUN (area_passwd_clear, + area_passwd_clear_cmd, + "(area-password|domain-password) clear WORD", + "Configure the authentication password for an area\n" + "Set the authentication password for a routing domain\n" + "Authentication type\n" + "Area password\n") +{ + u_char snp_auth = 0; + int level = (argv[0][0] == 'd') ? IS_LEVEL_2 : IS_LEVEL_1; + + if (argc > 2) + { + snp_auth = SNP_AUTH_SEND; + if (strncmp(argv[2], "v", 1) == 0) + snp_auth |= SNP_AUTH_RECV; + } + + return area_passwd_set(vty, level, isis_area_passwd_cleartext_set, + argv[1], snp_auth); +} + +ALIAS (area_passwd_clear, + area_passwd_clear_snpauth_cmd, + "(area-password|domain-password) clear WORD authenticate snp (send-only|validate)", + "Configure the authentication password for an area\n" + "Set the authentication password for a routing domain\n" + "Authentication type\n" + "Area password\n" + "Authentication\n" + "SNP PDUs\n" + "Send but do not check PDUs on receiving\n" + "Send and check PDUs on receiving\n") + +DEFUN (no_area_passwd, + no_area_passwd_cmd, + "no (area-password|domain-password)", + NO_STR + "Configure the authentication password for an area\n" + "Set the authentication password for a routing domain\n") +{ + int level = (argv[0][0] == 'd') ? IS_LEVEL_2 : IS_LEVEL_1; + struct isis_area *area = vty->index; + + if (!area) + { + vty_out (vty, "Can't find IS-IS instance%s", VTY_NEWLINE); + return CMD_ERR_NO_MATCH; + } + + return isis_area_passwd_unset (area, level); +} + +void +isis_vty_init (void) +{ + install_element (INTERFACE_NODE, &ip_router_isis_cmd); + install_element (INTERFACE_NODE, &no_ip_router_isis_cmd); + + install_element (INTERFACE_NODE, &isis_passive_cmd); + install_element (INTERFACE_NODE, &no_isis_passive_cmd); + + install_element (INTERFACE_NODE, &isis_circuit_type_cmd); + install_element (INTERFACE_NODE, &no_isis_circuit_type_cmd); + + install_element (INTERFACE_NODE, &isis_network_cmd); + install_element (INTERFACE_NODE, &no_isis_network_cmd); + + install_element (INTERFACE_NODE, &isis_passwd_cmd); + install_element (INTERFACE_NODE, &no_isis_passwd_cmd); + install_element (INTERFACE_NODE, &no_isis_passwd_arg_cmd); + + install_element (INTERFACE_NODE, &isis_priority_cmd); + install_element (INTERFACE_NODE, &no_isis_priority_cmd); + install_element (INTERFACE_NODE, &no_isis_priority_arg_cmd); + install_element (INTERFACE_NODE, &isis_priority_l1_cmd); + install_element (INTERFACE_NODE, &no_isis_priority_l1_cmd); + install_element (INTERFACE_NODE, &no_isis_priority_l1_arg_cmd); + install_element (INTERFACE_NODE, &isis_priority_l2_cmd); + install_element (INTERFACE_NODE, &no_isis_priority_l2_cmd); + install_element (INTERFACE_NODE, &no_isis_priority_l2_arg_cmd); + + install_element (INTERFACE_NODE, &isis_metric_cmd); + install_element (INTERFACE_NODE, &no_isis_metric_cmd); + install_element (INTERFACE_NODE, &no_isis_metric_arg_cmd); + install_element (INTERFACE_NODE, &isis_metric_l1_cmd); + install_element (INTERFACE_NODE, &no_isis_metric_l1_cmd); + install_element (INTERFACE_NODE, &no_isis_metric_l1_arg_cmd); + install_element (INTERFACE_NODE, &isis_metric_l2_cmd); + install_element (INTERFACE_NODE, &no_isis_metric_l2_cmd); + install_element (INTERFACE_NODE, &no_isis_metric_l2_arg_cmd); + + install_element (INTERFACE_NODE, &isis_hello_interval_cmd); + install_element (INTERFACE_NODE, &no_isis_hello_interval_cmd); + install_element (INTERFACE_NODE, &no_isis_hello_interval_arg_cmd); + install_element (INTERFACE_NODE, &isis_hello_interval_l1_cmd); + install_element (INTERFACE_NODE, &no_isis_hello_interval_l1_cmd); + install_element (INTERFACE_NODE, &no_isis_hello_interval_l1_arg_cmd); + install_element (INTERFACE_NODE, &isis_hello_interval_l2_cmd); + install_element (INTERFACE_NODE, &no_isis_hello_interval_l2_cmd); + install_element (INTERFACE_NODE, &no_isis_hello_interval_l2_arg_cmd); + + install_element (INTERFACE_NODE, &isis_hello_multiplier_cmd); + install_element (INTERFACE_NODE, &no_isis_hello_multiplier_cmd); + install_element (INTERFACE_NODE, &no_isis_hello_multiplier_arg_cmd); + install_element (INTERFACE_NODE, &isis_hello_multiplier_l1_cmd); + install_element (INTERFACE_NODE, &no_isis_hello_multiplier_l1_cmd); + install_element (INTERFACE_NODE, &no_isis_hello_multiplier_l1_arg_cmd); + install_element (INTERFACE_NODE, &isis_hello_multiplier_l2_cmd); + install_element (INTERFACE_NODE, &no_isis_hello_multiplier_l2_cmd); + install_element (INTERFACE_NODE, &no_isis_hello_multiplier_l2_arg_cmd); + + install_element (INTERFACE_NODE, &isis_hello_padding_cmd); + install_element (INTERFACE_NODE, &no_isis_hello_padding_cmd); + + install_element (INTERFACE_NODE, &csnp_interval_cmd); + install_element (INTERFACE_NODE, &no_csnp_interval_cmd); + install_element (INTERFACE_NODE, &no_csnp_interval_arg_cmd); + install_element (INTERFACE_NODE, &csnp_interval_l1_cmd); + install_element (INTERFACE_NODE, &no_csnp_interval_l1_cmd); + install_element (INTERFACE_NODE, &no_csnp_interval_l1_arg_cmd); + install_element (INTERFACE_NODE, &csnp_interval_l2_cmd); + install_element (INTERFACE_NODE, &no_csnp_interval_l2_cmd); + install_element (INTERFACE_NODE, &no_csnp_interval_l2_arg_cmd); + + install_element (INTERFACE_NODE, &psnp_interval_cmd); + install_element (INTERFACE_NODE, &no_psnp_interval_cmd); + install_element (INTERFACE_NODE, &no_psnp_interval_arg_cmd); + install_element (INTERFACE_NODE, &psnp_interval_l1_cmd); + install_element (INTERFACE_NODE, &no_psnp_interval_l1_cmd); + install_element (INTERFACE_NODE, &no_psnp_interval_l1_arg_cmd); + install_element (INTERFACE_NODE, &psnp_interval_l2_cmd); + install_element (INTERFACE_NODE, &no_psnp_interval_l2_cmd); + install_element (INTERFACE_NODE, &no_psnp_interval_l2_arg_cmd); + + install_element (ISIS_NODE, &metric_style_cmd); + install_element (ISIS_NODE, &no_metric_style_cmd); + + install_element (ISIS_NODE, &set_overload_bit_cmd); + install_element (ISIS_NODE, &no_set_overload_bit_cmd); + + install_element (ISIS_NODE, &set_attached_bit_cmd); + install_element (ISIS_NODE, &no_set_attached_bit_cmd); + + install_element (ISIS_NODE, &dynamic_hostname_cmd); + install_element (ISIS_NODE, &no_dynamic_hostname_cmd); + + install_element (ISIS_NODE, &area_lsp_mtu_cmd); + install_element (ISIS_NODE, &no_area_lsp_mtu_cmd); + install_element (ISIS_NODE, &no_area_lsp_mtu_arg_cmd); + + install_element (ISIS_NODE, &is_type_cmd); + install_element (ISIS_NODE, &no_is_type_cmd); + + install_element (ISIS_NODE, &lsp_gen_interval_cmd); + install_element (ISIS_NODE, &no_lsp_gen_interval_cmd); + install_element (ISIS_NODE, &no_lsp_gen_interval_arg_cmd); + install_element (ISIS_NODE, &lsp_gen_interval_l1_cmd); + install_element (ISIS_NODE, &no_lsp_gen_interval_l1_cmd); + install_element (ISIS_NODE, &no_lsp_gen_interval_l1_arg_cmd); + install_element (ISIS_NODE, &lsp_gen_interval_l2_cmd); + install_element (ISIS_NODE, &no_lsp_gen_interval_l2_cmd); + install_element (ISIS_NODE, &no_lsp_gen_interval_l2_arg_cmd); + + install_element (ISIS_NODE, &spf_interval_cmd); + install_element (ISIS_NODE, &no_spf_interval_cmd); + install_element (ISIS_NODE, &no_spf_interval_arg_cmd); + install_element (ISIS_NODE, &spf_interval_l1_cmd); + install_element (ISIS_NODE, &no_spf_interval_l1_cmd); + install_element (ISIS_NODE, &no_spf_interval_l1_arg_cmd); + install_element (ISIS_NODE, &spf_interval_l2_cmd); + install_element (ISIS_NODE, &no_spf_interval_l2_cmd); + install_element (ISIS_NODE, &no_spf_interval_l2_arg_cmd); + + install_element (ISIS_NODE, &max_lsp_lifetime_cmd); + install_element (ISIS_NODE, &no_max_lsp_lifetime_cmd); + install_element (ISIS_NODE, &no_max_lsp_lifetime_arg_cmd); + install_element (ISIS_NODE, &max_lsp_lifetime_l1_cmd); + install_element (ISIS_NODE, &no_max_lsp_lifetime_l1_cmd); + install_element (ISIS_NODE, &no_max_lsp_lifetime_l1_arg_cmd); + install_element (ISIS_NODE, &max_lsp_lifetime_l2_cmd); + install_element (ISIS_NODE, &no_max_lsp_lifetime_l2_cmd); + install_element (ISIS_NODE, &no_max_lsp_lifetime_l2_arg_cmd); + + install_element (ISIS_NODE, &lsp_refresh_interval_cmd); + install_element (ISIS_NODE, &no_lsp_refresh_interval_cmd); + install_element (ISIS_NODE, &no_lsp_refresh_interval_arg_cmd); + install_element (ISIS_NODE, &lsp_refresh_interval_l1_cmd); + install_element (ISIS_NODE, &no_lsp_refresh_interval_l1_cmd); + install_element (ISIS_NODE, &no_lsp_refresh_interval_l1_arg_cmd); + install_element (ISIS_NODE, &lsp_refresh_interval_l2_cmd); + install_element (ISIS_NODE, &no_lsp_refresh_interval_l2_cmd); + install_element (ISIS_NODE, &no_lsp_refresh_interval_l2_arg_cmd); + + install_element (ISIS_NODE, &area_passwd_md5_cmd); + install_element (ISIS_NODE, &area_passwd_md5_snpauth_cmd); + install_element (ISIS_NODE, &area_passwd_clear_cmd); + install_element (ISIS_NODE, &area_passwd_clear_snpauth_cmd); + install_element (ISIS_NODE, &no_area_passwd_cmd); +} diff --git a/isisd/isis_zebra.c b/isisd/isis_zebra.c index d5ccef9ea..40157b59a 100644 --- a/isisd/isis_zebra.c +++ b/isisd/isis_zebra.c @@ -4,6 +4,7 @@ * Copyright (C) 2001,2002 Sampo Saaristo * Tampere University of Technology * Institute of Communications Engineering + * Copyright (C) 2013-2015 Christian Franke * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public Licenseas published by the Free @@ -32,43 +33,60 @@ #include "zclient.h" #include "stream.h" #include "linklist.h" +#include "vrf.h" #include "isisd/dict.h" #include "isisd/isis_constants.h" #include "isisd/isis_common.h" +#include "isisd/isis_flags.h" +#include "isisd/isis_misc.h" +#include "isisd/isis_circuit.h" +#include "isisd/isis_tlv.h" #include "isisd/isisd.h" #include "isisd/isis_circuit.h" #include "isisd/isis_csm.h" +#include "isisd/isis_lsp.h" #include "isisd/isis_route.h" #include "isisd/isis_zebra.h" +#include "isisd/isis_te.h" struct zclient *zclient = NULL; -extern struct thread_master *master; -extern struct isis *isis; - -struct in_addr router_id_zebra; - /* Router-id update message from zebra. */ static int isis_router_id_update_zebra (int command, struct zclient *zclient, - zebra_size_t length) + zebra_size_t length, vrf_id_t vrf_id) { + struct isis_area *area; + struct listnode *node; struct prefix router_id; - zebra_router_id_update_read (zclient->ibuf,&router_id); - router_id_zebra = router_id.u.prefix4; + /* + * If ISIS TE is enable, TE Router ID is set through specific command. + * See mpls_te_router_addr() command in isis_te.c + */ + if (IS_MPLS_TE(isisMplsTE)) + return 0; + + zebra_router_id_update_read (zclient->ibuf, &router_id); + if (isis->router_id == router_id.u.prefix4.s_addr) + return 0; + + isis->router_id = router_id.u.prefix4.s_addr; + for (ALL_LIST_ELEMENTS_RO (isis->area_list, node, area)) + if (listcount (area->area_addrs) > 0) + lsp_regenerate_schedule (area, area->is_type, 0); - /* FIXME: Do we react somehow? */ return 0; } static int -isis_zebra_if_add (int command, struct zclient *zclient, zebra_size_t length) +isis_zebra_if_add (int command, struct zclient *zclient, zebra_size_t length, + vrf_id_t vrf_id) { struct interface *ifp; - ifp = zebra_interface_add_read (zclient->ibuf); + ifp = zebra_interface_add_read (zclient->ibuf, vrf_id); if (isis->debugs & DEBUG_ZEBRA) zlog_debug ("Zebra I/F add: %s index %d flags %ld metric %d mtu %d", @@ -81,13 +99,14 @@ isis_zebra_if_add (int command, struct zclient *zclient, zebra_size_t length) } static int -isis_zebra_if_del (int command, struct zclient *zclient, zebra_size_t length) +isis_zebra_if_del (int command, struct zclient *zclient, zebra_size_t length, + vrf_id_t vrf_id) { struct interface *ifp; struct stream *s; s = zclient->ibuf; - ifp = zebra_interface_state_read (s); + ifp = zebra_interface_state_read (s, vrf_id); if (!ifp) return 0; @@ -100,53 +119,28 @@ isis_zebra_if_del (int command, struct zclient *zclient, zebra_size_t length) zlog_debug ("Zebra I/F delete: %s index %d flags %ld metric %d mtu %d", ifp->name, ifp->ifindex, (long)ifp->flags, ifp->metric, ifp->mtu); + isis_csm_state_change (IF_DOWN_FROM_Z, circuit_scan_by_ifp (ifp), ifp); /* Cannot call if_delete because we should retain the pseudo interface in case there is configuration info attached to it. */ if_delete_retain(ifp); - isis_csm_state_change (IF_DOWN_FROM_Z, circuit_scan_by_ifp (ifp), ifp); - ifp->ifindex = IFINDEX_INTERNAL; return 0; } -static struct interface * -zebra_interface_if_lookup (struct stream *s) -{ - char ifname_tmp[INTERFACE_NAMSIZ]; - - /* Read interface name. */ - stream_get (ifname_tmp, s, INTERFACE_NAMSIZ); - - /* And look it up. */ - return if_lookup_by_name_len(ifname_tmp, - strnlen(ifname_tmp, INTERFACE_NAMSIZ)); -} - static int isis_zebra_if_state_up (int command, struct zclient *zclient, - zebra_size_t length) + zebra_size_t length, vrf_id_t vrf_id) { struct interface *ifp; - ifp = zebra_interface_if_lookup (zclient->ibuf); + ifp = zebra_interface_state_read (zclient->ibuf, vrf_id); - if (!ifp) + if (ifp == NULL) return 0; - if (if_is_operative (ifp)) - { - zebra_interface_if_set_value (zclient->ibuf, ifp); - /* HT: This is wrong actually. We can't assume that circuit exist - * if we delete circuit during if_state_down event. Needs rethink. - * TODO */ - isis_circuit_update_params (circuit_scan_by_ifp (ifp), ifp); - return 0; - } - - zebra_interface_if_set_value (zclient->ibuf, ifp); isis_csm_state_change (IF_UP_FROM_Z, circuit_scan_by_ifp (ifp), ifp); return 0; @@ -154,34 +148,34 @@ isis_zebra_if_state_up (int command, struct zclient *zclient, static int isis_zebra_if_state_down (int command, struct zclient *zclient, - zebra_size_t length) + zebra_size_t length, vrf_id_t vrf_id) { struct interface *ifp; + struct isis_circuit *circuit; - ifp = zebra_interface_if_lookup (zclient->ibuf); + ifp = zebra_interface_state_read (zclient->ibuf, vrf_id); if (ifp == NULL) return 0; - if (if_is_operative (ifp)) - { - zebra_interface_if_set_value (zclient->ibuf, ifp); - isis_csm_state_change (IF_DOWN_FROM_Z, circuit_scan_by_ifp (ifp), ifp); - } + circuit = isis_csm_state_change (IF_DOWN_FROM_Z, circuit_scan_by_ifp (ifp), + ifp); + if (circuit) + SET_FLAG(circuit->flags, ISIS_CIRCUIT_FLAPPED_AFTER_SPF); return 0; } static int isis_zebra_if_address_add (int command, struct zclient *zclient, - zebra_size_t length) + zebra_size_t length, vrf_id_t vrf_id) { struct connected *c; struct prefix *p; char buf[BUFSIZ]; c = zebra_interface_address_read (ZEBRA_INTERFACE_ADDRESS_ADD, - zclient->ibuf); + zclient->ibuf, vrf_id); if (c == NULL) return 0; @@ -205,7 +199,7 @@ isis_zebra_if_address_add (int command, struct zclient *zclient, static int isis_zebra_if_address_del (int command, struct zclient *client, - zebra_size_t length) + zebra_size_t length, vrf_id_t vrf_id) { struct connected *c; struct interface *ifp; @@ -215,7 +209,7 @@ isis_zebra_if_address_del (int command, struct zclient *client, #endif /* EXTREME_DEBUG */ c = zebra_interface_address_read (ZEBRA_INTERFACE_ADDRESS_DELETE, - zclient->ibuf); + zclient->ibuf, vrf_id); if (c == NULL) return 0; @@ -241,6 +235,23 @@ isis_zebra_if_address_del (int command, struct zclient *client, return 0; } +static int +isis_zebra_link_params (int command, struct zclient *zclient, + zebra_size_t length) +{ + struct interface *ifp; + + ifp = zebra_interface_link_params_read (zclient->ibuf); + + if (ifp == NULL) + return 0; + + /* Update TE TLV */ + isis_mpls_te_update(ifp); + + return 0; +} + static void isis_zebra_route_add_ipv4 (struct prefix *prefix, struct isis_route_info *route_info) @@ -251,10 +262,10 @@ isis_zebra_route_add_ipv4 (struct prefix *prefix, struct isis_nexthop *nexthop; struct listnode *node; - if (CHECK_FLAG (route_info->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNC)) + if (CHECK_FLAG (route_info->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNCED)) return; - if (zclient->redist[ZEBRA_ROUTE_ISIS]) + if (vrf_bitmap_check (zclient->redist[ZEBRA_ROUTE_ISIS], VRF_DEFAULT)) { message = 0; flags = 0; @@ -267,7 +278,7 @@ isis_zebra_route_add_ipv4 (struct prefix *prefix, stream = zclient->obuf; stream_reset (stream); - zclient_create_header (stream, ZEBRA_IPV4_ROUTE_ADD); + zclient_create_header (stream, ZEBRA_IPV4_ROUTE_ADD, VRF_DEFAULT); /* type */ stream_putc (stream, ZEBRA_ROUTE_ISIS); /* flags */ @@ -307,7 +318,8 @@ isis_zebra_route_add_ipv4 (struct prefix *prefix, stream_putw_at (stream, 0, stream_get_endp (stream)); zclient_send_message(zclient); - SET_FLAG (route_info->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNC); + SET_FLAG (route_info->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNCED); + UNSET_FLAG (route_info->flag, ISIS_ROUTE_FLAG_ZEBRA_RESYNC); } } @@ -318,8 +330,9 @@ isis_zebra_route_del_ipv4 (struct prefix *prefix, struct zapi_ipv4 api; struct prefix_ipv4 prefix4; - if (zclient->redist[ZEBRA_ROUTE_ISIS]) + if (vrf_bitmap_check (zclient->redist[ZEBRA_ROUTE_ISIS], VRF_DEFAULT)) { + api.vrf_id = VRF_DEFAULT; api.type = ZEBRA_ROUTE_ISIS; api.flags = 0; api.message = 0; @@ -329,27 +342,28 @@ isis_zebra_route_del_ipv4 (struct prefix *prefix, prefix4.prefix = prefix->u.prefix4; zapi_ipv4_route (ZEBRA_IPV4_ROUTE_DELETE, zclient, &prefix4, &api); } - UNSET_FLAG (route_info->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNC); + UNSET_FLAG (route_info->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNCED); return; } #ifdef HAVE_IPV6 -void +static void isis_zebra_route_add_ipv6 (struct prefix *prefix, struct isis_route_info *route_info) { struct zapi_ipv6 api; struct in6_addr **nexthop_list; - unsigned int *ifindex_list; + ifindex_t *ifindex_list; struct isis_nexthop6 *nexthop6; int i, size; struct listnode *node; struct prefix_ipv6 prefix6; - if (CHECK_FLAG (route_info->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNC)) + if (CHECK_FLAG (route_info->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNCED)) return; + api.vrf_id = VRF_DEFAULT; api.type = ZEBRA_ROUTE_ISIS; api.flags = 0; api.message = 0; @@ -376,7 +390,7 @@ isis_zebra_route_add_ipv6 (struct prefix *prefix, /* allocate memory for ifindex_list */ size = sizeof (unsigned int) * listcount (route_info->nexthops6); - ifindex_list = (unsigned int *) XMALLOC (MTYPE_ISIS_TMP, size); + ifindex_list = (ifindex_t *) XMALLOC (MTYPE_ISIS_TMP, size); if (!ifindex_list) { zlog_err ("isis_zebra_add_route_ipv6: out of memory!"); @@ -410,7 +424,8 @@ isis_zebra_route_add_ipv6 (struct prefix *prefix, prefix6.prefixlen = prefix->prefixlen; memcpy (&prefix6.prefix, &prefix->u.prefix6, sizeof (struct in6_addr)); zapi_ipv6_route (ZEBRA_IPV6_ROUTE_ADD, zclient, &prefix6, &api); - SET_FLAG (route_info->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNC); + SET_FLAG (route_info->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNCED); + UNSET_FLAG (route_info->flag, ISIS_ROUTE_FLAG_ZEBRA_RESYNC); } XFREE (MTYPE_ISIS_TMP, nexthop_list); @@ -425,15 +440,16 @@ isis_zebra_route_del_ipv6 (struct prefix *prefix, { struct zapi_ipv6 api; struct in6_addr **nexthop_list; - unsigned int *ifindex_list; + ifindex_t *ifindex_list; struct isis_nexthop6 *nexthop6; int i, size; struct listnode *node; struct prefix_ipv6 prefix6; - if (CHECK_FLAG (route_info->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNC)) + if (!CHECK_FLAG (route_info->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNCED)) return; + api.vrf_id = VRF_DEFAULT; api.type = ZEBRA_ROUTE_ISIS; api.flags = 0; api.message = 0; @@ -454,7 +470,7 @@ isis_zebra_route_del_ipv6 (struct prefix *prefix, /* allocate memory for ifindex_list */ size = sizeof (unsigned int) * listcount (route_info->nexthops6); - ifindex_list = (unsigned int *) XMALLOC (MTYPE_ISIS_TMP, size); + ifindex_list = (ifindex_t *) XMALLOC (MTYPE_ISIS_TMP, size); if (!ifindex_list) { zlog_err ("isis_zebra_route_del_ipv6: out of memory!"); @@ -488,7 +504,7 @@ isis_zebra_route_del_ipv6 (struct prefix *prefix, prefix6.prefixlen = prefix->prefixlen; memcpy (&prefix6.prefix, &prefix->u.prefix6, sizeof (struct in6_addr)); zapi_ipv6_route (ZEBRA_IPV6_ROUTE_DELETE, zclient, &prefix6, &api); - UNSET_FLAG (route_info->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNC); + UNSET_FLAG (route_info->flag, ISIS_ROUTE_FLAG_ZEBRA_SYNCED); } XFREE (MTYPE_ISIS_TMP, nexthop_list); @@ -504,7 +520,7 @@ isis_zebra_route_update (struct prefix *prefix, if (zclient->sock < 0) return; - if (!zclient->redist[ZEBRA_ROUTE_ISIS]) + if (!vrf_bitmap_check (zclient->redist[ZEBRA_ROUTE_ISIS], VRF_DEFAULT)) return; if (CHECK_FLAG (route_info->flag, ISIS_ROUTE_FLAG_ACTIVE)) @@ -530,16 +546,20 @@ isis_zebra_route_update (struct prefix *prefix, static int isis_zebra_read_ipv4 (int command, struct zclient *zclient, - zebra_size_t length) + zebra_size_t length, vrf_id_t vrf_id) { struct stream *stream; struct zapi_ipv4 api; struct prefix_ipv4 p; - unsigned long ifindex; - struct in_addr nexthop; + struct prefix *p_generic = (struct prefix*)&p; + unsigned long ifindex __attribute__ ((unused)); + struct in_addr nexthop __attribute__ ((unused)); + unsigned char plength = 0; stream = zclient->ibuf; + memset(&api, 0, sizeof(api)); memset (&p, 0, sizeof (struct prefix_ipv4)); + memset(&nexthop, 0, sizeof(nexthop)); ifindex = 0; api.type = stream_getc (stream); @@ -547,7 +567,8 @@ isis_zebra_read_ipv4 (int command, struct zclient *zclient, api.message = stream_getc (stream); p.family = AF_INET; - p.prefixlen = stream_getc (stream); + plength = stream_getc (stream); + p.prefixlen = MIN(IPV4_MAX_PREFIXLEN, plength); stream_get (&p.prefix, stream, PSIZE (p.prefixlen)); if (CHECK_FLAG (api.message, ZAPI_MESSAGE_NEXTHOP)) @@ -564,29 +585,80 @@ isis_zebra_read_ipv4 (int command, struct zclient *zclient, api.distance = stream_getc (stream); if (CHECK_FLAG (api.message, ZAPI_MESSAGE_METRIC)) api.metric = stream_getl (stream); - else - api.metric = 0; + + /* + * Avoid advertising a false default reachability. (A default + * route installed by IS-IS gets redistributed from zebra back + * into IS-IS causing us to start advertising default reachabity + * without this check) + */ + if (p.prefixlen == 0 && api.type == ZEBRA_ROUTE_ISIS) + command = ZEBRA_IPV4_ROUTE_DELETE; if (command == ZEBRA_IPV4_ROUTE_ADD) - { - if (isis->debugs & DEBUG_ZEBRA) - zlog_debug ("IPv4 Route add from Z"); - } + isis_redist_add(api.type, p_generic, api.distance, api.metric); + else + isis_redist_delete(api.type, p_generic); return 0; } -#ifdef HAVE_IPV6 static int isis_zebra_read_ipv6 (int command, struct zclient *zclient, - zebra_size_t length) + zebra_size_t length, vrf_id_t vrf_id) { + struct stream *stream; + struct zapi_ipv6 api; + struct prefix_ipv6 p; + struct prefix *p_generic = (struct prefix*)&p; + struct in6_addr nexthop; + unsigned long ifindex __attribute__((unused)); + + stream = zclient->ibuf; + memset(&api, 0, sizeof(api)); + memset(&p, 0, sizeof(struct prefix_ipv6)); + memset(&nexthop, 0, sizeof(nexthop)); + ifindex = 0; + + api.type = stream_getc(stream); + api.flags = stream_getc(stream); + api.message = stream_getc(stream); + + p.family = AF_INET6; + p.prefixlen = stream_getc(stream); + stream_get(&p.prefix, stream, PSIZE(p.prefixlen)); + + if (CHECK_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP)) + { + api.nexthop_num = stream_getc(stream); /* this is always 1 */ + stream_get(&nexthop, stream, sizeof(nexthop)); + } + if (CHECK_FLAG(api.message, ZAPI_MESSAGE_IFINDEX)) + { + api.ifindex_num = stream_getc(stream); + ifindex = stream_getl(stream); + } + if (CHECK_FLAG(api.message, ZAPI_MESSAGE_DISTANCE)) + api.distance = stream_getc(stream); + if (CHECK_FLAG(api.message, ZAPI_MESSAGE_METRIC)) + api.metric = stream_getl(stream); + + /* + * Avoid advertising a false default reachability. (A default + * route installed by IS-IS gets redistributed from zebra back + * into IS-IS causing us to start advertising default reachabity + * without this check) + */ + if (p.prefixlen == 0 && api.type == ZEBRA_ROUTE_ISIS) + command = ZEBRA_IPV6_ROUTE_DELETE; + + if (command == ZEBRA_IPV6_ROUTE_ADD) + isis_redist_add(api.type, p_generic, api.distance, api.metric); + else + isis_redist_delete(api.type, p_generic); + return 0; } -#endif - -#define ISIS_TYPE_IS_REDISTRIBUTED(T) \ -T == ZEBRA_ROUTE_MAX ? zclient->default_information : zclient->redist[type] int isis_distribute_list_update (int routetype) @@ -594,20 +666,36 @@ isis_distribute_list_update (int routetype) return 0; } -#if 0 /* Not yet. */ -static int -isis_redistribute_default_set (int routetype, int metric_type, - int metric_value) +void +isis_zebra_redistribute_set(int type) { - return 0; + if (type == DEFAULT_ROUTE) + zclient_redistribute_default(ZEBRA_REDISTRIBUTE_DEFAULT_ADD, zclient, VRF_DEFAULT); + else + zclient_redistribute(ZEBRA_REDISTRIBUTE_ADD, zclient, type, VRF_DEFAULT); +} + +void +isis_zebra_redistribute_unset(int type) +{ + if (type == DEFAULT_ROUTE) + zclient_redistribute_default(ZEBRA_REDISTRIBUTE_DEFAULT_DELETE, zclient, VRF_DEFAULT); + else + zclient_redistribute(ZEBRA_REDISTRIBUTE_DELETE, zclient, type, VRF_DEFAULT); +} + +static void +isis_zebra_connected (struct zclient *zclient) +{ + zclient_send_requests (zclient, VRF_DEFAULT); } -#endif /* 0 */ void -isis_zebra_init () +isis_zebra_init (struct thread_master *master) { - zclient = zclient_new (); + zclient = zclient_new (master); zclient_init (zclient, ZEBRA_ROUTE_ISIS); + zclient->zebra_connected = isis_zebra_connected; zclient->router_id_update = isis_router_id_update_zebra; zclient->interface_add = isis_zebra_if_add; zclient->interface_delete = isis_zebra_if_del; @@ -615,6 +703,7 @@ isis_zebra_init () zclient->interface_down = isis_zebra_if_state_down; zclient->interface_address_add = isis_zebra_if_address_add; zclient->interface_address_delete = isis_zebra_if_address_del; + zclient->interface_link_params = isis_zebra_link_params; zclient->ipv4_route_add = isis_zebra_read_ipv4; zclient->ipv4_route_delete = isis_zebra_read_ipv4; #ifdef HAVE_IPV6 diff --git a/isisd/isis_zebra.h b/isisd/isis_zebra.h index 889cd9b62..dd36d7d51 100644 --- a/isisd/isis_zebra.h +++ b/isisd/isis_zebra.h @@ -24,9 +24,11 @@ extern struct zclient *zclient; -void isis_zebra_init (void); +void isis_zebra_init (struct thread_master *); void isis_zebra_route_update (struct prefix *prefix, struct isis_route_info *route_info); int isis_distribute_list_update (int routetype); +void isis_zebra_redistribute_set(int type); +void isis_zebra_redistribute_unset(int type); #endif /* _ZEBRA_ISIS_ZEBRA_H */ diff --git a/isisd/isisd.c b/isisd/isisd.c index 20a328092..9844ae5b0 100644 --- a/isisd/isisd.c +++ b/isisd/isisd.c @@ -27,6 +27,7 @@ #include "command.h" #include "log.h" #include "memory.h" +#include "time.h" #include "linklist.h" #include "if.h" #include "hash.h" @@ -38,8 +39,9 @@ #include "isisd/include-netbsd/iso.h" #include "isisd/isis_constants.h" #include "isisd/isis_common.h" -#include "isisd/isis_circuit.h" #include "isisd/isis_flags.h" +#include "isisd/isis_circuit.h" +#include "isisd/isis_csm.h" #include "isisd/isisd.h" #include "isisd/isis_dynhn.h" #include "isisd/isis_adjacency.h" @@ -52,7 +54,7 @@ #include "isisd/isis_route.h" #include "isisd/isis_zebra.h" #include "isisd/isis_events.h" -#include "isisd/isis_csm.h" +#include "isisd/isis_te.h" #ifdef TOPOLOGY_GENERATE #include "spgrid.h" @@ -60,19 +62,17 @@ u_char DEFAULT_TOPOLOGY_BASEIS[6] = { 0xFE, 0xED, 0xFE, 0xED, 0x00, 0x00 }; #endif /* TOPOLOGY_GENERATE */ struct isis *isis = NULL; -extern struct thread_master *master; /* * Prototypes. */ -void isis_new(unsigned long); -struct isis_area *isis_area_create(void); int isis_area_get(struct vty *, const char *); int isis_area_destroy(struct vty *, const char *); -int area_net_title(struct vty *, const u_char *); -int area_clear_net_title(struct vty *, const u_char *); -int show_clns_neigh(struct vty *, char); -void print_debug(struct vty *, int, int); +int area_net_title(struct vty *, const char *); +int area_clear_net_title(struct vty *, const char *); +int show_isis_interface_common(struct vty *, const char *ifname, char); +int show_isis_neighbor_common(struct vty *, const char *id, char); +int clear_isis_neighbor_common(struct vty *, const char *id); int isis_config_write(struct vty *); @@ -85,8 +85,8 @@ isis_new (unsigned long process_id) * Default values */ isis->max_area_addrs = 3; - isis->process_id = process_id; + isis->router_id = 0; isis->area_list = list_new (); isis->init_circ_list = list_new (); isis->uptime = time (NULL); @@ -94,14 +94,16 @@ isis_new (unsigned long process_id) #ifdef HAVE_IPV6 isis->nexthops6 = list_new (); #endif /* HAVE_IPV6 */ + dyn_cache_init (); /* * uncomment the next line for full debugs */ /* isis->debugs = 0xFFFF; */ + isisMplsTE.status = disable; /* Only support TE metric */ } struct isis_area * -isis_area_create () +isis_area_create (const char *area_tag) { struct isis_area *area; @@ -115,43 +117,57 @@ isis_area_create () area->is_type = IS_LEVEL_1; else area->is_type = IS_LEVEL_1_AND_2; + /* * intialize the databases */ - area->lspdb[0] = lsp_db_init (); - area->lspdb[1] = lsp_db_init (); - - spftree_area_init (area); - area->route_table[0] = route_table_init (); - area->route_table[1] = route_table_init (); + if (area->is_type & IS_LEVEL_1) + { + area->lspdb[0] = lsp_db_init (); + area->route_table[0] = route_table_init (); +#ifdef HAVE_IPV6 + area->route_table6[0] = route_table_init (); +#endif /* HAVE_IPV6 */ + } + if (area->is_type & IS_LEVEL_2) + { + area->lspdb[1] = lsp_db_init (); + area->route_table[1] = route_table_init (); #ifdef HAVE_IPV6 - area->route_table6[0] = route_table_init (); - area->route_table6[1] = route_table_init (); + area->route_table6[1] = route_table_init (); #endif /* HAVE_IPV6 */ + } + + spftree_area_init (area); + area->circuit_list = list_new (); area->area_addrs = list_new (); THREAD_TIMER_ON (master, area->t_tick, lsp_tick, area, 1); flags_initialize (&area->flags); + /* * Default values */ - area->max_lsp_lifetime[0] = MAX_AGE; /* 1200 */ - area->max_lsp_lifetime[1] = MAX_AGE; /* 1200 */ - area->lsp_gen_interval[0] = LSP_GEN_INTERVAL_DEFAULT; - area->lsp_gen_interval[1] = LSP_GEN_INTERVAL_DEFAULT; - area->lsp_refresh[0] = MAX_LSP_GEN_INTERVAL; /* 900 */ - area->lsp_refresh[1] = MAX_LSP_GEN_INTERVAL; /* 900 */ + area->max_lsp_lifetime[0] = DEFAULT_LSP_LIFETIME; /* 1200 */ + area->max_lsp_lifetime[1] = DEFAULT_LSP_LIFETIME; /* 1200 */ + area->lsp_refresh[0] = DEFAULT_MAX_LSP_GEN_INTERVAL; /* 900 */ + area->lsp_refresh[1] = DEFAULT_MAX_LSP_GEN_INTERVAL; /* 900 */ + area->lsp_gen_interval[0] = DEFAULT_MIN_LSP_GEN_INTERVAL; + area->lsp_gen_interval[1] = DEFAULT_MIN_LSP_GEN_INTERVAL; area->min_spf_interval[0] = MINIMUM_SPF_INTERVAL; area->min_spf_interval[1] = MINIMUM_SPF_INTERVAL; area->dynhostname = 1; - area->oldmetric = 1; + area->oldmetric = 0; + area->newmetric = 1; area->lsp_frag_threshold = 90; + area->lsp_mtu = DEFAULT_LSP_MTU; #ifdef TOPOLOGY_GENERATE memcpy (area->topology_baseis, DEFAULT_TOPOLOGY_BASEIS, ISIS_SYS_ID_LEN); #endif /* TOPOLOGY_GENERATE */ - /* FIXME: Think of a better way... */ - area->min_bcast_mtu = 1497; + area->area_tag = strdup (area_tag); + listnode_add (isis->area_list, area); + area->isis = isis; return area; } @@ -185,9 +201,7 @@ isis_area_get (struct vty *vty, const char *area_tag) return CMD_SUCCESS; } - area = isis_area_create (); - area->area_tag = strdup (area_tag); - listnode_add (isis->area_list, area); + area = isis_area_create (area_tag); if (isis->debugs & DEBUG_EVENTS) zlog_debug ("New IS-IS area instance %s", area->area_tag); @@ -204,50 +218,102 @@ isis_area_destroy (struct vty *vty, const char *area_tag) struct isis_area *area; struct listnode *node, *nnode; struct isis_circuit *circuit; + struct area_addr *addr; area = isis_area_lookup (area_tag); if (area == NULL) { vty_out (vty, "Can't find ISIS instance %s", VTY_NEWLINE); - return CMD_WARNING; + return CMD_ERR_NO_MATCH; } if (area->circuit_list) { for (ALL_LIST_ELEMENTS (area->circuit_list, node, nnode, circuit)) - { - /* The fact that it's in circuit_list means that it was configured */ - isis_csm_state_change (ISIS_DISABLE, circuit, area); - isis_circuit_down (circuit); - isis_circuit_deconfigure (circuit, area); - } - + { + circuit->ip_router = 0; +#ifdef HAVE_IPV6 + circuit->ipv6_router = 0; +#endif + isis_csm_state_change (ISIS_DISABLE, circuit, area); + } list_delete (area->circuit_list); + area->circuit_list = NULL; + } + + if (area->lspdb[0] != NULL) + { + lsp_db_destroy (area->lspdb[0]); + area->lspdb[0] = NULL; + } + if (area->lspdb[1] != NULL) + { + lsp_db_destroy (area->lspdb[1]); + area->lspdb[1] = NULL; } - listnode_delete (isis->area_list, area); + + spftree_area_del (area); + + /* invalidate and validate would delete all routes from zebra */ + isis_route_invalidate (area); + isis_route_validate (area); + + if (area->route_table[0]) + { + route_table_finish (area->route_table[0]); + area->route_table[0] = NULL; + } + if (area->route_table[1]) + { + route_table_finish (area->route_table[1]); + area->route_table[1] = NULL; + } +#ifdef HAVE_IPV6 + if (area->route_table6[0]) + { + route_table_finish (area->route_table6[0]); + area->route_table6[0] = NULL; + } + if (area->route_table6[1]) + { + route_table_finish (area->route_table6[1]); + area->route_table6[1] = NULL; + } +#endif /* HAVE_IPV6 */ + + isis_redist_area_finish(area); + + for (ALL_LIST_ELEMENTS (area->area_addrs, node, nnode, addr)) + { + list_delete_node (area->area_addrs, node); + XFREE (MTYPE_ISIS_AREA_ADDR, addr); + } + area->area_addrs = NULL; THREAD_TIMER_OFF (area->t_tick); - if (area->t_remove_aged) - thread_cancel (area->t_remove_aged); THREAD_TIMER_OFF (area->t_lsp_refresh[0]); THREAD_TIMER_OFF (area->t_lsp_refresh[1]); - THREAD_TIMER_OFF (area->spftree[0]->t_spf); - THREAD_TIMER_OFF (area->spftree[1]->t_spf); + thread_cancel_event (master, area); + + listnode_delete (isis->area_list, area); - THREAD_TIMER_OFF (area->t_lsp_l1_regenerate); - THREAD_TIMER_OFF (area->t_lsp_l2_regenerate); + free (area->area_tag); XFREE (MTYPE_ISIS_AREA, area); - isis->sysid_set=0; + if (listcount (isis->area_list) == 0) + { + memset (isis->sysid, 0, ISIS_SYS_ID_LEN); + isis->sysid_set = 0; + } return CMD_SUCCESS; } int -area_net_title (struct vty *vty, const u_char *net_title) +area_net_title (struct vty *vty, const char *net_title) { struct isis_area *area; struct area_addr *addr; @@ -260,7 +326,7 @@ area_net_title (struct vty *vty, const u_char *net_title) if (!area) { vty_out (vty, "Can't find ISIS instance %s", VTY_NEWLINE); - return CMD_WARNING; + return CMD_ERR_NO_MATCH; } /* We check that we are not over the maximal number of addresses */ @@ -268,7 +334,7 @@ area_net_title (struct vty *vty, const u_char *net_title) { vty_out (vty, "Maximum of area addresses (%d) already reached %s", isis->max_area_addrs, VTY_NEWLINE); - return CMD_WARNING; + return CMD_ERR_NOTHING_TODO; } addr = XMALLOC (MTYPE_ISIS_AREA_ADDR, sizeof (struct area_addr)); @@ -280,10 +346,18 @@ area_net_title (struct vty *vty, const u_char *net_title) #endif /* EXTREME_DEBUG */ if (addr->addr_len < 8 || addr->addr_len > 20) { - zlog_warn ("area address must be at least 8..20 octets long (%d)", - addr->addr_len); + vty_out (vty, "area address must be at least 8..20 octets long (%d)%s", + addr->addr_len, VTY_NEWLINE); + XFREE (MTYPE_ISIS_AREA_ADDR, addr); + return CMD_ERR_AMBIGUOUS; + } + + if (addr->area_addr[addr->addr_len-1] != 0) + { + vty_out (vty, "nsel byte (last byte) in area address must be 0%s", + VTY_NEWLINE); XFREE (MTYPE_ISIS_AREA_ADDR, addr); - return CMD_WARNING; + return CMD_ERR_AMBIGUOUS; } if (isis->sysid_set == 0) @@ -291,7 +365,7 @@ area_net_title (struct vty *vty, const u_char *net_title) /* * First area address - get the SystemID for this router */ - memcpy (isis->sysid, GETSYSID (addr, ISIS_SYS_ID_LEN), ISIS_SYS_ID_LEN); + memcpy (isis->sysid, GETSYSID (addr), ISIS_SYS_ID_LEN); isis->sysid_set = 1; if (isis->debugs & DEBUG_EVENTS) zlog_debug ("Router has SystemID %s", sysid_print (isis->sysid)); @@ -301,20 +375,19 @@ area_net_title (struct vty *vty, const u_char *net_title) /* * Check that the SystemID portions match */ - if (memcmp (isis->sysid, GETSYSID (addr, ISIS_SYS_ID_LEN), - ISIS_SYS_ID_LEN)) + if (memcmp (isis->sysid, GETSYSID (addr), ISIS_SYS_ID_LEN)) { vty_out (vty, "System ID must not change when defining additional area" " addresses%s", VTY_NEWLINE); XFREE (MTYPE_ISIS_AREA_ADDR, addr); - return CMD_WARNING; + return CMD_ERR_AMBIGUOUS; } /* now we see that we don't already have this address */ for (ALL_LIST_ELEMENTS_RO (area->area_addrs, node, addrp)) { - if ((addrp->addr_len + ISIS_SYS_ID_LEN + 1) != (addr->addr_len)) + if ((addrp->addr_len + ISIS_SYS_ID_LEN + ISIS_NSEL_LEN) != (addr->addr_len)) continue; if (!memcmp (addrp->area_addr, addr->area_addr, addr->addr_len)) { @@ -322,26 +395,28 @@ area_net_title (struct vty *vty, const u_char *net_title) return CMD_SUCCESS; /* silent fail */ } } - } + /* * Forget the systemID part of the address */ - addr->addr_len -= (ISIS_SYS_ID_LEN + 1); + addr->addr_len -= (ISIS_SYS_ID_LEN + ISIS_NSEL_LEN); listnode_add (area->area_addrs, addr); /* only now we can safely generate our LSPs for this area */ if (listcount (area->area_addrs) > 0) { - lsp_l1_generate (area); - lsp_l2_generate (area); + if (area->is_type & IS_LEVEL_1) + lsp_generate (area, IS_LEVEL_1); + if (area->is_type & IS_LEVEL_2) + lsp_generate (area, IS_LEVEL_2); } return CMD_SUCCESS; } int -area_clear_net_title (struct vty *vty, const u_char *net_title) +area_clear_net_title (struct vty *vty, const char *net_title) { struct isis_area *area; struct area_addr addr, *addrp = NULL; @@ -352,7 +427,7 @@ area_clear_net_title (struct vty *vty, const u_char *net_title) if (!area) { vty_out (vty, "Can't find ISIS instance %s", VTY_NEWLINE); - return CMD_WARNING; + return CMD_ERR_NO_MATCH; } addr.addr_len = dotformat2buff (buff, net_title); @@ -360,13 +435,13 @@ area_clear_net_title (struct vty *vty, const u_char *net_title) { vty_out (vty, "Unsupported area address length %d, should be 8...20 %s", addr.addr_len, VTY_NEWLINE); - return CMD_WARNING; + return CMD_ERR_AMBIGUOUS; } memcpy (addr.area_addr, buff, (int) addr.addr_len); for (ALL_LIST_ELEMENTS_RO (area->area_addrs, node, addrp)) - if (addrp->addr_len == addr.addr_len && + if ((addrp->addr_len + ISIS_SYS_ID_LEN + 1) == addr.addr_len && !memcmp (addrp->area_addr, addr.area_addr, addr.addr_len)) break; @@ -374,26 +449,36 @@ area_clear_net_title (struct vty *vty, const u_char *net_title) { vty_out (vty, "No area address %s for area %s %s", net_title, area->area_tag, VTY_NEWLINE); - return CMD_WARNING; + return CMD_ERR_NO_MATCH; } listnode_delete (area->area_addrs, addrp); + XFREE (MTYPE_ISIS_AREA_ADDR, addrp); + + /* + * Last area address - reset the SystemID for this router + */ + if (listcount (area->area_addrs) == 0) + { + memset (isis->sysid, 0, ISIS_SYS_ID_LEN); + isis->sysid_set = 0; + if (isis->debugs & DEBUG_EVENTS) + zlog_debug ("Router has no SystemID"); + } return CMD_SUCCESS; } /* - * 'show clns neighbors' command + * 'show isis interface' command */ int -show_clns_neigh (struct vty *vty, char detail) +show_isis_interface_common (struct vty *vty, const char *ifname, char detail) { struct listnode *anode, *cnode; struct isis_area *area; struct isis_circuit *circuit; - struct list *db; - int i; if (!isis) { @@ -406,92 +491,246 @@ show_clns_neigh (struct vty *vty, char detail) vty_out (vty, "Area %s:%s", area->area_tag, VTY_NEWLINE); if (detail == ISIS_UI_LEVEL_BRIEF) - vty_out (vty, " System Id Interface L State " - "Holdtime SNPA%s", VTY_NEWLINE); + vty_out (vty, " Interface CircId State Type Level%s", + VTY_NEWLINE); for (ALL_LIST_ELEMENTS_RO (area->circuit_list, cnode, circuit)) - { - if (circuit->circ_type == CIRCUIT_T_BROADCAST) - { - for (i = 0; i < 2; i++) - { - db = circuit->u.bc.adjdb[i]; - if (db && db->count) - { - if (detail == ISIS_UI_LEVEL_BRIEF) - isis_adjdb_iterate (db, - (void (*) - (struct isis_adjacency *, - void *)) isis_adj_print_vty, - vty); - if (detail == ISIS_UI_LEVEL_DETAIL) - isis_adjdb_iterate (db, - (void (*) - (struct isis_adjacency *, - void *)) - isis_adj_print_vty_detail, vty); - if (detail == ISIS_UI_LEVEL_EXTENSIVE) - isis_adjdb_iterate (db, - (void (*) - (struct isis_adjacency *, - void *)) - isis_adj_print_vty_extensive, - vty); - } - } - } - else if (circuit->circ_type == CIRCUIT_T_P2P && - circuit->u.p2p.neighbor) - { - if (detail == ISIS_UI_LEVEL_BRIEF) - isis_adj_p2p_print_vty (circuit->u.p2p.neighbor, vty); - if (detail == ISIS_UI_LEVEL_DETAIL) - isis_adj_p2p_print_vty_detail (circuit->u.p2p.neighbor, vty); - if (detail == ISIS_UI_LEVEL_EXTENSIVE) - isis_adj_p2p_print_vty_extensive (circuit->u.p2p.neighbor, - vty); - } - } + if (!ifname) + isis_circuit_print_vty (circuit, vty, detail); + else if (strcmp(circuit->interface->name, ifname) == 0) + isis_circuit_print_vty (circuit, vty, detail); } return CMD_SUCCESS; } -DEFUN (show_clns_neighbors, - show_clns_neighbors_cmd, - "show clns neighbors", +DEFUN (show_isis_interface, + show_isis_interface_cmd, + "show isis interface", SHOW_STR - "clns network information\n" - "CLNS neighbor adjacencies\n") + "ISIS network information\n" + "ISIS interface\n") { - return show_clns_neigh (vty, ISIS_UI_LEVEL_BRIEF); + return show_isis_interface_common (vty, NULL, ISIS_UI_LEVEL_BRIEF); } -ALIAS (show_clns_neighbors, - show_isis_neighbors_cmd, - "show isis neighbors", +DEFUN (show_isis_interface_detail, + show_isis_interface_detail_cmd, + "show isis interface detail", SHOW_STR - "IS-IS network information\n" - "IS-IS neighbor adjacencies\n") + "ISIS network information\n" + "ISIS interface\n" + "show detailed information\n") +{ + return show_isis_interface_common (vty, NULL, ISIS_UI_LEVEL_DETAIL); +} -DEFUN (show_clns_neighbors_detail, - show_clns_neighbors_detail_cmd, - "show clns neighbors detail", +DEFUN (show_isis_interface_arg, + show_isis_interface_arg_cmd, + "show isis interface WORD", SHOW_STR - "clns network information\n" - "CLNS neighbor adjacencies\n" - "show detailed information\n") + "ISIS network information\n" + "ISIS interface\n" + "ISIS interface name\n") +{ + return show_isis_interface_common (vty, argv[0], ISIS_UI_LEVEL_DETAIL); +} + +/* + * 'show isis neighbor' command + */ + +int +show_isis_neighbor_common (struct vty *vty, const char *id, char detail) +{ + struct listnode *anode, *cnode, *node; + struct isis_area *area; + struct isis_circuit *circuit; + struct list *adjdb; + struct isis_adjacency *adj; + struct isis_dynhn *dynhn; + u_char sysid[ISIS_SYS_ID_LEN]; + int i; + + if (!isis) + { + vty_out (vty, "IS-IS Routing Process not enabled%s", VTY_NEWLINE); + return CMD_SUCCESS; + } + + memset (sysid, 0, ISIS_SYS_ID_LEN); + if (id) + { + if (sysid2buff (sysid, id) == 0) + { + dynhn = dynhn_find_by_name (id); + if (dynhn == NULL) + { + vty_out (vty, "Invalid system id %s%s", id, VTY_NEWLINE); + return CMD_SUCCESS; + } + memcpy (sysid, dynhn->id, ISIS_SYS_ID_LEN); + } + } + + for (ALL_LIST_ELEMENTS_RO (isis->area_list, anode, area)) + { + vty_out (vty, "Area %s:%s", area->area_tag, VTY_NEWLINE); + + if (detail == ISIS_UI_LEVEL_BRIEF) + vty_out (vty, " System Id Interface L State" + " Holdtime SNPA%s", VTY_NEWLINE); + + for (ALL_LIST_ELEMENTS_RO (area->circuit_list, cnode, circuit)) + { + if (circuit->circ_type == CIRCUIT_T_BROADCAST) + { + for (i = 0; i < 2; i++) + { + adjdb = circuit->u.bc.adjdb[i]; + if (adjdb && adjdb->count) + { + for (ALL_LIST_ELEMENTS_RO (adjdb, node, adj)) + if (!id || !memcmp (adj->sysid, sysid, + ISIS_SYS_ID_LEN)) + isis_adj_print_vty (adj, vty, detail); + } + } + } + else if (circuit->circ_type == CIRCUIT_T_P2P && + circuit->u.p2p.neighbor) + { + adj = circuit->u.p2p.neighbor; + if (!id || !memcmp (adj->sysid, sysid, ISIS_SYS_ID_LEN)) + isis_adj_print_vty (adj, vty, detail); + } + } + } + + return CMD_SUCCESS; +} + +/* + * 'clear isis neighbor' command + */ +int +clear_isis_neighbor_common (struct vty *vty, const char *id) +{ + struct listnode *anode, *cnode, *cnextnode, *node, *nnode; + struct isis_area *area; + struct isis_circuit *circuit; + struct list *adjdb; + struct isis_adjacency *adj; + struct isis_dynhn *dynhn; + u_char sysid[ISIS_SYS_ID_LEN]; + int i; + + if (!isis) + { + vty_out (vty, "IS-IS Routing Process not enabled%s", VTY_NEWLINE); + return CMD_SUCCESS; + } + + memset (sysid, 0, ISIS_SYS_ID_LEN); + if (id) + { + if (sysid2buff (sysid, id) == 0) + { + dynhn = dynhn_find_by_name (id); + if (dynhn == NULL) + { + vty_out (vty, "Invalid system id %s%s", id, VTY_NEWLINE); + return CMD_SUCCESS; + } + memcpy (sysid, dynhn->id, ISIS_SYS_ID_LEN); + } + } + + for (ALL_LIST_ELEMENTS_RO (isis->area_list, anode, area)) + { + for (ALL_LIST_ELEMENTS (area->circuit_list, cnode, cnextnode, circuit)) + { + if (circuit->circ_type == CIRCUIT_T_BROADCAST) + { + for (i = 0; i < 2; i++) + { + adjdb = circuit->u.bc.adjdb[i]; + if (adjdb && adjdb->count) + { + for (ALL_LIST_ELEMENTS (adjdb, node, nnode, adj)) + if (!id || !memcmp (adj->sysid, sysid, ISIS_SYS_ID_LEN)) + isis_adj_state_change (adj, ISIS_ADJ_DOWN, + "clear user request"); + } + } + } + else if (circuit->circ_type == CIRCUIT_T_P2P && + circuit->u.p2p.neighbor) + { + adj = circuit->u.p2p.neighbor; + if (!id || !memcmp (adj->sysid, sysid, ISIS_SYS_ID_LEN)) + isis_adj_state_change (adj, ISIS_ADJ_DOWN, + "clear user request"); + } + } + } + + return CMD_SUCCESS; +} + +DEFUN (show_isis_neighbor, + show_isis_neighbor_cmd, + "show isis neighbor", + SHOW_STR + "ISIS network information\n" + "ISIS neighbor adjacencies\n") { - return show_clns_neigh (vty, ISIS_UI_LEVEL_DETAIL); + return show_isis_neighbor_common (vty, NULL, ISIS_UI_LEVEL_BRIEF); } -ALIAS (show_clns_neighbors_detail, - show_isis_neighbors_detail_cmd, - "show isis neighbors detail", +DEFUN (show_isis_neighbor_detail, + show_isis_neighbor_detail_cmd, + "show isis neighbor detail", SHOW_STR - "IS-IS network information\n" - "IS-IS neighbor adjacencies\n" + "ISIS network information\n" + "ISIS neighbor adjacencies\n" "show detailed information\n") +{ + return show_isis_neighbor_common (vty, NULL, ISIS_UI_LEVEL_DETAIL); +} + +DEFUN (show_isis_neighbor_arg, + show_isis_neighbor_arg_cmd, + "show isis neighbor WORD", + SHOW_STR + "ISIS network information\n" + "ISIS neighbor adjacencies\n" + "System id\n") +{ + return show_isis_neighbor_common (vty, argv[0], ISIS_UI_LEVEL_DETAIL); +} + +DEFUN (clear_isis_neighbor, + clear_isis_neighbor_cmd, + "clear isis neighbor", + CLEAR_STR + "Reset ISIS network information\n" + "Reset ISIS neighbor adjacencies\n") +{ + return clear_isis_neighbor_common (vty, NULL); +} + +DEFUN (clear_isis_neighbor_arg, + clear_isis_neighbor_arg_cmd, + "clear isis neighbor WORD", + CLEAR_STR + "ISIS network information\n" + "ISIS neighbor adjacencies\n" + "System id\n") +{ + return clear_isis_neighbor_common (vty, argv[0]); +} + /* * 'isis debug', 'show debugging' */ @@ -535,17 +774,24 @@ print_debug (struct vty *vty, int flags, int onoff) VTY_NEWLINE); if (flags & DEBUG_EVENTS) vty_out (vty, "IS-IS Event debugging is %s%s", onoffs, VTY_NEWLINE); - + if (flags & DEBUG_PACKET_DUMP) + vty_out (vty, "IS-IS Packet dump debugging is %s%s", onoffs, VTY_NEWLINE); + if (flags & DEBUG_LSP_GEN) + vty_out (vty, "IS-IS LSP generation debugging is %s%s", onoffs, VTY_NEWLINE); + if (flags & DEBUG_LSP_SCHED) + vty_out (vty, "IS-IS LSP scheduling debugging is %s%s", onoffs, VTY_NEWLINE); } DEFUN (show_debugging, - show_debugging_cmd, - "show debugging", + show_debugging_isis_cmd, + "show debugging isis", SHOW_STR "State of each debugging option\n") { - vty_out (vty, "IS-IS:%s", VTY_NEWLINE); - print_debug (vty, isis->debugs, 1); + if (isis->debugs) { + vty_out (vty, "IS-IS:%s", VTY_NEWLINE); + print_debug (vty, isis->debugs, 1); + } return CMD_SUCCESS; } @@ -617,6 +863,21 @@ config_write_debug (struct vty *vty) vty_out (vty, "debug isis events%s", VTY_NEWLINE); write++; } + if (flags & DEBUG_PACKET_DUMP) + { + vty_out (vty, "debug isis packet-dump%s", VTY_NEWLINE); + write++; + } + if (flags & DEBUG_LSP_GEN) + { + vty_out (vty, "debug isis lsp-gen%s", VTY_NEWLINE); + write++; + } + if (flags & DEBUG_LSP_SCHED) + { + vty_out (vty, "debug isis lsp-sched%s", VTY_NEWLINE); + write++; + } return write; } @@ -803,7 +1064,6 @@ DEFUN (no_debug_isis_spfevents, return CMD_SUCCESS; } - DEFUN (debug_isis_spfstats, debug_isis_spfstats_cmd, "debug isis spf-statistics ", @@ -908,6 +1168,84 @@ DEFUN (no_debug_isis_events, return CMD_SUCCESS; } +DEFUN (debug_isis_packet_dump, + debug_isis_packet_dump_cmd, + "debug isis packet-dump", + DEBUG_STR + "IS-IS information\n" + "IS-IS packet dump\n") +{ + isis->debugs |= DEBUG_PACKET_DUMP; + print_debug (vty, DEBUG_PACKET_DUMP, 1); + + return CMD_SUCCESS; +} + +DEFUN (no_debug_isis_packet_dump, + no_debug_isis_packet_dump_cmd, + "no debug isis packet-dump", + UNDEBUG_STR + "IS-IS information\n" + "IS-IS packet dump\n") +{ + isis->debugs &= ~DEBUG_PACKET_DUMP; + print_debug (vty, DEBUG_PACKET_DUMP, 0); + + return CMD_SUCCESS; +} + +DEFUN (debug_isis_lsp_gen, + debug_isis_lsp_gen_cmd, + "debug isis lsp-gen", + DEBUG_STR + "IS-IS information\n" + "IS-IS generation of own LSPs\n") +{ + isis->debugs |= DEBUG_LSP_GEN; + print_debug (vty, DEBUG_LSP_GEN, 1); + + return CMD_SUCCESS; +} + +DEFUN (no_debug_isis_lsp_gen, + no_debug_isis_lsp_gen_cmd, + "no debug isis lsp-gen", + UNDEBUG_STR + "IS-IS information\n" + "IS-IS generation of own LSPs\n") +{ + isis->debugs &= ~DEBUG_LSP_GEN; + print_debug (vty, DEBUG_LSP_GEN, 0); + + return CMD_SUCCESS; +} + +DEFUN (debug_isis_lsp_sched, + debug_isis_lsp_sched_cmd, + "debug isis lsp-sched", + DEBUG_STR + "IS-IS information\n" + "IS-IS scheduling of LSP generation\n") +{ + isis->debugs |= DEBUG_LSP_SCHED; + print_debug (vty, DEBUG_LSP_SCHED, 1); + + return CMD_SUCCESS; +} + +DEFUN (no_debug_isis_lsp_sched, + no_debug_isis_lsp_sched_cmd, + "no debug isis lsp-gen", + UNDEBUG_STR + "IS-IS information\n" + "IS-IS scheduling of LSP generation\n") +{ + isis->debugs &= ~DEBUG_LSP_SCHED; + print_debug (vty, DEBUG_LSP_SCHED, 0); + + return CMD_SUCCESS; +} + DEFUN (show_hostname, show_hostname_cmd, "show isis hostname", @@ -920,80 +1258,314 @@ DEFUN (show_hostname, return CMD_SUCCESS; } -DEFUN (show_database, - show_database_cmd, - "show isis database", - SHOW_STR "IS-IS information\n" "IS-IS link state database\n") +static void +vty_out_timestr(struct vty *vty, time_t uptime) { - struct listnode *node; + struct tm *tm; + time_t difftime = time (NULL); + difftime -= uptime; + tm = gmtime (&difftime); + +#define ONE_DAY_SECOND 60*60*24 +#define ONE_WEEK_SECOND 60*60*24*7 + if (difftime < ONE_DAY_SECOND) + vty_out (vty, "%02d:%02d:%02d", + tm->tm_hour, tm->tm_min, tm->tm_sec); + else if (difftime < ONE_WEEK_SECOND) + vty_out (vty, "%dd%02dh%02dm", + tm->tm_yday, tm->tm_hour, tm->tm_min); + else + vty_out (vty, "%02dw%dd%02dh", + tm->tm_yday/7, + tm->tm_yday - ((tm->tm_yday/7) * 7), tm->tm_hour); + vty_out (vty, " ago"); +} + +DEFUN (show_isis_summary, + show_isis_summary_cmd, + "show isis summary", + SHOW_STR "IS-IS information\n" "IS-IS summary\n") +{ + struct listnode *node, *node2; struct isis_area *area; - int level, lsp_count; + struct isis_spftree *spftree; + int level; - if (isis->area_list->count == 0) + if (isis == NULL) + { + vty_out (vty, "ISIS is not running%s", VTY_NEWLINE); return CMD_SUCCESS; + } + + vty_out (vty, "Process Id : %ld%s", isis->process_id, + VTY_NEWLINE); + if (isis->sysid_set) + vty_out (vty, "System Id : %s%s", sysid_print (isis->sysid), + VTY_NEWLINE); + + vty_out (vty, "Up time : "); + vty_out_timestr(vty, isis->uptime); + vty_out (vty, "%s", VTY_NEWLINE); + + if (isis->area_list) + vty_out (vty, "Number of areas : %d%s", isis->area_list->count, + VTY_NEWLINE); for (ALL_LIST_ELEMENTS_RO (isis->area_list, node, area)) + { + vty_out (vty, "Area %s:%s", area->area_tag ? area->area_tag : "null", + VTY_NEWLINE); + + if (listcount (area->area_addrs) > 0) { - vty_out (vty, "Area %s:%s", area->area_tag ? area->area_tag : "null", - VTY_NEWLINE); - for (level = 0; level < ISIS_LEVELS; level++) - { - if (area->lspdb[level] && dict_count (area->lspdb[level]) > 0) - { - vty_out (vty, "IS-IS Level-%d link-state database:%s", - level + 1, VTY_NEWLINE); + struct area_addr *area_addr; + for (ALL_LIST_ELEMENTS_RO (area->area_addrs, node2, area_addr)) + { + vty_out (vty, " Net: %s%s", + isonet_print (area_addr->area_addr, + area_addr->addr_len + ISIS_SYS_ID_LEN + + 1), VTY_NEWLINE); + } + } + + for (level = ISIS_LEVEL1; level <= ISIS_LEVELS; level++) + { + if ((area->is_type & level) == 0) + continue; - lsp_count = lsp_print_all (vty, area->lspdb[level], - ISIS_UI_LEVEL_BRIEF, - area->dynhostname); + vty_out (vty, " Level-%d:%s", level, VTY_NEWLINE); + spftree = area->spftree[level - 1]; + if (spftree->pending) + vty_out (vty, " IPv4 SPF: (pending)%s", VTY_NEWLINE); + else + vty_out (vty, " IPv4 SPF:%s", VTY_NEWLINE); - vty_out (vty, "%s %u LSPs%s%s", - VTY_NEWLINE, lsp_count, VTY_NEWLINE, VTY_NEWLINE); - } - } + vty_out (vty, " minimum interval : %d%s", + area->min_spf_interval[level - 1], VTY_NEWLINE); + + vty_out (vty, " last run elapsed : "); + vty_out_timestr(vty, spftree->last_run_timestamp); + vty_out (vty, "%s", VTY_NEWLINE); + + vty_out (vty, " last run duration : %u usec%s", + (u_int32_t)spftree->last_run_duration, VTY_NEWLINE); + + vty_out (vty, " run count : %d%s", + spftree->runcount, VTY_NEWLINE); + +#ifdef HAVE_IPV6 + spftree = area->spftree6[level - 1]; + if (spftree->pending) + vty_out (vty, " IPv6 SPF: (pending)%s", VTY_NEWLINE); + else + vty_out (vty, " IPv6 SPF:%s", VTY_NEWLINE); + + vty_out (vty, " minimum interval : %d%s", + area->min_spf_interval[level - 1], VTY_NEWLINE); + + vty_out (vty, " last run elapsed : "); + vty_out_timestr(vty, spftree->last_run_timestamp); + vty_out (vty, "%s", VTY_NEWLINE); + + vty_out (vty, " last run duration : %llu msec%s", + (unsigned long long)spftree->last_run_duration, VTY_NEWLINE); + + vty_out (vty, " run count : %d%s", + spftree->runcount, VTY_NEWLINE); +#endif } + } + vty_out (vty, "%s", VTY_NEWLINE); return CMD_SUCCESS; } -DEFUN (show_database_detail, - show_database_detail_cmd, - "show isis database detail", - SHOW_STR - "IS-IS information\n" - "IS-IS link state database\n") +/* + * This function supports following display options: + * [ show isis database [detail] ] + * [ show isis database [detail] ] + * [ show isis database [detail] ] + * [ show isis database . [detail] ] + * [ show isis database . [detail] ] + * [ show isis database .- [detail] ] + * [ show isis database .- [detail] ] + * [ show isis database detail ] + * [ show isis database detail ] + * [ show isis database detail . ] + * [ show isis database detail . ] + * [ show isis database detail .- ] + * [ show isis database detail .- ] + */ +static int +show_isis_database (struct vty *vty, const char *argv, int ui_level) { struct listnode *node; struct isis_area *area; + struct isis_lsp *lsp; + struct isis_dynhn *dynhn; + const char *pos = argv; + u_char lspid[ISIS_SYS_ID_LEN+2]; + char sysid[255]; + u_char number[3]; int level, lsp_count; if (isis->area_list->count == 0) return CMD_SUCCESS; - for (ALL_LIST_ELEMENTS_RO (isis->area_list, node, area)) - { - vty_out (vty, "Area %s:%s", area->area_tag ? area->area_tag : "null", - VTY_NEWLINE); - for (level = 0; level < ISIS_LEVELS; level++) - { - if (area->lspdb[level] && dict_count (area->lspdb[level]) > 0) - { - vty_out (vty, "IS-IS Level-%d Link State Database:%s", - level + 1, VTY_NEWLINE); - - lsp_count = lsp_print_all (vty, area->lspdb[level], - ISIS_UI_LEVEL_DETAIL, - area->dynhostname); + memset (&lspid, 0, ISIS_SYS_ID_LEN); + memset (&sysid, 0, 255); - vty_out (vty, "%s %u LSPs%s%s", - VTY_NEWLINE, lsp_count, VTY_NEWLINE, VTY_NEWLINE); - } - } + /* + * extract fragment and pseudo id from the string argv + * in the forms: + * (a) .- or + * (b) . or + * (c) or + * Where systemid is in the form: + * xxxx.xxxx.xxxx + */ + if (argv) + strncpy (sysid, argv, 254); + if (argv && strlen (argv) > 3) + { + pos = argv + strlen (argv) - 3; + if (strncmp (pos, "-", 1) == 0) + { + memcpy (number, ++pos, 2); + lspid[ISIS_SYS_ID_LEN+1] = (u_char) strtol ((char *)number, NULL, 16); + pos -= 4; + if (strncmp (pos, ".", 1) != 0) + return CMD_ERR_AMBIGUOUS; + } + if (strncmp (pos, ".", 1) == 0) + { + memcpy (number, ++pos, 2); + lspid[ISIS_SYS_ID_LEN] = (u_char) strtol ((char *)number, NULL, 16); + sysid[pos - argv - 1] = '\0'; + } + } + + for (ALL_LIST_ELEMENTS_RO (isis->area_list, node, area)) + { + vty_out (vty, "Area %s:%s", area->area_tag ? area->area_tag : "null", + VTY_NEWLINE); + + for (level = 0; level < ISIS_LEVELS; level++) + { + if (area->lspdb[level] && dict_count (area->lspdb[level]) > 0) + { + lsp = NULL; + if (argv != NULL) + { + /* + * Try to find the lsp-id if the argv string is in + * the form hostname.- + */ + if (sysid2buff (lspid, sysid)) + { + lsp = lsp_search (lspid, area->lspdb[level]); + } + else if ((dynhn = dynhn_find_by_name (sysid))) + { + memcpy (lspid, dynhn->id, ISIS_SYS_ID_LEN); + lsp = lsp_search (lspid, area->lspdb[level]); + } + else if (strncmp(unix_hostname (), sysid, 15) == 0) + { + memcpy (lspid, isis->sysid, ISIS_SYS_ID_LEN); + lsp = lsp_search (lspid, area->lspdb[level]); + } + } + + if (lsp != NULL || argv == NULL) + { + vty_out (vty, "IS-IS Level-%d link-state database:%s", + level + 1, VTY_NEWLINE); + + /* print the title in all cases */ + vty_out (vty, "LSP ID PduLen " + "SeqNumber Chksum Holdtime ATT/P/OL%s", + VTY_NEWLINE); + } + + if (lsp) + { + if (ui_level == ISIS_UI_LEVEL_DETAIL) + lsp_print_detail (lsp, vty, area->dynhostname); + else + lsp_print (lsp, vty, area->dynhostname); + } + else if (argv == NULL) + { + lsp_count = lsp_print_all (vty, area->lspdb[level], + ui_level, + area->dynhostname); + + vty_out (vty, " %u LSPs%s%s", + lsp_count, VTY_NEWLINE, VTY_NEWLINE); + } + } + } } return CMD_SUCCESS; } +DEFUN (show_database_brief, + show_database_cmd, + "show isis database", + SHOW_STR + "IS-IS information\n" + "IS-IS link state database\n") +{ + return show_isis_database (vty, NULL, ISIS_UI_LEVEL_BRIEF); +} + +DEFUN (show_database_lsp_brief, + show_database_arg_cmd, + "show isis database WORD", + SHOW_STR + "IS-IS information\n" + "IS-IS link state database\n" + "LSP ID\n") +{ + return show_isis_database (vty, argv[0], ISIS_UI_LEVEL_BRIEF); +} + +DEFUN (show_database_lsp_detail, + show_database_arg_detail_cmd, + "show isis database WORD detail", + SHOW_STR + "IS-IS information\n" + "IS-IS link state database\n" + "LSP ID\n" + "Detailed information\n") +{ + return show_isis_database (vty, argv[0], ISIS_UI_LEVEL_DETAIL); +} + +DEFUN (show_database_detail, + show_database_detail_cmd, + "show isis database detail", + SHOW_STR + "IS-IS information\n" + "IS-IS link state database\n") +{ + return show_isis_database (vty, NULL, ISIS_UI_LEVEL_DETAIL); +} + +DEFUN (show_database_detail_lsp, + show_database_detail_arg_cmd, + "show isis database detail WORD", + SHOW_STR + "IS-IS information\n" + "IS-IS link state database\n" + "Detailed information\n" + "LSP ID\n") +{ + return show_isis_database (vty, argv[0], ISIS_UI_LEVEL_DETAIL); +} + /* * 'router isis' command */ @@ -1043,555 +1615,284 @@ DEFUN (no_net, return area_clear_net_title (vty, argv[0]); } -DEFUN (area_passwd, - area_passwd_cmd, - "area-password WORD", - "Configure the authentication password for an area\n" - "Area password\n") +void isis_area_lsp_mtu_set(struct isis_area *area, unsigned int lsp_mtu) { - struct isis_area *area; + area->lsp_mtu = lsp_mtu; + lsp_regenerate_schedule(area, IS_LEVEL_1_AND_2, 1); +} + +static int +isis_area_passwd_set(struct isis_area *area, int level, u_char passwd_type, + const char *passwd, u_char snp_auth) +{ + struct isis_passwd *dest; + struct isis_passwd modified; int len; - area = vty->index; + assert((level == IS_LEVEL_1) || (level == IS_LEVEL_2)); + dest = (level == IS_LEVEL_1) ? &area->area_passwd : &area->domain_passwd; + memset(&modified, 0, sizeof(modified)); - if (!area) + if (passwd_type != ISIS_PASSWD_TYPE_UNUSED) { - vty_out (vty, "Cant find IS-IS instance%s", VTY_NEWLINE); - return CMD_WARNING; - } + if (!passwd) + return -1; - len = strlen (argv[0]); - if (len > 254) - { - vty_out (vty, "Too long area password (>254)%s", VTY_NEWLINE); - return CMD_WARNING; - } - area->area_passwd.len = (u_char) len; - area->area_passwd.type = ISIS_PASSWD_TYPE_CLEARTXT; - strncpy ((char *)area->area_passwd.passwd, argv[0], 255); + len = strlen(passwd); + if (len > 254) + return -1; - if (argc > 1) - { - SET_FLAG(area->area_passwd.snp_auth, SNP_AUTH_SEND); - if (strncmp(argv[1], "v", 1) == 0) - SET_FLAG(area->area_passwd.snp_auth, SNP_AUTH_RECV); - else - UNSET_FLAG(area->area_passwd.snp_auth, SNP_AUTH_RECV); + modified.len = len; + strncpy((char*)modified.passwd, passwd, 255); + modified.type = passwd_type; + modified.snp_auth = snp_auth; } - else + + if (memcmp(&modified, dest, sizeof(modified))) { - UNSET_FLAG(area->area_passwd.snp_auth, SNP_AUTH_SEND); - UNSET_FLAG(area->area_passwd.snp_auth, SNP_AUTH_RECV); + memcpy(dest, &modified, sizeof(modified)); + lsp_regenerate_schedule(area, IS_LEVEL_1|IS_LEVEL_2, 1); } - return CMD_SUCCESS; + return 0; } -ALIAS (area_passwd, - area_passwd_snpauth_cmd, - "area-password WORD authenticate snp (send-only|validate)", - "Configure the authentication password for an area\n" - "Area password\n" - "Authentication\n" - "SNP PDUs\n" - "Send but do not check PDUs on receiving\n" - "Send and check PDUs on receiving\n"); - -DEFUN (no_area_passwd, - no_area_passwd_cmd, - "no area-password", - NO_STR - "Configure the authentication password for an area\n") +int +isis_area_passwd_unset (struct isis_area *area, int level) { - struct isis_area *area; - - area = vty->index; - - if (!area) - { - vty_out (vty, "Cant find IS-IS instance%s", VTY_NEWLINE); - return CMD_WARNING; - } - - memset (&area->area_passwd, 0, sizeof (struct isis_passwd)); - - return CMD_SUCCESS; + return isis_area_passwd_set (area, level, ISIS_PASSWD_TYPE_UNUSED, NULL, 0); } -DEFUN (domain_passwd, - domain_passwd_cmd, - "domain-password WORD", - "Set the authentication password for a routing domain\n" - "Routing domain password\n") +int +isis_area_passwd_cleartext_set (struct isis_area *area, int level, + const char *passwd, u_char snp_auth) { - struct isis_area *area; - int len; - - area = vty->index; - - if (!area) - { - vty_out (vty, "Cant find IS-IS instance%s", VTY_NEWLINE); - return CMD_WARNING; - } + return isis_area_passwd_set (area, level, ISIS_PASSWD_TYPE_CLEARTXT, + passwd, snp_auth); +} - len = strlen (argv[0]); - if (len > 254) - { - vty_out (vty, "Too long area password (>254)%s", VTY_NEWLINE); - return CMD_WARNING; - } - area->domain_passwd.len = (u_char) len; - area->domain_passwd.type = ISIS_PASSWD_TYPE_CLEARTXT; - strncpy ((char *)area->domain_passwd.passwd, argv[0], 255); +int +isis_area_passwd_hmac_md5_set (struct isis_area *area, int level, + const char *passwd, u_char snp_auth) +{ + return isis_area_passwd_set (area, level, ISIS_PASSWD_TYPE_HMAC_MD5, + passwd, snp_auth); +} - if (argc > 1) +static void +area_resign_level (struct isis_area *area, int level) +{ + if (area->lspdb[level - 1]) { - SET_FLAG(area->domain_passwd.snp_auth, SNP_AUTH_SEND); - if (strncmp(argv[1], "v", 1) == 0) - SET_FLAG(area->domain_passwd.snp_auth, SNP_AUTH_RECV); - else - UNSET_FLAG(area->domain_passwd.snp_auth, SNP_AUTH_RECV); + lsp_db_destroy (area->lspdb[level - 1]); + area->lspdb[level - 1] = NULL; } - else + if (area->spftree[level - 1]) { - UNSET_FLAG(area->domain_passwd.snp_auth, SNP_AUTH_SEND); - UNSET_FLAG(area->domain_passwd.snp_auth, SNP_AUTH_RECV); + isis_spftree_del (area->spftree[level - 1]); + area->spftree[level - 1] = NULL; } - - return CMD_SUCCESS; -} - -ALIAS (domain_passwd, - domain_passwd_snpauth_cmd, - "domain-password WORD authenticate snp (send-only|validate)", - "Set the authentication password for a routing domain\n" - "Routing domain password\n" - "Authentication\n" - "SNP PDUs\n" - "Send but do not check PDUs on receiving\n" - "Send and check PDUs on receiving\n"); - -DEFUN (no_domain_passwd, - no_domain_passwd_cmd, - "no domain-password WORD", - NO_STR - "Set the authentication password for a routing domain\n") -{ - struct isis_area *area; - - area = vty->index; - - if (!area) +#ifdef HAVE_IPV6 + if (area->spftree6[level - 1]) { - vty_out (vty, "Cant find IS-IS instance%s", VTY_NEWLINE); - return CMD_WARNING; + isis_spftree_del (area->spftree6[level - 1]); + area->spftree6[level - 1] = NULL; } - - memset (&area->domain_passwd, 0, sizeof (struct isis_passwd)); - - return CMD_SUCCESS; -} - -DEFUN (is_type, - is_type_cmd, - "is-type (level-1|level-1-2|level-2-only)", - "IS Level for this routing process (OSI only)\n" - "Act as a station router only\n" - "Act as both a station router and an area router\n" - "Act as an area router only\n") -{ - struct isis_area *area; - int type; - - area = vty->index; - - if (!area) +#endif + if (area->route_table[level - 1]) { - vty_out (vty, "Cant find IS-IS instance%s", VTY_NEWLINE); - return CMD_WARNING; + route_table_finish (area->route_table[level - 1]); + area->route_table[level - 1] = NULL; } - - type = string2circuit_t (argv[0]); - if (!type) +#ifdef HAVE_IPV6 + if (area->route_table6[level - 1]) { - vty_out (vty, "Unknown IS level %s", VTY_NEWLINE); - return CMD_SUCCESS; + route_table_finish (area->route_table6[level - 1]); + area->route_table6[level - 1] = NULL; } +#endif /* HAVE_IPV6 */ - isis_event_system_type_change (area, type); - - return CMD_SUCCESS; -} - -DEFUN (no_is_type, - no_is_type_cmd, - "no is-type (level-1|level-1-2|level-2-only)", - NO_STR - "IS Level for this routing process (OSI only)\n" - "Act as a station router only\n" - "Act as both a station router and an area router\n" - "Act as an area router only\n") -{ - struct isis_area *area; - int type; - - area = vty->index; - assert (area); - - /* - * Put the is-type back to default. Which is level-1-2 on first - * circuit for the area level-1 for the rest - */ - if (listgetdata (listhead (isis->area_list)) == area) - type = IS_LEVEL_1_AND_2; - else - type = IS_LEVEL_1; - - isis_event_system_type_change (area, type); - - return CMD_SUCCESS; + sched_debug("ISIS (%s): Resigned from L%d - canceling LSP regeneration timer.", + area->area_tag, level); + THREAD_TIMER_OFF (area->t_lsp_refresh[level - 1]); + area->lsp_regenerate_pending[level - 1] = 0; } -DEFUN (lsp_gen_interval, - lsp_gen_interval_cmd, - "lsp-gen-interval <1-120>", - "Minimum interval between regenerating same LSP\n" - "Minimum interval in seconds\n") +void +isis_area_is_type_set(struct isis_area *area, int is_type) { - struct isis_area *area; - uint16_t interval; - - area = vty->index; - assert (area); - - interval = atoi (argv[0]); - area->lsp_gen_interval[0] = interval; - area->lsp_gen_interval[1] = interval; + struct listnode *node; + struct isis_circuit *circuit; - return CMD_SUCCESS; -} + if (isis->debugs & DEBUG_EVENTS) + zlog_debug ("ISIS-Evt (%s) system type change %s -> %s", area->area_tag, + circuit_t2string (area->is_type), circuit_t2string (is_type)); + + if (area->is_type == is_type) + return; /* No change */ + + switch (area->is_type) + { + case IS_LEVEL_1: + if (is_type == IS_LEVEL_2) + area_resign_level (area, IS_LEVEL_1); + + if (area->lspdb[1] == NULL) + area->lspdb[1] = lsp_db_init (); + if (area->route_table[1] == NULL) + area->route_table[1] = route_table_init (); +#ifdef HAVE_IPV6 + if (area->route_table6[1] == NULL) + area->route_table6[1] = route_table_init (); +#endif /* HAVE_IPV6 */ + break; -DEFUN (no_lsp_gen_interval, - no_lsp_gen_interval_cmd, - "no lsp-gen-interval", - NO_STR - "Minimum interval between regenerating same LSP\n") -{ - struct isis_area *area; + case IS_LEVEL_1_AND_2: + if (is_type == IS_LEVEL_1) + area_resign_level (area, IS_LEVEL_2); + else + area_resign_level (area, IS_LEVEL_1); + break; - area = vty->index; - assert (area); + case IS_LEVEL_2: + if (is_type == IS_LEVEL_1) + area_resign_level (area, IS_LEVEL_2); - area->lsp_gen_interval[0] = LSP_GEN_INTERVAL_DEFAULT; - area->lsp_gen_interval[1] = LSP_GEN_INTERVAL_DEFAULT; + if (area->lspdb[0] == NULL) + area->lspdb[0] = lsp_db_init (); + if (area->route_table[0] == NULL) + area->route_table[0] = route_table_init (); +#ifdef HAVE_IPV6 + if (area->route_table6[0] == NULL) + area->route_table6[0] = route_table_init (); +#endif /* HAVE_IPV6 */ + break; - return CMD_SUCCESS; -} + default: + break; + } -ALIAS (no_lsp_gen_interval, - no_lsp_gen_interval_arg_cmd, - "no lsp-gen-interval <1-120>", - NO_STR - "Minimum interval between regenerating same LSP\n" - "Minimum interval in seconds\n") + area->is_type = is_type; -DEFUN (lsp_gen_interval_l1, - lsp_gen_interval_l1_cmd, - "lsp-gen-interval level-1 <1-120>", - "Minimum interval between regenerating same LSP\n" - "Set interval for level 1 only\n" - "Minimum interval in seconds\n") -{ - struct isis_area *area; - uint16_t interval; + /* override circuit's is_type */ + if (area->is_type != IS_LEVEL_1_AND_2) + { + for (ALL_LIST_ELEMENTS_RO (area->circuit_list, node, circuit)) + isis_circuit_is_type_set (circuit, is_type); + } - area = vty->index; - assert (area); + spftree_area_init (area); - interval = atoi (argv[0]); - area->lsp_gen_interval[0] = interval; + if (is_type & IS_LEVEL_1) + lsp_generate (area, IS_LEVEL_1); + if (is_type & IS_LEVEL_2) + lsp_generate (area, IS_LEVEL_2); + lsp_regenerate_schedule (area, IS_LEVEL_1 | IS_LEVEL_2, 1); - return CMD_SUCCESS; + return; } -DEFUN (no_lsp_gen_interval_l1, - no_lsp_gen_interval_l1_cmd, - "no lsp-gen-interval level-1", - NO_STR - "Minimum interval between regenerating same LSP\n" - "Set interval for level 1 only\n") +void isis_area_metricstyle_set(struct isis_area *area, bool old_metric, + bool new_metric) { - struct isis_area *area; - - area = vty->index; - assert (area); - - area->lsp_gen_interval[0] = LSP_GEN_INTERVAL_DEFAULT; - - return CMD_SUCCESS; + if (area->oldmetric != old_metric + || area->newmetric != new_metric) + { + area->oldmetric = old_metric; + area->newmetric = new_metric; + lsp_regenerate_schedule(area, IS_LEVEL_1 | IS_LEVEL_2, 1); + } } -ALIAS (no_lsp_gen_interval_l1, - no_lsp_gen_interval_l1_arg_cmd, - "no lsp-gen-interval level-1 <1-120>", - NO_STR - "Minimum interval between regenerating same LSP\n" - "Set interval for level 1 only\n" - "Minimum interval in seconds\n") - -DEFUN (lsp_gen_interval_l2, - lsp_gen_interval_l2_cmd, - "lsp-gen-interval level-2 <1-120>", - "Minimum interval between regenerating same LSP\n" - "Set interval for level 2 only\n" - "Minimum interval in seconds\n") +void isis_area_overload_bit_set(struct isis_area *area, bool overload_bit) { - struct isis_area *area; - int interval; + char new_overload_bit = overload_bit ? LSPBIT_OL : 0; - area = vty->index; - assert (area); - - interval = atoi (argv[0]); - area->lsp_gen_interval[1] = interval; - - return CMD_SUCCESS; + if (new_overload_bit != area->overload_bit) + { + area->overload_bit = new_overload_bit; + lsp_regenerate_schedule(area, IS_LEVEL_1 | IS_LEVEL_2, 1); + } } -DEFUN (no_lsp_gen_interval_l2, - no_lsp_gen_interval_l2_cmd, - "no lsp-gen-interval level-2", - NO_STR - "Minimum interval between regenerating same LSP\n" - "Set interval for level 2 only\n") +void isis_area_attached_bit_set(struct isis_area *area, bool attached_bit) { - struct isis_area *area; - int interval; - - area = vty->index; - assert (area); + char new_attached_bit = attached_bit ? LSPBIT_ATT : 0; - interval = atoi (argv[0]); - area->lsp_gen_interval[1] = LSP_GEN_INTERVAL_DEFAULT; - - return CMD_SUCCESS; + if (new_attached_bit != area->attached_bit) + { + area->attached_bit = new_attached_bit; + lsp_regenerate_schedule(area, IS_LEVEL_1 | IS_LEVEL_2, 1); + } } -ALIAS (no_lsp_gen_interval_l2, - no_lsp_gen_interval_l2_arg_cmd, - "no lsp-gen-interval level-2 <1-120>", - NO_STR - "Minimum interval between regenerating same LSP\n" - "Set interval for level 2 only\n" - "Minimum interval in seconds\n") - -DEFUN (metric_style, - metric_style_cmd, - "metric-style (narrow|transition|wide)", - "Use old-style (ISO 10589) or new-style packet formats\n" - "Use old style of TLVs with narrow metric\n" - "Send and accept both styles of TLVs during transition\n" - "Use new style of TLVs to carry wider metric\n") +void isis_area_dynhostname_set(struct isis_area *area, bool dynhostname) { - struct isis_area *area; - - area = vty->index; - assert (area); - - if (strncmp (argv[0], "w", 1) == 0) - { - 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) + if (area->dynhostname != dynhostname) { - area->newmetric = 0; - area->oldmetric = 1; + area->dynhostname = dynhostname; + lsp_regenerate_schedule(area, IS_LEVEL_1 | IS_LEVEL_2, 0); } - - return CMD_SUCCESS; } -DEFUN (no_metric_style, - no_metric_style_cmd, - "no metric-style", - NO_STR - "Use old-style (ISO 10589) or new-style packet formats\n") +void +isis_area_max_lsp_lifetime_set(struct isis_area *area, int level, + uint16_t max_lsp_lifetime) { - struct isis_area *area; + assert((level == IS_LEVEL_1) || (level == IS_LEVEL_2)); - area = vty->index; - assert (area); + if (area->max_lsp_lifetime[level-1] == max_lsp_lifetime) + return; - /* Default is narrow metric. */ - area->newmetric = 0; - area->oldmetric = 1; - - return CMD_SUCCESS; + area->max_lsp_lifetime[level-1] = max_lsp_lifetime; + lsp_regenerate_schedule(area, level, 1); } -DEFUN (dynamic_hostname, - dynamic_hostname_cmd, - "hostname dynamic", - "Dynamic hostname for IS-IS\n" - "Dynamic hostname\n") +void +isis_area_lsp_refresh_set(struct isis_area *area, int level, + uint16_t lsp_refresh) { - struct isis_area *area; + assert((level == IS_LEVEL_1) || (level == IS_LEVEL_2)); - area = vty->index; - assert (area); + if (area->lsp_refresh[level-1] == lsp_refresh) + return; - area->dynhostname = 1; - - return CMD_SUCCESS; + area->lsp_refresh[level-1] = lsp_refresh; + lsp_regenerate_schedule(area, level, 1); } -DEFUN (no_dynamic_hostname, - no_dynamic_hostname_cmd, - "no hostname dynamic", - NO_STR - "Dynamic hostname for IS-IS\n" - "Dynamic hostname\n") +DEFUN (log_adj_changes, + log_adj_changes_cmd, + "log-adjacency-changes", + "Log changes in adjacency state\n") { struct isis_area *area; area = vty->index; assert (area); - area->dynhostname = 0; - - return CMD_SUCCESS; -} - -DEFUN (spf_interval, - spf_interval_cmd, - "spf-interval <1-120>", - "Minimum interval between SPF calculations\n" - "Minimum interval between consecutive SPFs in seconds\n") -{ - struct isis_area *area; - u_int16_t interval; - - area = vty->index; - interval = atoi (argv[0]); - area->min_spf_interval[0] = interval; - area->min_spf_interval[1] = interval; - - return CMD_SUCCESS; -} - -DEFUN (no_spf_interval, - no_spf_interval_cmd, - "no spf-interval", - NO_STR - "Minimum interval between SPF calculations\n") -{ - struct isis_area *area; - - area = vty->index; - - area->min_spf_interval[0] = MINIMUM_SPF_INTERVAL; - area->min_spf_interval[1] = MINIMUM_SPF_INTERVAL; - - return CMD_SUCCESS; -} - -ALIAS (no_spf_interval, - no_spf_interval_arg_cmd, - "no spf-interval <1-120>", - NO_STR - "Minimum interval between SPF calculations\n" - "Minimum interval between consecutive SPFs in seconds\n") - -DEFUN (spf_interval_l1, - spf_interval_l1_cmd, - "spf-interval level-1 <1-120>", - "Minimum interval between SPF calculations\n" - "Set interval for level 1 only\n" - "Minimum interval between consecutive SPFs in seconds\n") -{ - struct isis_area *area; - u_int16_t interval; - - area = vty->index; - interval = atoi (argv[0]); - area->min_spf_interval[0] = interval; - - return CMD_SUCCESS; -} - -DEFUN (no_spf_interval_l1, - no_spf_interval_l1_cmd, - "no spf-interval level-1", - NO_STR - "Minimum interval between SPF calculations\n" - "Set interval for level 1 only\n") -{ - struct isis_area *area; - - area = vty->index; - - area->min_spf_interval[0] = MINIMUM_SPF_INTERVAL; - - return CMD_SUCCESS; -} - -ALIAS (no_spf_interval, - no_spf_interval_l1_arg_cmd, - "no spf-interval level-1 <1-120>", - NO_STR - "Minimum interval between SPF calculations\n" - "Set interval for level 1 only\n" - "Minimum interval between consecutive SPFs in seconds\n") - -DEFUN (spf_interval_l2, - spf_interval_l2_cmd, - "spf-interval level-2 <1-120>", - "Minimum interval between SPF calculations\n" - "Set interval for level 2 only\n" - "Minimum interval between consecutive SPFs in seconds\n") -{ - struct isis_area *area; - u_int16_t interval; - - area = vty->index; - interval = atoi (argv[0]); - area->min_spf_interval[1] = interval; + area->log_adj_changes = 1; return CMD_SUCCESS; } -DEFUN (no_spf_interval_l2, - no_spf_interval_l2_cmd, - "no spf-interval level-2", - NO_STR - "Minimum interval between SPF calculations\n" - "Set interval for level 2 only\n") +DEFUN (no_log_adj_changes, + no_log_adj_changes_cmd, + "no log-adjacency-changes", + "Stop logging changes in adjacency state\n") { struct isis_area *area; area = vty->index; + assert (area); - area->min_spf_interval[1] = MINIMUM_SPF_INTERVAL; + area->log_adj_changes = 0; return CMD_SUCCESS; } -ALIAS (no_spf_interval, - no_spf_interval_l2_arg_cmd, - "no spf-interval level-2 <1-120>", - NO_STR - "Minimum interval between SPF calculations\n" - "Set interval for level 2 only\n" - "Minimum interval between consecutive SPFs in seconds\n") - #ifdef TOPOLOGY_GENERATE + DEFUN (topology_generate_grid, topology_generate_grid_cmd, "topology generate grid <1-100> <1-100> <1-65000> [param] [param] " @@ -1623,7 +1924,7 @@ DEFUN (topology_generate_grid, generate_topology_lsps (area); /* Regenerate L1 LSP to get two way connection to the generated * topology. */ - lsp_regenerate_schedule (area); + lsp_regenerate_schedule (area, IS_LEVEL_1 | IS_LEVEL_2, 1); } return CMD_SUCCESS; @@ -1633,7 +1934,7 @@ DEFUN (show_isis_generated_topology, show_isis_generated_topology_cmd, "show isis generated-topologies", SHOW_STR - "CLNS network information\n" + "ISIS network information\n" "Show generated topologies\n") { struct isis_area *area; @@ -1717,182 +2018,8 @@ DEFUN (topology_basedynh, area->topology_basedynh = strndup (argv[0], 16); return CMD_SUCCESS; } -#endif /* TOPOLOGY_GENERATE */ - -DEFUN (lsp_lifetime, - lsp_lifetime_cmd, - "lsp-lifetime <380-65535>", - "Maximum LSP lifetime\n" - "LSP lifetime in seconds\n") -{ - struct isis_area *area; - uint16_t interval; - - area = vty->index; - assert (area); - - interval = atoi (argv[0]); - - if (interval < ISIS_MIN_LSP_LIFETIME) - { - vty_out (vty, "LSP lifetime (%us) below %us%s", - interval, ISIS_MIN_LSP_LIFETIME, VTY_NEWLINE); - - return CMD_WARNING; - } - - - area->max_lsp_lifetime[0] = interval; - area->max_lsp_lifetime[1] = interval; - area->lsp_refresh[0] = interval - 300; - area->lsp_refresh[1] = interval - 300; - - if (area->t_lsp_refresh[0]) - { - thread_cancel (area->t_lsp_refresh[0]); - thread_execute (master, lsp_refresh_l1, area, 0); - } - - if (area->t_lsp_refresh[1]) - { - thread_cancel (area->t_lsp_refresh[1]); - thread_execute (master, lsp_refresh_l2, area, 0); - } - - - return CMD_SUCCESS; -} - -DEFUN (no_lsp_lifetime, - no_lsp_lifetime_cmd, - "no lsp-lifetime", - NO_STR - "LSP lifetime in seconds\n") -{ - struct isis_area *area; - - area = vty->index; - assert (area); - - area->max_lsp_lifetime[0] = MAX_AGE; /* 1200s */ - area->max_lsp_lifetime[1] = MAX_AGE; /* 1200s */ - area->lsp_refresh[0] = MAX_LSP_GEN_INTERVAL; /* 900s */ - area->lsp_refresh[1] = MAX_LSP_GEN_INTERVAL; /* 900s */ - - return CMD_SUCCESS; -} - -ALIAS (no_lsp_lifetime, - no_lsp_lifetime_arg_cmd, - "no lsp-lifetime <380-65535>", - NO_STR - "Maximum LSP lifetime\n" - "LSP lifetime in seconds\n") - -DEFUN (lsp_lifetime_l1, - lsp_lifetime_l1_cmd, - "lsp-lifetime level-1 <380-65535>", - "Maximum LSP lifetime for Level 1 only\n" - "LSP lifetime for Level 1 only in seconds\n") -{ - struct isis_area *area; - uint16_t interval; - - area = vty->index; - assert (area); - - interval = atoi (argv[0]); - - if (interval < ISIS_MIN_LSP_LIFETIME) - { - vty_out (vty, "Level-1 LSP lifetime (%us) below %us%s", - interval, ISIS_MIN_LSP_LIFETIME, VTY_NEWLINE); - - return CMD_WARNING; - } - - area->max_lsp_lifetime[0] = interval; - area->lsp_refresh[0] = interval - 300; - - return CMD_SUCCESS; -} - -DEFUN (no_lsp_lifetime_l1, - no_lsp_lifetime_l1_cmd, - "no lsp-lifetime level-1", - NO_STR - "LSP lifetime for Level 1 only in seconds\n") -{ - struct isis_area *area; - - area = vty->index; - assert (area); - - area->max_lsp_lifetime[0] = MAX_AGE; /* 1200s */ - area->lsp_refresh[0] = MAX_LSP_GEN_INTERVAL; /* 900s */ - - return CMD_SUCCESS; -} - -ALIAS (no_lsp_lifetime_l1, - no_lsp_lifetime_l1_arg_cmd, - "no lsp-lifetime level-1 <380-65535>", - NO_STR - "Maximum LSP lifetime for Level 1 only\n" - "LSP lifetime for Level 1 only in seconds\n") - -DEFUN (lsp_lifetime_l2, - lsp_lifetime_l2_cmd, - "lsp-lifetime level-2 <380-65535>", - "Maximum LSP lifetime for Level 2 only\n" - "LSP lifetime for Level 2 only in seconds\n") -{ - struct isis_area *area; - uint16_t interval; - - area = vty->index; - assert (area); - - interval = atoi (argv[0]); - - if (interval < ISIS_MIN_LSP_LIFETIME) - { - vty_out (vty, "Level-2 LSP lifetime (%us) below %us%s", - interval, ISIS_MIN_LSP_LIFETIME, VTY_NEWLINE); - - return CMD_WARNING; - } - - area->max_lsp_lifetime[1] = interval; - area->lsp_refresh[1] = interval - 300; - - return CMD_SUCCESS; -} - -DEFUN (no_lsp_lifetime_l2, - no_lsp_lifetime_l2_cmd, - "no lsp-lifetime level-2", - NO_STR - "LSP lifetime for Level 2 only in seconds\n") -{ - struct isis_area *area; - - area = vty->index; - assert (area); - - area->max_lsp_lifetime[1] = MAX_AGE; /* 1200s */ - area->lsp_refresh[1] = MAX_LSP_GEN_INTERVAL; /* 900s */ - - return CMD_SUCCESS; -} - -ALIAS (no_lsp_lifetime_l2, - no_lsp_lifetime_l2_arg_cmd, - "no lsp-lifetime level-2 <380-65535>", - NO_STR - "Maximum LSP lifetime for Level 2 only\n" - "LSP lifetime for Level 2 only in seconds\n") +#endif /* TOPOLOGY_GENERATE */ /* IS-IS configuration write function */ int @@ -1939,25 +2066,34 @@ 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) + { + vty_out (vty, " set-overload-bit%s", VTY_NEWLINE); + write++; + } /* ISIS - Area is-type (level-1-2 is default) */ if (area->is_type == IS_LEVEL_1) { vty_out (vty, " is-type level-1%s", VTY_NEWLINE); write++; } - else + else if (area->is_type == IS_LEVEL_2) { - if (area->is_type == IS_LEVEL_2) - { - vty_out (vty, " is-type level-2-only%s", VTY_NEWLINE); - write++; - } + vty_out (vty, " is-type level-2-only%s", VTY_NEWLINE); + write++; } + write += isis_redist_config_write(vty, area, AF_INET); + write += isis_redist_config_write(vty, area, AF_INET6); /* ISIS - Lsp generation interval */ if (area->lsp_gen_interval[0] == area->lsp_gen_interval[1]) { - if (area->lsp_gen_interval[0] != LSP_GEN_INTERVAL_DEFAULT) + if (area->lsp_gen_interval[0] != DEFAULT_MIN_LSP_GEN_INTERVAL) { vty_out (vty, " lsp-gen-interval %d%s", area->lsp_gen_interval[0], VTY_NEWLINE); @@ -1966,13 +2102,13 @@ isis_config_write (struct vty *vty) } else { - if (area->lsp_gen_interval[0] != LSP_GEN_INTERVAL_DEFAULT) + if (area->lsp_gen_interval[0] != DEFAULT_MIN_LSP_GEN_INTERVAL) { vty_out (vty, " lsp-gen-interval level-1 %d%s", area->lsp_gen_interval[0], VTY_NEWLINE); write++; } - if (area->lsp_gen_interval[1] != LSP_GEN_INTERVAL_DEFAULT) + if (area->lsp_gen_interval[1] != DEFAULT_MIN_LSP_GEN_INTERVAL) { vty_out (vty, " lsp-gen-interval level-2 %d%s", area->lsp_gen_interval[1], VTY_NEWLINE); @@ -1982,28 +2118,59 @@ isis_config_write (struct vty *vty) /* ISIS - LSP lifetime */ if (area->max_lsp_lifetime[0] == area->max_lsp_lifetime[1]) { - if (area->max_lsp_lifetime[0] != MAX_AGE) + if (area->max_lsp_lifetime[0] != DEFAULT_LSP_LIFETIME) { - vty_out (vty, " lsp-lifetime %u%s", area->max_lsp_lifetime[0], + vty_out (vty, " max-lsp-lifetime %u%s", area->max_lsp_lifetime[0], VTY_NEWLINE); write++; } } else { - if (area->max_lsp_lifetime[0] != MAX_AGE) + if (area->max_lsp_lifetime[0] != DEFAULT_LSP_LIFETIME) { - vty_out (vty, " lsp-lifetime level-1 %u%s", + vty_out (vty, " max-lsp-lifetime level-1 %u%s", area->max_lsp_lifetime[0], VTY_NEWLINE); write++; } - if (area->max_lsp_lifetime[1] != MAX_AGE) + if (area->max_lsp_lifetime[1] != DEFAULT_LSP_LIFETIME) { - vty_out (vty, " lsp-lifetime level-2 %u%s", + vty_out (vty, " max-lsp-lifetime level-2 %u%s", area->max_lsp_lifetime[1], VTY_NEWLINE); write++; } } + /* ISIS - LSP refresh interval */ + if (area->lsp_refresh[0] == area->lsp_refresh[1]) + { + if (area->lsp_refresh[0] != DEFAULT_MAX_LSP_GEN_INTERVAL) + { + vty_out (vty, " lsp-refresh-interval %u%s", area->lsp_refresh[0], + VTY_NEWLINE); + write++; + } + } + else + { + if (area->lsp_refresh[0] != DEFAULT_MAX_LSP_GEN_INTERVAL) + { + vty_out (vty, " lsp-refresh-interval level-1 %u%s", + area->lsp_refresh[0], VTY_NEWLINE); + write++; + } + if (area->lsp_refresh[1] != DEFAULT_MAX_LSP_GEN_INTERVAL) + { + vty_out (vty, " lsp-refresh-interval level-2 %u%s", + area->lsp_refresh[1], VTY_NEWLINE); + write++; + } + } + if (area->lsp_mtu != DEFAULT_LSP_MTU) + { + vty_out(vty, " lsp-mtu %u%s", area->lsp_mtu, VTY_NEWLINE); + write++; + } + /* Minimum SPF interval. */ if (area->min_spf_interval[0] == area->min_spf_interval[1]) { @@ -2030,9 +2197,23 @@ isis_config_write (struct vty *vty) } } /* Authentication passwords. */ - if (area->area_passwd.len > 0) + if (area->area_passwd.type == ISIS_PASSWD_TYPE_HMAC_MD5) { - vty_out(vty, " area-password %s", area->area_passwd.passwd); + vty_out(vty, " area-password md5 %s", area->area_passwd.passwd); + if (CHECK_FLAG(area->area_passwd.snp_auth, SNP_AUTH_SEND)) + { + vty_out(vty, " authenticate snp "); + if (CHECK_FLAG(area->area_passwd.snp_auth, SNP_AUTH_RECV)) + vty_out(vty, "validate"); + else + vty_out(vty, "send-only"); + } + vty_out(vty, "%s", VTY_NEWLINE); + write++; + } + else if (area->area_passwd.type == ISIS_PASSWD_TYPE_CLEARTXT) + { + vty_out(vty, " area-password clear %s", area->area_passwd.passwd); if (CHECK_FLAG(area->area_passwd.snp_auth, SNP_AUTH_SEND)) { vty_out(vty, " authenticate snp "); @@ -2043,10 +2224,26 @@ isis_config_write (struct vty *vty) } vty_out(vty, "%s", VTY_NEWLINE); write++; - } - if (area->domain_passwd.len > 0) + } + if (area->domain_passwd.type == ISIS_PASSWD_TYPE_HMAC_MD5) { - vty_out(vty, " domain-password %s", area->domain_passwd.passwd); + vty_out(vty, " domain-password md5 %s", + area->domain_passwd.passwd); + if (CHECK_FLAG(area->domain_passwd.snp_auth, SNP_AUTH_SEND)) + { + vty_out(vty, " authenticate snp "); + if (CHECK_FLAG(area->domain_passwd.snp_auth, SNP_AUTH_RECV)) + vty_out(vty, "validate"); + else + vty_out(vty, "send-only"); + } + vty_out(vty, "%s", VTY_NEWLINE); + write++; + } + else if (area->domain_passwd.type == ISIS_PASSWD_TYPE_CLEARTXT) + { + vty_out(vty, " domain-password clear %s", + area->domain_passwd.passwd); if (CHECK_FLAG(area->domain_passwd.snp_auth, SNP_AUTH_SEND)) { vty_out(vty, " authenticate snp "); @@ -2059,12 +2256,18 @@ isis_config_write (struct vty *vty) write++; } + if (area->log_adj_changes) + { + vty_out (vty, " log-adjacency-changes%s", VTY_NEWLINE); + write++; + } + #ifdef TOPOLOGY_GENERATE if (memcmp (area->topology_baseis, DEFAULT_TOPOLOGY_BASEIS, ISIS_SYS_ID_LEN)) { vty_out (vty, " topology base-is %s%s", - sysid_print (area->topology_baseis), VTY_NEWLINE); + sysid_print ((u_char *)area->topology_baseis), VTY_NEWLINE); write++; } if (area->topology_basedynh) @@ -2082,12 +2285,13 @@ isis_config_write (struct vty *vty) #endif /* TOPOLOGY_GENERATE */ } + isis_mpls_te_config_write_router(vty); } return write; } -static struct cmd_node isis_node = { +struct cmd_node isis_node = { ISIS_NODE, "%s(config-router)# ", 1 @@ -2099,24 +2303,26 @@ isis_init () /* Install IS-IS top node */ install_node (&isis_node, isis_config_write); - install_element (VIEW_NODE, &show_clns_neighbors_cmd); - install_element (VIEW_NODE, &show_isis_neighbors_cmd); - install_element (VIEW_NODE, &show_clns_neighbors_detail_cmd); - install_element (VIEW_NODE, &show_isis_neighbors_detail_cmd); + install_element (VIEW_NODE, &show_isis_summary_cmd); + + install_element (VIEW_NODE, &show_isis_interface_cmd); + install_element (VIEW_NODE, &show_isis_interface_detail_cmd); + install_element (VIEW_NODE, &show_isis_interface_arg_cmd); + + install_element (VIEW_NODE, &show_isis_neighbor_cmd); + install_element (VIEW_NODE, &show_isis_neighbor_detail_cmd); + install_element (VIEW_NODE, &show_isis_neighbor_arg_cmd); + install_element (VIEW_NODE, &clear_isis_neighbor_cmd); + install_element (VIEW_NODE, &clear_isis_neighbor_arg_cmd); install_element (VIEW_NODE, &show_hostname_cmd); install_element (VIEW_NODE, &show_database_cmd); + install_element (VIEW_NODE, &show_database_arg_cmd); + install_element (VIEW_NODE, &show_database_arg_detail_cmd); install_element (VIEW_NODE, &show_database_detail_cmd); + install_element (VIEW_NODE, &show_database_detail_arg_cmd); - install_element (ENABLE_NODE, &show_clns_neighbors_cmd); - install_element (ENABLE_NODE, &show_isis_neighbors_cmd); - install_element (ENABLE_NODE, &show_clns_neighbors_detail_cmd); - install_element (ENABLE_NODE, &show_isis_neighbors_detail_cmd); - - install_element (ENABLE_NODE, &show_hostname_cmd); - install_element (ENABLE_NODE, &show_database_cmd); - install_element (ENABLE_NODE, &show_database_detail_cmd); - install_element (ENABLE_NODE, &show_debugging_cmd); + install_element (ENABLE_NODE, &show_debugging_isis_cmd); install_node (&debug_node, config_write_debug); @@ -2142,6 +2348,12 @@ isis_init () install_element (ENABLE_NODE, &no_debug_isis_rtevents_cmd); install_element (ENABLE_NODE, &debug_isis_events_cmd); install_element (ENABLE_NODE, &no_debug_isis_events_cmd); + install_element (ENABLE_NODE, &debug_isis_packet_dump_cmd); + install_element (ENABLE_NODE, &no_debug_isis_packet_dump_cmd); + install_element (ENABLE_NODE, &debug_isis_lsp_gen_cmd); + install_element (ENABLE_NODE, &no_debug_isis_lsp_gen_cmd); + install_element (ENABLE_NODE, &debug_isis_lsp_sched_cmd); + install_element (ENABLE_NODE, &no_debug_isis_lsp_sched_cmd); install_element (CONFIG_NODE, &debug_isis_adj_cmd); install_element (CONFIG_NODE, &no_debug_isis_adj_cmd); @@ -2165,6 +2377,12 @@ isis_init () install_element (CONFIG_NODE, &no_debug_isis_rtevents_cmd); install_element (CONFIG_NODE, &debug_isis_events_cmd); install_element (CONFIG_NODE, &no_debug_isis_events_cmd); + install_element (CONFIG_NODE, &debug_isis_packet_dump_cmd); + install_element (CONFIG_NODE, &no_debug_isis_packet_dump_cmd); + install_element (CONFIG_NODE, &debug_isis_lsp_gen_cmd); + install_element (CONFIG_NODE, &no_debug_isis_lsp_gen_cmd); + install_element (CONFIG_NODE, &debug_isis_lsp_sched_cmd); + install_element (CONFIG_NODE, &no_debug_isis_lsp_sched_cmd); install_element (CONFIG_NODE, &router_isis_cmd); install_element (CONFIG_NODE, &no_router_isis_cmd); @@ -2174,52 +2392,9 @@ isis_init () install_element (ISIS_NODE, &net_cmd); install_element (ISIS_NODE, &no_net_cmd); - install_element (ISIS_NODE, &is_type_cmd); - install_element (ISIS_NODE, &no_is_type_cmd); - - install_element (ISIS_NODE, &area_passwd_cmd); - install_element (ISIS_NODE, &area_passwd_snpauth_cmd); - install_element (ISIS_NODE, &no_area_passwd_cmd); - - install_element (ISIS_NODE, &domain_passwd_cmd); - install_element (ISIS_NODE, &domain_passwd_snpauth_cmd); - install_element (ISIS_NODE, &no_domain_passwd_cmd); - - install_element (ISIS_NODE, &lsp_gen_interval_cmd); - install_element (ISIS_NODE, &no_lsp_gen_interval_cmd); - install_element (ISIS_NODE, &no_lsp_gen_interval_arg_cmd); - install_element (ISIS_NODE, &lsp_gen_interval_l1_cmd); - install_element (ISIS_NODE, &no_lsp_gen_interval_l1_cmd); - install_element (ISIS_NODE, &no_lsp_gen_interval_l1_arg_cmd); - install_element (ISIS_NODE, &lsp_gen_interval_l2_cmd); - install_element (ISIS_NODE, &no_lsp_gen_interval_l2_cmd); - install_element (ISIS_NODE, &no_lsp_gen_interval_l2_arg_cmd); - - install_element (ISIS_NODE, &spf_interval_cmd); - install_element (ISIS_NODE, &no_spf_interval_cmd); - install_element (ISIS_NODE, &no_spf_interval_arg_cmd); - install_element (ISIS_NODE, &spf_interval_l1_cmd); - install_element (ISIS_NODE, &no_spf_interval_l1_cmd); - install_element (ISIS_NODE, &no_spf_interval_l1_arg_cmd); - install_element (ISIS_NODE, &spf_interval_l2_cmd); - install_element (ISIS_NODE, &no_spf_interval_l2_cmd); - install_element (ISIS_NODE, &no_spf_interval_l2_arg_cmd); - - install_element (ISIS_NODE, &lsp_lifetime_cmd); - install_element (ISIS_NODE, &no_lsp_lifetime_cmd); - install_element (ISIS_NODE, &no_lsp_lifetime_arg_cmd); - install_element (ISIS_NODE, &lsp_lifetime_l1_cmd); - install_element (ISIS_NODE, &no_lsp_lifetime_l1_cmd); - install_element (ISIS_NODE, &no_lsp_lifetime_l1_arg_cmd); - install_element (ISIS_NODE, &lsp_lifetime_l2_cmd); - install_element (ISIS_NODE, &no_lsp_lifetime_l2_cmd); - install_element (ISIS_NODE, &no_lsp_lifetime_l2_arg_cmd); - - install_element (ISIS_NODE, &dynamic_hostname_cmd); - install_element (ISIS_NODE, &no_dynamic_hostname_cmd); - - install_element (ISIS_NODE, &metric_style_cmd); - install_element (ISIS_NODE, &no_metric_style_cmd); + install_element (ISIS_NODE, &log_adj_changes_cmd); + install_element (ISIS_NODE, &no_log_adj_changes_cmd); + #ifdef TOPOLOGY_GENERATE install_element (ISIS_NODE, &topology_generate_grid_cmd); install_element (ISIS_NODE, &topology_baseis_cmd); @@ -2227,11 +2402,5 @@ isis_init () install_element (ISIS_NODE, &no_topology_baseis_cmd); install_element (ISIS_NODE, &no_topology_baseis_noid_cmd); install_element (VIEW_NODE, &show_isis_generated_topology_cmd); - install_element (ENABLE_NODE, &show_isis_generated_topology_cmd); #endif /* TOPOLOGY_GENERATE */ - - isis_new (0); - isis_circuit_init (); - isis_zebra_init (); - isis_spf_cmds_init (); } diff --git a/isisd/isisd.h b/isisd/isisd.h index b17982e25..eedb451dd 100644 --- a/isisd/isisd.h +++ b/isisd/isisd.h @@ -25,21 +25,22 @@ #define ISISD_VERSION "0.0.7" +#include "isisd/isis_constants.h" +#include "isisd/isis_common.h" +#include "isisd/isis_redist.h" +#include "isis_flags.h" +#include "dict.h" + /* uncomment if you are a developer in bug hunt */ /* #define EXTREME_DEBUG */ /* #define EXTREME_TLV_DEBUG */ -struct rmap -{ - char *name; - struct route_map *map; -}; - struct isis { u_long process_id; int sysid_set; u_char sysid[ISIS_SYS_ID_LEN]; /* SystemID for this IS */ + u_int32_t router_id; /* Router ID from zebra */ struct list *area_list; /* list of IS-IS areas */ struct list *init_circ_list; struct list *nexthops; /* IPv4 next hops from this IS */ @@ -52,32 +53,11 @@ struct isis time_t uptime; /* when did we start */ struct thread *t_dync_clean; /* dynamic hostname cache cleanup thread */ - /* Redistributed external information. */ - struct route_table *external_info[ZEBRA_ROUTE_MAX + 1]; - /* Redistribute metric info. */ - struct - { - int type; /* Internal or External */ - int value; /* metric value */ - } dmetric[ZEBRA_ROUTE_MAX + 1]; - - struct - { - char *name; - struct route_map *map; - } rmap[ZEBRA_ROUTE_MAX + 1]; -#ifdef HAVE_IPV6 - struct - { - struct - { - char *name; - struct route_map *map; - } rmap[ZEBRA_ROUTE_MAX + 1]; - } inet6_afmode; -#endif + struct route_table *ext_info[REDIST_PROTOCOL_COUNT]; }; +extern struct isis *isis; + struct isis_area { struct isis *isis; /* back pointer */ @@ -88,15 +68,24 @@ struct isis_area struct isis_spftree *spftree6[ISIS_LEVELS]; /* The v6 SPTs */ struct route_table *route_table6[ISIS_LEVELS]; /* IPv6 routes */ #endif - unsigned int min_bcast_mtu; +#define DEFAULT_LSP_MTU 1497 + unsigned int lsp_mtu; /* Size of LSPs to generate */ struct list *circuit_list; /* IS-IS circuits */ struct flags flags; struct thread *t_tick; /* LSP walker */ - struct thread *t_remove_aged; - struct thread *t_lsp_l1_regenerate; - struct thread *t_lsp_l2_regenerate; - int lsp_regenerate_pending[ISIS_LEVELS]; struct thread *t_lsp_refresh[ISIS_LEVELS]; + /* t_lsp_refresh is used in two ways: + * a) regular refresh of LSPs + * b) (possibly throttled) updates to LSPs + * + * The lsp_regenerate_pending flag tracks whether the timer is active + * for the a) or the b) case. + * + * It is of utmost importance to clear this flag when the timer is + * rescheduled for normal refresh, because otherwise, updates will + * be delayed until the next regular refresh. + */ + int lsp_regenerate_pending[ISIS_LEVELS]; /* * Configurables @@ -114,6 +103,10 @@ struct isis_area struct list *area_addrs; u_int16_t max_lsp_lifetime[ISIS_LEVELS]; char is_type; /* level-1 level-1-2 or level-2-only */ + /* are we overloaded? */ + char overload_bit; + /* L1/L2 router identifier for inter-area traffic */ + char attached_bit; u_int16_t lsp_refresh[ISIS_LEVELS]; /* minimum time allowed before lsp retransmission */ u_int16_t lsp_gen_interval[ISIS_LEVELS]; @@ -122,22 +115,53 @@ struct isis_area /* the percentage of LSP mtu size used, before generating a new frag */ int lsp_frag_threshold; int ip_circuits; + /* logging adjacency changes? */ + u_char log_adj_changes; #ifdef HAVE_IPV6 int ipv6_circuits; #endif /* HAVE_IPV6 */ /* Counters */ u_int32_t circuit_state_changes; + struct isis_redist redist_settings[REDIST_PROTOCOL_COUNT] + [ZEBRA_ROUTE_MAX + 1][ISIS_LEVELS]; + struct route_table *ext_reach[REDIST_PROTOCOL_COUNT][ISIS_LEVELS]; #ifdef TOPOLOGY_GENERATE struct list *topology; - char topology_baseis[ISIS_SYS_ID_LEN]; /* IS for the first IS emulated. */ + u_char topology_baseis[ISIS_SYS_ID_LEN]; /* IS for the first IS emulated. */ char *topology_basedynh; /* Dynamic hostname base. */ char top_params[200]; /* FIXME: what is reasonable? */ #endif /* TOPOLOGY_GENERATE */ }; void isis_init (void); +void isis_new(unsigned long); +struct isis_area *isis_area_create(const char *); struct isis_area *isis_area_lookup (const char *); +int isis_area_get (struct vty *vty, const char *area_tag); +void print_debug(struct vty *, int, int); + +void isis_area_overload_bit_set(struct isis_area *area, bool overload_bit); +void isis_area_attached_bit_set(struct isis_area *area, bool attached_bit); +void isis_area_dynhostname_set(struct isis_area *area, bool dynhostname); +void isis_area_metricstyle_set(struct isis_area *area, bool old_metric, + bool new_metric); +void isis_area_lsp_mtu_set(struct isis_area *area, unsigned int lsp_mtu); +void isis_area_is_type_set(struct isis_area *area, int is_type); +void isis_area_max_lsp_lifetime_set(struct isis_area *area, int level, + uint16_t max_lsp_lifetime); +void isis_area_lsp_refresh_set(struct isis_area *area, int level, + uint16_t lsp_refresh); +/* IS_LEVEL_1 sets area_passwd, IS_LEVEL_2 domain_passwd */ +int isis_area_passwd_unset (struct isis_area *area, int level); +int isis_area_passwd_cleartext_set (struct isis_area *area, int level, + const char *passwd, u_char snp_auth); +int isis_area_passwd_hmac_md5_set (struct isis_area *area, int level, + const char *passwd, u_char snp_auth); +void isis_vty_init (void); + +/* Master of threads. */ +extern struct thread_master *master; #define DEBUG_ADJ_PACKETS (1<<0) #define DEBUG_CHECKSUM_ERRORS (1<<1) @@ -151,5 +175,28 @@ struct isis_area *isis_area_lookup (const char *); #define DEBUG_RTE_EVENTS (1<<9) #define DEBUG_EVENTS (1<<10) #define DEBUG_ZEBRA (1<<11) +#define DEBUG_PACKET_DUMP (1<<12) +#define DEBUG_LSP_GEN (1<<13) +#define DEBUG_LSP_SCHED (1<<14) + +#define lsp_debug(...) \ + do \ + { \ + if (isis->debugs & DEBUG_LSP_GEN) \ + zlog_debug(__VA_ARGS__); \ + } \ + while (0) + +#define sched_debug(...) \ + do \ + { \ + if (isis->debugs & DEBUG_LSP_SCHED) \ + zlog_debug(__VA_ARGS__); \ + } \ + while (0) + +#define DEBUG_TE (1<<13) + +#define IS_DEBUG_ISIS(x) (isis->debugs & x) #endif /* ISISD_H */ diff --git a/isisd/topology/Makefile.am b/isisd/topology/Makefile.am index b25497cb3..fe73ae5c6 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 +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/isisd/topology/random.c b/isisd/topology/random.c index c49c08202..2c457c52c 100644 --- a/isisd/topology/random.c +++ b/isisd/topology/random.c @@ -7,6 +7,7 @@ /* */ /*********************************************************************/ +#include #include #include diff --git a/isisd/topology/spacyc.c b/isisd/topology/spacyc.c index 853144737..91a4799ce 100644 --- a/isisd/topology/spacyc.c +++ b/isisd/topology/spacyc.c @@ -1,3 +1,4 @@ +#include #include #include #include diff --git a/isisd/topology/spgrid.c b/isisd/topology/spgrid.c index 611b67279..e1c87abf2 100644 --- a/isisd/topology/spgrid.c +++ b/isisd/topology/spgrid.c @@ -1,11 +1,11 @@ +#include + #include #include #include #include "random.c" -#include - #include "thread.h" #include "vty.h" #include "log.h" @@ -50,8 +50,8 @@ long X, /* horizontal size of grid */ long x, y, - y1, y2, yp, - dl, dx, xn, yn, count, + yy1, yy2, yyp, + dl, dx, xn, yyn, count, *mess; double n; @@ -598,7 +598,7 @@ gen_spgrid_topology (struct vty *vty, struct list *topology) init_rand ( seed1); pl = pl - pm + 1; - for ( x = 0; x < X; x ++ ) + for ( x = 0; x < X; x ++ ) { for ( y = 0; y < Y; y ++ ) { p_t = pm + nrand ( pl ); if ( pn_f ) p_t *= (long) ( (1 + x) * pn ); @@ -606,9 +606,10 @@ gen_spgrid_topology (struct vty *vty, struct list *topology) p[ NODE ( x, y ) ] = p_t; } - p[n0] = 0; - if ( s_f ) p[n0-1] = 0; } + p[n0] = 0; + if ( s_f ) p[n0-1] = 0; + } if ( s_f ) /* additional arcs from artifical source */ { @@ -670,12 +671,12 @@ gen_spgrid_topology (struct vty *vty, struct list *topology) for ( k = ax; k > 0; k -- ) { - y1 = nrand ( Y ); + yy1 = nrand ( Y ); do - y2 = nrand ( Y ); - while ( y2 == y1 ); - i = NODE ( x, y1 ); - j = NODE ( x, y2 ); + yy2 = nrand ( Y ); + while ( yy2 == yy1 ); + i = NODE ( x, yy1 ); + j = NODE ( x, yy2 ); l = am + nrand ( al ); print_arc (vty, topology, i, j, l ); } @@ -711,13 +712,13 @@ gen_spgrid_topology (struct vty *vty, struct list *topology) dx = xn - x; if ( ip_f ) { - yp = nrand(Y-y); - yn = mess[ yp ]; - mess[ yp ] = mess[ Y - y - 1 ]; + yyp = nrand(Y-y); + yyn = mess[ yyp ]; + mess[ yyp ] = mess[ Y - y - 1 ]; } else - yn = y; - j = NODE ( xn, yn ); + yyn = y; + j = NODE ( xn, yyn ); l = im + nrand ( il ); if ( in != 0 ) l *= (long) ( in * dx ); diff --git a/isisd/topology/sprand.c b/isisd/topology/sprand.c index 28b58b30e..1c1eb1912 100644 --- a/isisd/topology/sprand.c +++ b/isisd/topology/sprand.c @@ -1,3 +1,5 @@ +#include + #include #include #include diff --git a/lib/.gitignore b/lib/.gitignore index 00af85a6e..02aa432ce 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 890cc5ca9..cb9e1fb57 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -1,24 +1,26 @@ ## Process this file with automake to produce Makefile.in. -INCLUDES = @INCLUDES@ -I.. -I$(top_srcdir) -I$(top_srcdir)/lib @SNMP_INCLUDES@ +AM_CPPFLAGS = -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib +AM_CFLAGS = $(WERROR) DEFS = @DEFS@ -DSYSCONFDIR=\"$(sysconfdir)/\" lib_LTLIBRARIES = libzebra.la -libzebra_la_LDFLAGS = -version-info 0:0:0 +libzebra_la_LDFLAGS = -version-info 1:0:0 libzebra_la_SOURCES = \ network.c pid_output.c getopt.c getopt1.c daemon.c \ checksum.c vector.c linklist.c vty.c command.c \ sockunion.c prefix.c thread.c if.c memory.c buffer.c table.c hash.c \ filter.c routemap.c distribute.c stream.c str.c log.c plist.c \ - zclient.c sockopt.c smux.c md5.c if_rmap.c keychain.c privs.c \ - sigevent.c pqueue.c jhash.c memtypes.c workqueue.c + 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 vrf.c \ + event_counter.c nexthop.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 +29,43 @@ 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 vrf.h fifo.h event_counter.h \ + nexthop.h -EXTRA_DIST = regex.c regex-gnu.h memtypes.awk route_types.pl route_types.txt +noinst_HEADERS = \ + plist_int.h + +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/agentx.c b/lib/agentx.c new file mode 100644 index 000000000..5d7d057da --- /dev/null +++ b/lib/agentx.c @@ -0,0 +1,315 @@ +/* SNMP support + * Copyright (C) 2012 Vincent Bernat + * + * 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 + +#if defined HAVE_SNMP && defined SNMP_AGENTX +#include +#include +#include +#include + +#include "command.h" +#include "smux.h" + +static int agentx_enabled = 0; + +static struct thread_master *agentx_tm; +static struct thread *timeout_thr = NULL; +static struct list *events = NULL; + +static void agentx_events_update(void); + +static int +agentx_timeout(struct thread *t) +{ + timeout_thr = NULL; + + snmp_timeout (); + run_alarms (); + netsnmp_check_outstanding_agent_requests (); + agentx_events_update (); + return 0; +} + +static int +agentx_read(struct thread *t) +{ + fd_set fds; + struct listnode *ln = THREAD_ARG (t); + list_delete_node (events, ln); + + FD_ZERO (&fds); + FD_SET (THREAD_FD (t), &fds); + snmp_read (&fds); + + netsnmp_check_outstanding_agent_requests (); + agentx_events_update (); + return 0; +} + +static void +agentx_events_update(void) +{ + int maxfd = 0; + int block = 1; + struct timeval timeout = { .tv_sec = 0, .tv_usec = 0 }; + fd_set fds; + struct listnode *ln; + struct thread *thr; + int fd, thr_fd; + + THREAD_OFF (timeout_thr); + + FD_ZERO (&fds); + snmp_select_info (&maxfd, &fds, &timeout, &block); + + if (!block) + timeout_thr = thread_add_timer_tv (agentx_tm, agentx_timeout, NULL, &timeout); + + ln = listhead (events); + thr = ln ? listgetdata (ln) : NULL; + thr_fd = thr ? THREAD_FD (thr) : -1; + + /* "two-pointer" / two-list simultaneous iteration + * ln/thr/thr_fd point to the next existing event listener to hit while + * fd counts to catch up */ + for (fd = 0; fd < maxfd; fd++) + { + /* caught up */ + if (thr_fd == fd) + { + struct listnode *nextln = listnextnode (ln); + if (!FD_ISSET (fd, &fds)) + { + thread_cancel (thr); + list_delete_node (events, ln); + } + ln = nextln; + thr = ln ? listgetdata (ln) : NULL; + thr_fd = thr ? THREAD_FD (thr) : -1; + } + /* need listener, but haven't hit one where it would be */ + else if (FD_ISSET (fd, &fds)) + { + struct listnode *newln; + thr = thread_add_read (agentx_tm, agentx_read, NULL, fd); + newln = listnode_add_before (events, ln, thr); + thr->arg = newln; + } + } + + /* leftover event listeners at this point have fd > maxfd, delete them */ + while (ln) + { + struct listnode *nextln = listnextnode (ln); + thread_cancel (listgetdata (ln)); + list_delete_node (events, ln); + ln = nextln; + } +} + +/* AgentX node. */ +static struct cmd_node agentx_node = +{ + SMUX_NODE, + "" /* AgentX has no interface. */ +}; + +/* Logging NetSNMP messages */ +static int +agentx_log_callback(int major, int minor, + void *serverarg, void *clientarg) +{ + struct snmp_log_message *slm = (struct snmp_log_message *)serverarg; + char *msg = strdup (slm->msg); + if (msg) msg[strlen(msg)-1] = '\0'; + switch (slm->priority) + { + case LOG_EMERG: zlog_err ("snmp[emerg]: %s", msg?msg:slm->msg); break; + case LOG_ALERT: zlog_err ("snmp[alert]: %s", msg?msg:slm->msg); break; + case LOG_CRIT: zlog_err ("snmp[crit]: %s", msg?msg:slm->msg); break; + case LOG_ERR: zlog_err ("snmp[err]: %s", msg?msg:slm->msg); break; + case LOG_WARNING: zlog_warn ("snmp[warning]: %s", msg?msg:slm->msg); break; + case LOG_NOTICE: zlog_notice("snmp[notice]: %s", msg?msg:slm->msg); break; + case LOG_INFO: zlog_info ("snmp[info]: %s", msg?msg:slm->msg); break; + case LOG_DEBUG: zlog_debug ("snmp[debug]: %s", msg?msg:slm->msg); break; + } + free(msg); + return SNMP_ERR_NOERROR; +} + +static int +config_write_agentx (struct vty *vty) +{ + if (agentx_enabled) + vty_out (vty, "agentx%s", VTY_NEWLINE); + return 0; +} + +DEFUN (agentx_enable, + agentx_enable_cmd, + "agentx", + "SNMP AgentX protocol settings\n" + "SNMP AgentX settings\n") +{ + if (!agentx_enabled) + { + init_snmp("quagga"); + events = list_new(); + agentx_events_update (); + agentx_enabled = 1; + return CMD_SUCCESS; + } + vty_out (vty, "SNMP AgentX already enabled%s", VTY_NEWLINE); + return CMD_WARNING; +} + +DEFUN (no_agentx, + no_agentx_cmd, + "no agentx", + NO_STR + "SNMP AgentX protocol settings\n" + "SNMP AgentX settings\n") +{ + if (!agentx_enabled) return CMD_SUCCESS; + vty_out (vty, "SNMP AgentX support cannot be disabled once enabled%s", VTY_NEWLINE); + return CMD_WARNING; +} + +void +smux_init (struct thread_master *tm) +{ + agentx_tm = tm; + + netsnmp_enable_subagent (); + snmp_disable_log (); + snmp_enable_calllog (); + snmp_register_callback (SNMP_CALLBACK_LIBRARY, + SNMP_CALLBACK_LOGGING, + agentx_log_callback, + NULL); + init_agent ("quagga"); + + install_node (&agentx_node, config_write_agentx); + install_element (CONFIG_NODE, &agentx_enable_cmd); + install_element (CONFIG_NODE, &no_agentx_cmd); +} + +void +smux_register_mib (const char *descr, struct variable *var, + size_t width, int num, + oid name[], size_t namelen) +{ + register_mib (descr, var, width, num, name, namelen); +} + +int +smux_trap (struct variable *vp, size_t vp_len, + const oid *ename, size_t enamelen, + const oid *name, size_t namelen, + const oid *iname, size_t inamelen, + const struct trap_object *trapobj, size_t trapobjlen, + u_char sptrap) +{ + oid objid_snmptrap[] = { 1, 3, 6, 1, 6, 3, 1, 1, 4, 1, 0 }; + size_t objid_snmptrap_len = sizeof objid_snmptrap / sizeof (oid); + oid notification_oid[MAX_OID_LEN]; + size_t notification_oid_len; + unsigned int i; + + netsnmp_variable_list *notification_vars = NULL; + if (!agentx_enabled) return 0; + + /* snmpTrapOID */ + oid_copy (notification_oid, ename, enamelen); + notification_oid[enamelen] = sptrap; + notification_oid_len = enamelen + 1; + snmp_varlist_add_variable (¬ification_vars, + objid_snmptrap, objid_snmptrap_len, + ASN_OBJECT_ID, + (u_char *) notification_oid, + notification_oid_len * sizeof(oid)); + + /* Provided bindings */ + for (i = 0; i < trapobjlen; i++) + { + unsigned int j; + oid oid[MAX_OID_LEN]; + size_t oid_len, onamelen; + u_char *val; + size_t val_len; + WriteMethod *wm = NULL; + struct variable cvp; + + /* Make OID. */ + if (trapobj[i].namelen > 0) + { + /* Columnar object */ + onamelen = trapobj[i].namelen; + oid_copy (oid, name, namelen); + oid_copy (oid + namelen, trapobj[i].name, onamelen); + oid_copy (oid + namelen + onamelen, iname, inamelen); + oid_len = namelen + onamelen + inamelen; + } + else + { + /* Scalar object */ + onamelen = trapobj[i].namelen * (-1); + oid_copy (oid, name, namelen); + oid_copy (oid + namelen, trapobj[i].name, onamelen); + oid[onamelen + namelen] = 0; + oid_len = namelen + onamelen + 1; + } + + /* Locate the appropriate function and type in the MIB registry. */ + for (j = 0; j < vp_len; j++) + { + if (oid_compare (trapobj[i].name, onamelen, vp[j].name, vp[j].namelen) != 0) + continue; + /* We found the appropriate variable in the MIB registry. */ + oid_copy(cvp.name, name, namelen); + oid_copy(cvp.name + namelen, vp[j].name, vp[j].namelen); + cvp.namelen = namelen + vp[j].namelen; + cvp.type = vp[j].type; + cvp.magic = vp[j].magic; + cvp.acl = vp[j].acl; + cvp.findVar = vp[j].findVar; + /* Grab the result. */ + val = cvp.findVar (&cvp, oid, &oid_len, 1, &val_len, &wm); + if (!val) break; + snmp_varlist_add_variable (¬ification_vars, + oid, oid_len, + vp[j].type, + val, + val_len); + break; + } + } + + + send_v2trap (notification_vars); + snmp_free_varbind (notification_vars); + agentx_events_update (); + return 1; +} + +#endif /* HAVE_SNMP */ diff --git a/lib/buffer.c b/lib/buffer.c index f19a9e0c8..ee9310100 100644 --- a/lib/buffer.c +++ b/lib/buffer.c @@ -148,7 +148,7 @@ buffer_add (struct buffer *b) { struct buffer_data *d; - d = XMALLOC(MTYPE_BUFFER_DATA, offsetof(struct buffer_data, data[b->size])); + d = XMALLOC(MTYPE_BUFFER_DATA, offsetof(struct buffer_data, data) + b->size); d->cp = d->sp = 0; d->next = NULL; @@ -262,7 +262,7 @@ buffer_flush_window (struct buffer *b, int fd, int width, int height, /* For erase and more data add two to b's buffer_data count.*/ if (b->head->next == NULL) { - iov_alloc = sizeof(small_iov)/sizeof(small_iov[0]); + iov_alloc = array_size(small_iov); iov = small_iov; } else @@ -322,7 +322,8 @@ buffer_flush_window (struct buffer *b, int fd, int width, int height, /* This should absolutely never occur. */ zlog_err("%s: corruption detected: iov_small overflowed; " "head %p, tail %p, head->next %p", - __func__, b->head, b->tail, b->head->next); + __func__, (void *)b->head, (void *)b->tail, + (void *)b->head->next); iov = XMALLOC(MTYPE_TMP, iov_alloc*sizeof(*iov)); memcpy(iov, small_iov, sizeof(small_iov)); } diff --git a/lib/checksum.c b/lib/checksum.c index 3ddde8152..43940b715 100644 --- a/lib/checksum.c +++ b/lib/checksum.c @@ -51,6 +51,8 @@ in_cksum(void *parg, int nbytes) /* To be consistent, offset is 0-based index, rather than the 1-based index required in the specification ISO 8473, Annex C.1 */ +/* calling with offset == FLETCHER_CHECKSUM_VALIDATE will validate the checksum + without modifying the buffer; a valid checksum returns 0 */ u_int16_t fletcher_checksum(u_char * buffer, const size_t len, const uint16_t offset) { @@ -62,13 +64,14 @@ fletcher_checksum(u_char * buffer, const size_t len, const uint16_t offset) checksum = 0; - assert (offset < len); - /* - * Zero the csum in the packet. - */ - csum = (u_int16_t *) (buffer + offset); - *(csum) = 0; + if (offset != FLETCHER_CHECKSUM_VALIDATE) + /* Zero the csum in the packet. */ + { + assert (offset < (len - 1)); /* account for two bytes of checksum */ + csum = (u_int16_t *) (buffer + offset); + *(csum) = 0; + } p = buffer; c0 = 0; @@ -89,7 +92,7 @@ fletcher_checksum(u_char * buffer, const size_t len, const uint16_t offset) left -= partial_len; } - + /* The cast is important, to ensure the mod is taken as a signed value. */ x = (int)((len - offset - 1) * c0 - c1) % 255; @@ -98,17 +101,24 @@ fletcher_checksum(u_char * buffer, const size_t len, const uint16_t offset) y = 510 - c0 - x; if (y > 255) y -= 255; - - /* - * Now we write this to the packet. - * We could skip this step too, since the checksum returned would - * be stored into the checksum field by the caller. - */ - buffer[offset] = x; - buffer[offset + 1] = y; - - /* Take care of the endian issue */ - checksum = htons((x << 8) | (y & 0xFF)); + + if (offset == FLETCHER_CHECKSUM_VALIDATE) + { + checksum = (c1 << 8) + c0; + } + else + { + /* + * Now we write this to the packet. + * We could skip this step too, since the checksum returned would + * be stored into the checksum field by the caller. + */ + buffer[offset] = x; + buffer[offset + 1] = y; + + /* Take care of the endian issue */ + checksum = htons((x << 8) | (y & 0xFF)); + } return checksum; } diff --git a/lib/checksum.h b/lib/checksum.h index da1d3cbad..b310f744c 100644 --- a/lib/checksum.h +++ b/lib/checksum.h @@ -1,2 +1,3 @@ extern int in_cksum(void *, int); +#define FLETCHER_CHECKSUM_VALIDATE 0xffff extern u_int16_t fletcher_checksum(u_char *, const size_t len, const uint16_t offset); diff --git a/lib/command.c b/lib/command.c index e62a7a7e6..662f8a3d8 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 * @@ -186,61 +212,27 @@ argv_concat (const char **argv, int argc, int shift) return str; } -/* Install top node of command vector. */ -void -install_node (struct cmd_node *node, - int (*func) (struct vty *)) -{ - vector_set_index (cmdvec, node->node, node); - node->func = func; - 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) +static unsigned int +cmd_hash_key (void *p) { - 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); + return (uintptr_t) p; } static int -cmp_desc (const void *p, const void *q) +cmd_hash_cmp (const void *a, const void *b) { - const struct desc *a = *(struct desc * const *)p; - const struct desc *b = *(struct desc * const *)q; - - return strcmp (a->cmd, b->cmd); + return a == b; } -/* Sort each node's command element according to command string. */ +/* Install top node of command vector. */ void -sort_node () +install_node (struct cmd_node *node, + int (*func) (struct vty *)) { - 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); - } - } + vector_set_index (cmdvec, node->node, node); + node->func = func; + node->cmd_vector = vector_init (VECTOR_MIN_SIZE); + node->cmd_hash = hash_create (cmd_hash_key, cmd_hash_cmp); } /* Breaking up string into each command piece. I assume given @@ -312,15 +304,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 +362,245 @@ 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; - len = cp - sp; + token = XCALLOC(MTYPE_CMD_TOKENS, sizeof(*token)); + token->type = TOKEN_MULTIPLE; + token->multiple = vector_init(VECTOR_MIN_SIZE); - token = XMALLOC (MTYPE_STRVEC, len + 1); - memcpy (token, sp, len); - *(token + len) = '\0'; + vector_set(state->curvect, token); + if (state->curvect != state->topvect) + state->intvect = state->curvect; + state->curvect = token->multiple; +} - desc = XCALLOC (MTYPE_DESC, sizeof (struct desc)); - desc->cmd = token; - desc->str = cmd_desc_str (&dp); +static void +format_parser_end_keyword(struct format_parser_state *state) +{ + if (state->in_multiple + || !state->in_keyword) + format_parser_error(state, "Unexpected '}'"); - 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); + if (state->in_keyword == 1) + format_parser_error(state, "Empty keyword group"); + + state->cp++; + state->in_keyword = 0; + state->curvect = state->topvect; +} + +static void +format_parser_end_multiple(struct format_parser_state *state) +{ + char *dummy; + + if (!state->in_multiple) + format_parser_error(state, "Unexpected ')'"); + + 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; + if (strcmp (cmd, "A.B.C.D") == 0) + token->terminal = TERMINAL_IPV4; + else if (strcmp (cmd, "A.B.C.D/M") == 0) + token->terminal = TERMINAL_IPV4_PREFIX; + else if (strcmp (cmd, "X:X::X:X") == 0) + token->terminal = TERMINAL_IPV6; + else if (strcmp (cmd, "X:X::X:X/M") == 0) + token->terminal = TERMINAL_IPV6_PREFIX; + else if (cmd[0] == '[') + token->terminal = TERMINAL_OPTION; + else if (cmd[0] == '.') + token->terminal = TERMINAL_VARARG; + else if (cmd[0] == '<') + token->terminal = TERMINAL_RANGE; + else if (cmd[0] >= 'A' && cmd[0] <= 'Z') + token->terminal = TERMINAL_VARIABLE; + else + token->terminal = TERMINAL_LITERAL; + + 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. */ @@ -485,7 +621,11 @@ install_element (enum node_type ntype, struct cmd_element *cmd) /* cmd_init hasn't been called */ if (!cmdvec) - return; + { + fprintf (stderr, "%s called before cmd_init, breakage likely\n", + __func__); + return; + } cnode = vector_slot (cmdvec, ntype); @@ -495,13 +635,25 @@ install_element (enum node_type ntype, struct cmd_element *cmd) ntype); exit (1); } - + + if (hash_lookup (cnode->cmd_hash, cmd) != NULL) + { +#ifdef DEV_BUILD + fprintf (stderr, + "Multiple command installs to node %d of command:\n%s\n", + ntype, cmd->string); +#endif + return; + } + + assert (hash_get (cnode->cmd_hash, cmd, hash_alloc_intern)); + vector_set (cnode->cmd_vector, cmd); + if (cmd->tokens == NULL) + cmd->tokens = cmd_parse_format(cmd->string, cmd->doc); - if (cmd->strvec == NULL) - cmd->strvec = cmd_make_descvec (cmd->string, cmd->doc); - - cmd->cmdsize = cmd_cmdsize (cmd->strvec); + if (ntype == VIEW_NODE) + install_element (ENABLE_NODE, cmd); } static const unsigned char itoa64[] = @@ -633,54 +785,6 @@ cmd_node_vector (vector v, enum node_type ntype) return cnode->cmd_vector; } -#if 0 -/* Filter command vector by symbol. This function is not actually used; - * should it be deleted? */ -static int -cmd_filter_by_symbol (char *command, char *symbol) -{ - int i, lim; - - if (strcmp (symbol, "IPV4_ADDRESS") == 0) - { - i = 0; - lim = strlen (command); - while (i < lim) - { - if (! (isdigit ((int) command[i]) || command[i] == '.' || command[i] == '/')) - return 1; - i++; - } - return 0; - } - if (strcmp (symbol, "STRING") == 0) - { - i = 0; - lim = strlen (command); - while (i < lim) - { - if (! (isalpha ((int) command[i]) || command[i] == '_' || command[i] == '-')) - return 1; - i++; - } - return 0; - } - if (strcmp (symbol, "IFNAME") == 0) - { - i = 0; - lim = strlen (command); - while (i < lim) - { - if (! isalnum ((int) command[i])) - return 1; - i++; - } - return 0; - } - return 0; -} -#endif - /* Completion match types. */ enum match_type { @@ -847,9 +951,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; @@ -868,86 +969,7 @@ cmd_ipv6_match (const char *str) if (ret == 1) return exact_match; - while (*str != '\0') - { - switch (state) - { - case STATE_START: - if (*str == ':') - { - if (*(str + 1) != ':' && *(str + 1) != '\0') - return no_match; - colons--; - state = STATE_COLON; - } - else - { - sp = str; - state = STATE_ADDR; - } - - continue; - case STATE_COLON: - colons++; - if (*(str + 1) == ':') - state = STATE_DOUBLE; - else - { - sp = str + 1; - state = STATE_ADDR; - } - break; - case STATE_DOUBLE: - if (double_colon) - return no_match; - - if (*(str + 1) == ':') - return no_match; - else - { - if (*(str + 1) != '\0') - colons++; - sp = str + 1; - state = STATE_ADDR; - } - - double_colon++; - nums++; - break; - case STATE_ADDR: - if (*(str + 1) == ':' || *(str + 1) == '\0') - { - if (str - sp > 3) - return no_match; - - nums++; - state = STATE_COLON; - } - if (*(str + 1) == '.') - state = STATE_DOT; - break; - case STATE_DOT: - state = STATE_ADDR; - break; - default: - break; - } - - if (nums > 8) - return no_match; - - if (colons > 7) - return no_match; - - str++; - } - -#if 0 - if (nums < 11) - return partly_match; -#endif /* 0 */ - - return exact_match; + return no_match; } static enum match_type @@ -1033,7 +1055,12 @@ cmd_ipv6_prefix_match (const char *str) if (*(str + 1) == ':') state = STATE_COLON; else if (*(str + 1) == '.') - state = STATE_DOT; + { + if (colons || double_colon) + state = STATE_DOT; + else + return no_match; + } else if (*(str + 1) == '/') state = STATE_SLASH; } @@ -1070,14 +1097,6 @@ cmd_ipv6_prefix_match (const char *str) if (mask < 0 || mask > 128) return no_match; -/* I don't know why mask < 13 makes command match partly. - Forgive me to make this comments. I Want to set static default route - because of lack of function to originate default in ospf6d; sorry - yasu - if (mask < 13) - return partly_match; -*/ - return exact_match; } @@ -1130,264 +1149,708 @@ 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; + str = token->cmd; - /* 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; + if (filter == FILTER_RELAXED) + if (!word || !strlen(word)) + return partly_match; - descvec = vector_slot (cmd_element->strvec, index); + if (!word) + return no_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; + switch (token->terminal) + { + case TERMINAL_VARARG: + return vararg_match; + + case TERMINAL_RANGE: + if (cmd_range_match(str, word)) + return range_match; + break; + + case TERMINAL_IPV6: + match_type = cmd_ipv6_match(word); + if ((filter == FILTER_RELAXED && match_type != no_match) + || (filter == FILTER_STRICT && match_type == exact_match)) + return ipv6_match; + break; + + case TERMINAL_IPV6_PREFIX: + 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; + break; + + case TERMINAL_IPV4: + match_type = cmd_ipv4_match(word); + if ((filter == FILTER_RELAXED && match_type != no_match) + || (filter == FILTER_STRICT && match_type == exact_match)) + return ipv4_match; + break; + + case TERMINAL_IPV4_PREFIX: + 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; + break; + + case TERMINAL_OPTION: + case TERMINAL_VARIABLE: + return extend_match; + + case TERMINAL_LITERAL: + 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; + break; - matched++; - } - } -#ifdef HAVE_IPV6 - else if (CMD_IPV6 (str)) - { - if (cmd_ipv6_match (command)) - { - if (match_type < ipv6_match) - match_type = ipv6_match; + default: + assert (0); + } - 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 (token->terminal == TERMINAL_OPTION) + 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 (TERMINAL_RECORD (token->terminal)) + { + 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 (token->terminal == TERMINAL_VARARG) + 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 = NULL; + 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 + && TERMINAL_RECORD (word_token->terminal)) + || 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) { case exact_match: - if (!(CMD_OPTION (str) || CMD_VARIABLE (str)) + if (!TERMINAL_RECORD (cmd_token->terminal) && strcmp (command, str) == 0) match++; break; case partly_match: - if (!(CMD_OPTION (str) || CMD_VARIABLE (str)) + if (!TERMINAL_RECORD (cmd_token->terminal) && strncmp (command, str, strlen (command)) == 0) { if (matched && strcmp (matched, str) != 0) @@ -1409,7 +1872,7 @@ is_cmd_ambiguous (char *command, vector v, int index, enum match_type type) break; #ifdef HAVE_IPV6 case ipv6_match: - if (CMD_IPV6 (str)) + if (cmd_token->terminal == TERMINAL_IPV6) match++; break; case ipv6_prefix_match: @@ -1423,7 +1886,7 @@ is_cmd_ambiguous (char *command, vector v, int index, enum match_type type) break; #endif /* HAVE_IPV6 */ case ipv4_match: - if (CMD_IPV4 (str)) + if (cmd_token->terminal == TERMINAL_IPV4) match++; break; case ipv4_prefix_match: @@ -1436,7 +1899,7 @@ is_cmd_ambiguous (char *command, vector v, int index, enum match_type type) } break; case extend_match: - if (CMD_OPTION (str) || CMD_VARIABLE (str)) + if (TERMINAL_RECORD (cmd_token->terminal)) match++; break; case no_match: @@ -1445,18 +1908,19 @@ 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; } /* If src matches dst return dst string, otherwise return NULL */ static const char * -cmd_entry_function (const char *src, const char *dst) +cmd_entry_function (const char *src, struct cmd_token *token) { + const char *dst = token->cmd; + /* Skip variable arguments. */ - if (CMD_OPTION (dst) || CMD_VARIABLE (dst) || CMD_VARARG (dst) || - CMD_IPV4 (dst) || CMD_IPV4_PREFIX (dst) || CMD_RANGE (dst)) + if (TERMINAL_RECORD (token->terminal)) return NULL; /* In case of 'command \t', given src is NULL string. */ @@ -1474,69 +1938,72 @@ cmd_entry_function (const char *src, const char *dst) /* This version will return the dst string always if it is CMD_VARIABLE for '?' key processing */ static const char * -cmd_entry_function_desc (const char *src, const char *dst) +cmd_entry_function_desc (const char *src, struct cmd_token *token) { - if (CMD_VARARG (dst)) - return dst; - - if (CMD_RANGE (dst)) - { - if (cmd_range_match (dst, src)) - return dst; - else - return NULL; - } - -#ifdef HAVE_IPV6 - if (CMD_IPV6 (dst)) - { - if (cmd_ipv6_match (src)) - return dst; - else - return NULL; - } - - if (CMD_IPV6_PREFIX (dst)) - { - if (cmd_ipv6_prefix_match (src)) - return dst; - else - return NULL; - } -#endif /* HAVE_IPV6 */ + const char *dst = token->cmd; - if (CMD_IPV4 (dst)) + switch (token->terminal) { - if (cmd_ipv4_match (src)) - return dst; - else - return NULL; - } - - if (CMD_IPV4_PREFIX (dst)) - { - if (cmd_ipv4_prefix_match (src)) - return dst; - else - return NULL; + case TERMINAL_VARARG: + return dst; + + case TERMINAL_RANGE: + if (cmd_range_match (dst, src)) + return dst; + else + return NULL; + + case TERMINAL_IPV6: + if (cmd_ipv6_match (src)) + return dst; + else + return NULL; + + case TERMINAL_IPV6_PREFIX: + if (cmd_ipv6_prefix_match (src)) + return dst; + else + return NULL; + + case TERMINAL_IPV4: + if (cmd_ipv4_match (src)) + return dst; + else + return NULL; + + case TERMINAL_IPV4_PREFIX: + if (cmd_ipv4_prefix_match (src)) + return dst; + else + return NULL; + + /* Optional or variable commands always match on '?' */ + case TERMINAL_OPTION: + case TERMINAL_VARIABLE: + return dst; + + case TERMINAL_LITERAL: + /* In case of 'command \t', given src is NULL string. */ + if (src == NULL) + return dst; + + if (strncmp (src, dst, strlen (src)) == 0) + return dst; + else + return NULL; + + default: + assert(0); + return NULL; } - - /* Optional or variable commands always match on '?' */ - if (CMD_OPTION (dst) || CMD_VARIABLE (dst)) - return dst; - - /* In case of 'command \t', given src is NULL string. */ - if (src == NULL) - return dst; - - if (strncmp (src, dst, strlen (src)) == 0) - return dst; - else - 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) { @@ -1550,19 +2017,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 @@ -1578,6 +2051,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) @@ -1591,6 +2093,10 @@ 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; + uint32_t command_found = 0; + const char *last_word; /* Set index. */ if (vector_active (vline) == 0) @@ -1598,111 +2104,135 @@ 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); + + if (matches) + cmd_matches_free(&matches); + + ret = cmd_vector_filter(cmd_vector, + FILTER_RELAXED, + vline, i, + &match, + &matches); + + if (ret != CMD_SUCCESS) + { + vector_free (cmd_vector); + vector_free (matchvec); + cmd_matches_free(&matches); + *status = ret; + return NULL; + } + + /* The last match may well be ambigious, so break here */ + if (i == index) + break; + + 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 (matches); i++) + { + if ((cmd_element = vector_slot (cmd_vector, i)) != NULL) + { + unsigned int j; + vector vline_trimmed; - for (j = 0; j < vector_active (cmd_vector); j++) - if ((cmd_element = vector_slot (cmd_vector, j)) != NULL - && (vector_active (cmd_element->strvec))) + command_found++; + 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)) { - 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); - } + if (match != vararg_match) + vector_set(matchvec, &token_cr); } - - vector_set (matchvec, &desc_cr); - vector_free (cmd_vector); - return matchvec; - } - - 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; - } - } + vector_free(vline_trimmed); + } - /* Prepare match vector */ - /* matchvec = vector_init (INIT_MATCHVEC_SIZE); */ + 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; - /* 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); + string = cmd_entry_function_desc(command, token); + if (string && desc_unique_string(matchvec, string)) + vector_set(matchvec, token); + } + } + } + } - /* Make description vector. */ - for (i = 0; i < vector_active (cmd_vector); i++) - if ((cmd_element = vector_slot (cmd_vector, i)) != NULL) - { - vector strvec = cmd_element->strvec; + /* + * We can get into this situation when the command is complete + * but the last part of the command is an optional piece of + * the cli. + */ + last_word = vector_slot(vline, vector_active(vline) - 1); + if (command_found == 0 && (last_word == NULL || !strlen(last_word))) + vector_set(matchvec, &token_cr); - /* 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); - } - } - } - } - } vector_free (cmd_vector); + cmd_matches_free(&matches); if (vector_slot (matchvec, 0) == NULL) { @@ -1712,6 +2242,7 @@ cmd_describe_command_real (vector vline, struct vty *vty, int *status) } *status = CMD_SUCCESS; + cmd_describe_sort(matchvec); return matchvec; } @@ -1782,21 +2313,46 @@ 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) +cmd_complete_command_real (vector vline, struct vty *vty, int *status, int islib) { unsigned int i; 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) { @@ -1807,66 +2363,71 @@ 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; - } - /* - else if (ret == 2) - { - vector_free (cmd_vector); - *status = CMD_ERR_NO_MATCH; - 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; + } + } /* 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; - - descvec = vector_slot (strvec, index); - for (j = 0; j < vector_active (descvec); j++) - if ((desc = vector_slot (descvec, j))) - { - if ((string = - cmd_entry_function (vector_slot (vline, index), - desc->cmd))) - if (cmd_unique_string (matchvec, string)) - vector_set (matchvec, XSTRDUP (MTYPE_TMP, string)); - } - } + unsigned int j; + + for (j = 0; j < vector_active (match_vector); j++) + if ((token = vector_slot (match_vector, j))) + { + string = cmd_entry_function (vector_slot (vline, index), token); + if (string && cmd_unique_string (matchvec, string)) + vector_set (matchvec, (islib != 0 ? + XSTRDUP (MTYPE_TMP, string) : + strdup (string) /* rl freed */)); + } } /* 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) @@ -1906,18 +2467,23 @@ cmd_complete_command_real (vector vline, struct vty *vty, int *status) { char *lcdstr; - lcdstr = XMALLOC (MTYPE_STRVEC, lcd + 1); + lcdstr = (islib != 0 ? + XMALLOC (MTYPE_TMP, lcd + 1) : + malloc(lcd + 1)); memcpy (lcdstr, matchvec->index[0], lcd); lcdstr[lcd] = '\0'; - /* match_str = (char **) &lcdstr; */ - /* Free matchvec. */ for (i = 0; i < vector_active (matchvec); i++) - { - if (vector_slot (matchvec, i)) - XFREE (MTYPE_STRVEC, vector_slot (matchvec, i)); - } + { + if (vector_slot (matchvec, i)) + { + if (islib != 0) + XFREE (MTYPE_TMP, vector_slot (matchvec, i)); + else + free (vector_slot (matchvec, i)); + } + } vector_free (matchvec); /* Make new matchvec. */ @@ -1933,13 +2499,14 @@ 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; } char ** -cmd_complete_command (vector vline, struct vty *vty, int *status) +cmd_complete_command_lib (vector vline, struct vty *vty, int *status, int islib) { char **ret; @@ -1960,15 +2527,20 @@ cmd_complete_command (vector vline, struct vty *vty, int *status) vector_set_index (shifted_vline, index-1, vector_lookup(vline, index)); } - ret = cmd_complete_command_real (shifted_vline, vty, status); + ret = cmd_complete_command_real (shifted_vline, vty, status, islib); vector_free(shifted_vline); vty->node = onode; return ret; } + return cmd_complete_command_real (vline, vty, status, islib); +} - return cmd_complete_command_real (vline, vty, status); +char ** +cmd_complete_command (vector vline, struct vty *vty, int *status) +{ + return cmd_complete_command_lib (vline, vty, status, 0); } /* return parent node */ @@ -1983,6 +2555,9 @@ node_parent ( enum node_type node ) switch (node) { case BGP_VPNV4_NODE: + case BGP_VPNV6_NODE: + case BGP_ENCAP_NODE: + case BGP_ENCAPV6_NODE: case BGP_IPV4_NODE: case BGP_IPV4M_NODE: case BGP_IPV6_NODE: @@ -1992,8 +2567,12 @@ node_parent ( enum node_type node ) case KEYCHAIN_KEY_NODE: ret = KEYCHAIN_NODE; break; + case LINK_PARAMS_NODE: + ret = INTERFACE_NODE; + break; default: ret = CONFIG_NODE; + break; } return ret; @@ -2001,7 +2580,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; @@ -2013,35 +2594,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; @@ -2051,12 +2645,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 @@ -2080,35 +2671,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) @@ -2121,6 +2686,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) { @@ -2144,7 +2724,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; @@ -2152,7 +2732,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; @@ -2163,7 +2743,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) { @@ -2178,151 +2758,89 @@ 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; - } - } + return cmd_execute_command_real(vline, FILTER_STRICT, vty, cmd); +} - /* 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); +/** + * Parse one line of config, walking up the parse tree attempting to find a match + * + * @param vty The vty context in which the command should be executed. + * @param cmd Pointer where the struct cmd_element* of the match command + * will be stored, if any. May be set to NULL if this info is + * not needed. + * @param use_daemon Boolean to control whether or not we match on CMD_SUCCESS_DAEMON + * or not. + * @return The status of the command that has been executed or an error code + * as to why no command could be executed. + */ +int +command_config_read_one_line (struct vty *vty, struct cmd_element **cmd, int use_daemon) +{ + vector vline; + int saved_node; + int ret; - if (match == vararg_match || index >= cmd_element->cmdsize) - { - matched_element = cmd_element; - matched_count++; - } - else - incomplete_count++; - } + vline = cmd_make_strvec (vty->buf); - /* Finish of using cmd_vector. */ - vector_free (cmd_vector); + /* In case of comment line */ + if (vline == NULL) + return CMD_SUCCESS; - /* 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; - } + /* Execute configuration command : this is strict match */ + ret = cmd_execute_command_strict (vline, vty, cmd); - if (matched_count > 1) - return CMD_ERR_AMBIGUOUS; + saved_node = vty->node; - /* Argument treatment */ - varflag = 0; - argc = 0; + while (!(use_daemon && ret == CMD_SUCCESS_DAEMON) && + ret != CMD_SUCCESS && ret != CMD_WARNING && + ret != CMD_ERR_NOTHING_TODO && vty->node != CONFIG_NODE) { + vty->node = node_parent(vty->node); + ret = cmd_execute_command_strict (vline, vty, NULL); + } - for (i = 0; i < vector_active (vline); i++) + // If climbing the tree did not work then ignore the command and + // stay at the same node + if (!(use_daemon && ret == CMD_SUCCESS_DAEMON) && + ret != CMD_SUCCESS && ret != CMD_WARNING && + ret != CMD_ERR_NOTHING_TODO) { - 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; + vty->node = saved_node; } - /* For vtysh execution. */ - if (cmd) - *cmd = matched_element; - - if (matched_element->daemon) - return CMD_SUCCESS_DAEMON; + cmd_free_strvec (vline); - /* Now execute matched command */ - return (*matched_element->func) (matched_element, vty, argc, argv); + return ret; } /* 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; - vector vline; + *line_num = 0; - while (fgets (vty->buf, VTY_BUFSIZ, fp)) + while (fgets (vty->buf, vty->max, fp)) { - vline = cmd_make_strvec (vty->buf); - - /* In case of comment line */ - if (vline == NULL) - continue; - /* Execute configuration command : this is strict match */ - ret = cmd_execute_command_strict (vline, vty, NULL); - - /* Try again with setting node to CONFIG_NODE */ - while (ret != CMD_SUCCESS && ret != CMD_WARNING - && ret != CMD_ERR_NOTHING_TODO && vty->node != CONFIG_NODE) - { - vty->node = node_parent(vty->node); - ret = cmd_execute_command_strict (vline, vty, NULL); - } + ++(*line_num); - cmd_free_strvec (vline); + ret = command_config_read_one_line (vty, NULL, 0); if (ret != CMD_SUCCESS && ret != CMD_WARNING && ret != CMD_ERR_NOTHING_TODO) @@ -2407,12 +2925,16 @@ DEFUN (config_exit, case KEYCHAIN_NODE: case MASC_NODE: case RMAP_NODE: + case PIM_NODE: case VTY_NODE: vty->node = CONFIG_NODE; break; - case BGP_VPNV4_NODE: case BGP_IPV4_NODE: case BGP_IPV4M_NODE: + case BGP_VPNV4_NODE: + case BGP_VPNV6_NODE: + case BGP_ENCAP_NODE: + case BGP_ENCAPV6_NODE: case BGP_IPV6_NODE: case BGP_IPV6M_NODE: vty->node = BGP_NODE; @@ -2420,6 +2942,9 @@ DEFUN (config_exit, case KEYCHAIN_KEY_NODE: vty->node = KEYCHAIN_NODE; break; + case LINK_PARAMS_NODE: + vty->node = INTERFACE_NODE; + break; default: break; } @@ -2452,7 +2977,10 @@ DEFUN (config_end, case RIPNG_NODE: case BABEL_NODE: case BGP_NODE: + case BGP_ENCAP_NODE: + case BGP_ENCAPV6_NODE: case BGP_VPNV4_NODE: + case BGP_VPNV6_NODE: case BGP_IPV4_NODE: case BGP_IPV4M_NODE: case BGP_IPV6_NODE: @@ -2464,7 +2992,9 @@ DEFUN (config_end, case KEYCHAIN_NODE: case KEYCHAIN_KEY_NODE: case MASC_NODE: + case PIM_NODE: case VTY_NODE: + case LINK_PARAMS_NODE: vty_config_unlock (vty); vty->node = ENABLE_NODE; break; @@ -2483,7 +3013,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; } @@ -2578,7 +3110,7 @@ DEFUN (config_write_file, /* Make vty for configuration file. */ file_vty = vty_new (); - file_vty->fd = fd; + file_vty->wfd = fd; file_vty->type = VTY_FILE; /* Config file header print. */ @@ -3506,6 +4038,37 @@ DEFUN (no_banner_motd, return CMD_SUCCESS; } +DEFUN (show_commandtree, + show_commandtree_cmd, + "show commandtree", + NO_STR + "Show command tree\n") +{ + /* TBD */ + vector cmd_vector; + unsigned int i; + + vty_out (vty, "Current node id: %d%s", vty->node, VTY_NEWLINE); + + /* vector of all commands installed at this node */ + cmd_vector = vector_copy (cmd_node_vector (cmdvec, vty->node)); + + /* loop over all commands at this node */ + for (i = 0; i < vector_active(cmd_vector); ++i) + { + struct cmd_element *cmd_element; + + /* A cmd_element (seems to be) is an individual command */ + if ((cmd_element = vector_slot (cmd_vector, i)) == NULL) + continue; + + vty_out (vty, " %s%s", cmd_element->string, VTY_NEWLINE); + } + + vector_free (cmd_vector); + return CMD_SUCCESS; +} + /* Set config filename. Called from vty.c */ void host_config_set (char *filename) @@ -3515,15 +4078,36 @@ host_config_set (char *filename) host.config = XSTRDUP (MTYPE_HOST, filename); } -void -install_default (enum node_type node) +const char * +host_config_get (void) +{ + return host.config; +} + +static void +install_default_basic (enum node_type node) { install_element (node, &config_exit_cmd); install_element (node, &config_quit_cmd); - install_element (node, &config_end_cmd); install_element (node, &config_help_cmd); install_element (node, &config_list_cmd); +} +/* Install common/default commands for a privileged node */ +void +install_default (enum node_type node) +{ + /* VIEW_NODE is inited below, via install_default_basic, and + install_element's of commands to VIEW_NODE automatically are + also installed to ENABLE_NODE. + + For all other nodes, we must ensure install_default_basic is + also called/ + */ + if (node != VIEW_NODE && node != ENABLE_NODE) + install_default_basic (node); + + install_element (node, &config_end_cmd); install_element (node, &config_write_terminal_cmd); install_element (node, &config_write_file_cmd); install_element (node, &config_write_memory_cmd); @@ -3535,13 +4119,15 @@ 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.terminal = TERMINAL_LITERAL; + token_cr.cmd = command_cr; + token_cr.desc = XSTRDUP(MTYPE_CMD_TOKENS, ""); /* Allocate initial top vector of commands. */ cmdvec = vector_init (VECTOR_MIN_SIZE); - + /* Default host value settings. */ host.name = NULL; host.password = NULL; @@ -3564,23 +4150,19 @@ cmd_init (int terminal) install_element (VIEW_NODE, &show_version_cmd); if (terminal) { - install_element (VIEW_NODE, &config_list_cmd); - install_element (VIEW_NODE, &config_exit_cmd); - install_element (VIEW_NODE, &config_quit_cmd); - install_element (VIEW_NODE, &config_help_cmd); + install_default_basic (VIEW_NODE); + install_element (VIEW_NODE, &config_enable_cmd); install_element (VIEW_NODE, &config_terminal_length_cmd); install_element (VIEW_NODE, &config_terminal_no_length_cmd); install_element (VIEW_NODE, &show_logging_cmd); + install_element (VIEW_NODE, &show_commandtree_cmd); install_element (VIEW_NODE, &echo_cmd); - install_element (RESTRICTED_NODE, &config_list_cmd); - install_element (RESTRICTED_NODE, &config_exit_cmd); - install_element (RESTRICTED_NODE, &config_quit_cmd); - install_element (RESTRICTED_NODE, &config_help_cmd); install_element (RESTRICTED_NODE, &config_enable_cmd); install_element (RESTRICTED_NODE, &config_terminal_length_cmd); install_element (RESTRICTED_NODE, &config_terminal_no_length_cmd); + install_element (RESTRICTED_NODE, &show_commandtree_cmd); install_element (RESTRICTED_NODE, &echo_cmd); } @@ -3592,14 +4174,9 @@ cmd_init (int terminal) install_element (ENABLE_NODE, ©_runningconfig_startupconfig_cmd); } install_element (ENABLE_NODE, &show_startup_config_cmd); - install_element (ENABLE_NODE, &show_version_cmd); if (terminal) { - install_element (ENABLE_NODE, &config_terminal_length_cmd); - install_element (ENABLE_NODE, &config_terminal_no_length_cmd); - install_element (ENABLE_NODE, &show_logging_cmd); - install_element (ENABLE_NODE, &echo_cmd); install_element (ENABLE_NODE, &config_logmsg_cmd); install_default (CONFIG_NODE); @@ -3648,24 +4225,70 @@ cmd_init (int terminal) install_element (CONFIG_NODE, &no_service_terminal_length_cmd); install_element (VIEW_NODE, &show_thread_cpu_cmd); - install_element (ENABLE_NODE, &show_thread_cpu_cmd); install_element (RESTRICTED_NODE, &show_thread_cpu_cmd); install_element (ENABLE_NODE, &clear_thread_cpu_cmd); install_element (VIEW_NODE, &show_work_queues_cmd); - install_element (ENABLE_NODE, &show_work_queues_cmd); } - srand(time(NULL)); + install_element (CONFIG_NODE, &show_commandtree_cmd); + srandom(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) { @@ -3675,32 +4298,13 @@ 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); + hash_clean (cmd_node->cmd_hash, NULL); + hash_free (cmd_node->cmd_hash); + cmd_node->cmd_hash = NULL; } vector_free (cmdvec); @@ -3708,9 +4312,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 2d708d8ec..cc5dd0891 100644 --- a/lib/command.h +++ b/lib/command.h @@ -26,6 +26,7 @@ #include "vector.h" #include "vty.h" #include "lib/route_types.h" +#include "hash.h" /* Host configuration variable */ struct host @@ -68,6 +69,7 @@ enum node_type AUTH_ENABLE_NODE, /* Authentication mode for change enable. */ ENABLE_NODE, /* Enable node. */ CONFIG_NODE, /* Config node. Default mode of config file. */ + VRF_NODE, /* VRF node. */ SERVICE_NODE, /* Service node. */ DEBUG_NODE, /* Debug node. */ AAA_NODE, /* AAA node. */ @@ -81,13 +83,17 @@ enum node_type BABEL_NODE, /* Babel protocol mode node. */ BGP_NODE, /* BGP protocol mode which includes BGP4+ */ BGP_VPNV4_NODE, /* BGP MPLS-VPN PE exchange. */ + BGP_VPNV6_NODE, /* BGP MPLS-VPN PE exchange. */ BGP_IPV4_NODE, /* BGP IPv4 unicast address family. */ BGP_IPV4M_NODE, /* BGP IPv4 multicast address family. */ BGP_IPV6_NODE, /* BGP IPv6 address family */ BGP_IPV6M_NODE, /* BGP IPv6 multicast address family. */ + BGP_ENCAP_NODE, /* BGP ENCAP SAFI */ + BGP_ENCAPV6_NODE, /* BGP ENCAP SAFI */ 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. */ @@ -103,6 +109,8 @@ enum node_type FORWARDING_NODE, /* IP forwarding node. */ PROTOCOL_NODE, /* protocol filtering node */ VTY_NODE, /* Vty node. */ + LINK_PARAMS_NODE, /* Link-parameters node */ + ZEBRA_IF_DEFAULTS_NODE, /* If defaults dummy node */ }; /* Node which has some commands and prompt string and configuration @@ -122,7 +130,10 @@ struct cmd_node int (*func) (struct vty *); /* Vector of this node's command list. */ - vector cmd_vector; + vector cmd_vector; + + /* Hashed index of command node list, for de-dupping primarily */ + struct hash *cmd_hash; }; enum @@ -138,18 +149,50 @@ 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, +}; + +enum cmd_terminal_type +{ + _TERMINAL_BUG = 0, + TERMINAL_LITERAL, + TERMINAL_OPTION, + TERMINAL_VARIABLE, + TERMINAL_VARARG, + TERMINAL_RANGE, + TERMINAL_IPV4, + TERMINAL_IPV4_PREFIX, + TERMINAL_IPV6, + TERMINAL_IPV6_PREFIX, +}; + +/* argument to be recorded on argv[] if it's not a literal */ +#define TERMINAL_RECORD(t) ((t) >= TERMINAL_OPTION) + /* Command description structure. */ -struct desc +struct cmd_token { + enum cmd_token_type type; + enum cmd_terminal_type terminal; + + /* 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 +235,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) \ @@ -259,16 +465,16 @@ struct desc #endif /* VTYSH_EXTRACT_PL */ -/* Some macroes */ -#define CMD_OPTION(S) ((S[0]) == '[') -#define CMD_VARIABLE(S) (((S[0]) >= 'A' && (S[0]) <= 'Z') || ((S[0]) == '<')) -#define CMD_VARARG(S) ((S[0]) == '.') -#define CMD_RANGE(S) ((S[0] == '<')) +/* + * Sometimes #defines create maximum values that + * need to have strings created from them that + * allow the parser to match against them. + * These macros allow that. + */ +#define CMD_CREATE_STR(s) CMD_CREATE_STR_HELPER(s) +#define CMD_CREATE_STR_HELPER(s) #s +#define CMD_RANGE_STR(a,s) "<" CMD_CREATE_STR(a) "-" CMD_CREATE_STR(s) ">" -#define CMD_IPV4(S) ((strcmp ((S), "A.B.C.D") == 0)) -#define CMD_IPV4_PREFIX(S) ((strcmp ((S), "A.B.C.D/M") == 0)) -#define CMD_IPV6(S) ((strcmp ((S), "X:X::X:X") == 0)) -#define CMD_IPV6_PREFIX(S) ((strcmp ((S), "X:X::X:X/M") == 0)) /* Common descriptions. */ #define SHOW_STR "Show running system information\n" @@ -279,6 +485,10 @@ struct desc #define CLEAR_STR "Reset functions\n" #define RIP_STR "RIP information\n" #define BGP_STR "BGP information\n" +#define BGP_SOFT_STR "Soft reconfig inbound and outbound updates\n" +#define BGP_SOFT_IN_STR "Send route-refresh unless using 'soft-reconfiguration inbound'\n" +#define BGP_SOFT_OUT_STR "Resend all outbound updates\n" +#define BGP_SOFT_RSCLIENT_RIB_STR "Soft reconfig for rsclient RIB\n" #define OSPF_STR "OSPF information\n" #define NEIGHBOR_STR "Specify neighbor router\n" #define DEBUG_STR "Debugging functions (see also 'undebug')\n" @@ -305,6 +515,10 @@ struct desc "(neighbor|interface|area|lsa|zebra|config|dbex|spf|route|lsdb|redistribute|hook|asbr|prefix|abr)" #define ISIS_STR "IS-IS information\n" #define AREA_TAG_STR "[area tag]\n" +#define MPLS_TE_STR "MPLS-TE specific commands\n" +#define LINK_PARAMS_STR "Configure interface link parameters\n" +#define OSPF_RI_STR "OSPF Router Information specific commands\n" +#define PCE_STR "PCE Router Information specific commands\n" #define CONF_BACKUP_EXT ".sav" @@ -330,7 +544,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 @@ -341,12 +554,13 @@ extern vector cmd_make_strvec (const char *); 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 char **cmd_complete_command_lib (vector, struct vty *, int *status, int islib); extern const char *cmd_prompt (enum node_type); -extern int config_from_file (struct vty *, FILE *); +extern int command_config_read_one_line (struct vty *vty, struct cmd_element **, int use_config_node); +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); @@ -356,7 +570,7 @@ extern struct cmd_element config_exit_cmd; extern struct cmd_element config_quit_cmd; extern struct cmd_element config_help_cmd; extern struct cmd_element config_list_cmd; -extern char *host_config_file (void); +extern const char *host_config_get (void); extern void host_config_set (char *); extern void print_version (const char *); diff --git a/lib/distribute.c b/lib/distribute.c index 8d6f63774..583befb36 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) { @@ -45,22 +45,33 @@ distribute_new (void) static void distribute_free (struct distribute *dist) { + int i = 0; if (dist->ifname) XFREE (MTYPE_DISTRIBUTE_IFNAME, dist->ifname); - if (dist->list[DISTRIBUTE_IN]) - free (dist->list[DISTRIBUTE_IN]); - if (dist->list[DISTRIBUTE_OUT]) - free (dist->list[DISTRIBUTE_OUT]); + for (i=0; i < DISTRIBUTE_MAX; i++) + if (dist->list[i]) + free(dist->list[i]); - if (dist->prefix[DISTRIBUTE_IN]) - free (dist->prefix[DISTRIBUTE_IN]); - if (dist->prefix[DISTRIBUTE_OUT]) - free (dist->prefix[DISTRIBUTE_OUT]); + for (i=0; i < DISTRIBUTE_MAX; i++) + if (dist->prefix[i]) + free(dist->prefix[i]); XFREE (MTYPE_DISTRIBUTE, dist); } +static void +distribute_free_if_empty(struct distribute *dist) +{ + int i; + for (i=0; i < DISTRIBUTE_MAX; i++) + if (dist->list[i] != NULL || dist->prefix[i] != NULL) + return; + + hash_release (disthash, dist); + distribute_free (dist); +} + /* Lookup interface's distribute list. */ struct distribute * distribute_lookup (const char *ifname) @@ -133,39 +144,30 @@ 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, +distribute_list_set (const char *ifname, enum distribute_type type, const char *alist_name) { struct distribute *dist; dist = distribute_get (ifname); - if (type == DISTRIBUTE_IN) - { - if (dist->list[DISTRIBUTE_IN]) - free (dist->list[DISTRIBUTE_IN]); - dist->list[DISTRIBUTE_IN] = strdup (alist_name); - } - if (type == DISTRIBUTE_OUT) - { - if (dist->list[DISTRIBUTE_OUT]) - free (dist->list[DISTRIBUTE_OUT]); - dist->list[DISTRIBUTE_OUT] = strdup (alist_name); - } + if (dist->list[type]) + free (dist->list[type]); + dist->list[type] = strdup (alist_name); /* Apply this distribute-list to the interface. */ (*distribute_add_hook) (dist); - + return dist; } /* Unset distribute-list. If matched distribute-list exist then return 1. */ static int -distribute_list_unset (const char *ifname, enum distribute_type type, +distribute_list_unset (const char *ifname, enum distribute_type type, const char *alist_name) { struct distribute *dist; @@ -174,41 +176,19 @@ distribute_list_unset (const char *ifname, enum distribute_type type, if (!dist) return 0; - if (type == DISTRIBUTE_IN) - { - if (!dist->list[DISTRIBUTE_IN]) - return 0; - if (strcmp (dist->list[DISTRIBUTE_IN], alist_name) != 0) - return 0; - - free (dist->list[DISTRIBUTE_IN]); - dist->list[DISTRIBUTE_IN] = NULL; - } - - if (type == DISTRIBUTE_OUT) - { - if (!dist->list[DISTRIBUTE_OUT]) - return 0; - if (strcmp (dist->list[DISTRIBUTE_OUT], alist_name) != 0) - return 0; + if (!dist->list[type]) + return 0; + if (strcmp (dist->list[type], alist_name) != 0) + return 0; - free (dist->list[DISTRIBUTE_OUT]); - dist->list[DISTRIBUTE_OUT] = NULL; - } + free (dist->list[type]); + dist->list[type] = NULL; /* Apply this distribute-list to the interface. */ (*distribute_delete_hook) (dist); - /* If both out and in is NULL then free distribute list. */ - if (dist->list[DISTRIBUTE_IN] == NULL && - dist->list[DISTRIBUTE_OUT] == NULL && - dist->prefix[DISTRIBUTE_IN] == NULL && - dist->prefix[DISTRIBUTE_OUT] == NULL) - { - hash_release (disthash, dist); - distribute_free (dist); - } - + /* If all dist are NULL, then free distribute list. */ + distribute_free_if_empty(dist); return 1; } @@ -221,18 +201,9 @@ distribute_list_prefix_set (const char *ifname, enum distribute_type type, dist = distribute_get (ifname); - if (type == DISTRIBUTE_IN) - { - if (dist->prefix[DISTRIBUTE_IN]) - free (dist->prefix[DISTRIBUTE_IN]); - dist->prefix[DISTRIBUTE_IN] = strdup (plist_name); - } - if (type == DISTRIBUTE_OUT) - { - if (dist->prefix[DISTRIBUTE_OUT]) - free (dist->prefix[DISTRIBUTE_OUT]); - dist->prefix[DISTRIBUTE_OUT] = strdup (plist_name); - } + if (dist->prefix[type]) + free (dist->prefix[type]); + dist->prefix[type] = strdup (plist_name); /* Apply this distribute-list to the interface. */ (*distribute_add_hook) (dist); @@ -252,41 +223,19 @@ distribute_list_prefix_unset (const char *ifname, enum distribute_type type, if (!dist) return 0; - if (type == DISTRIBUTE_IN) - { - if (!dist->prefix[DISTRIBUTE_IN]) - return 0; - if (strcmp (dist->prefix[DISTRIBUTE_IN], plist_name) != 0) - return 0; - - free (dist->prefix[DISTRIBUTE_IN]); - dist->prefix[DISTRIBUTE_IN] = NULL; - } - - if (type == DISTRIBUTE_OUT) - { - if (!dist->prefix[DISTRIBUTE_OUT]) - return 0; - if (strcmp (dist->prefix[DISTRIBUTE_OUT], plist_name) != 0) - return 0; + if (!dist->prefix[type]) + return 0; + if (strcmp (dist->prefix[type], plist_name) != 0) + return 0; - free (dist->prefix[DISTRIBUTE_OUT]); - dist->prefix[DISTRIBUTE_OUT] = NULL; - } + free (dist->prefix[type]); + dist->prefix[type] = NULL; /* Apply this distribute-list to the interface. */ (*distribute_delete_hook) (dist); - /* If both out and in is NULL then free distribute list. */ - if (dist->list[DISTRIBUTE_IN] == NULL && - dist->list[DISTRIBUTE_OUT] == NULL && - dist->prefix[DISTRIBUTE_IN] == NULL && - dist->prefix[DISTRIBUTE_OUT] == NULL) - { - hash_release (disthash, dist); - distribute_free (dist); - } - + /* If all dist are NULL, then free distribute list. */ + distribute_free_if_empty(dist); return 1; } @@ -302,9 +251,9 @@ DEFUN (distribute_list_all, /* Check of distribute list type. */ if (strncmp (argv[1], "i", 1) == 0) - type = DISTRIBUTE_IN; + type = DISTRIBUTE_V4_IN; else if (strncmp (argv[1], "o", 1) == 0) - type = DISTRIBUTE_OUT; + type = DISTRIBUTE_V4_OUT; else { vty_out (vty, "distribute list direction must be [in|out]%s", @@ -318,8 +267,36 @@ DEFUN (distribute_list_all, return CMD_SUCCESS; } -ALIAS (distribute_list_all, +DEFUN (ipv6_distribute_list_all, ipv6_distribute_list_all_cmd, + "ipv6 distribute-list WORD (in|out)", + "Filter networks in routing updates\n" + "Access-list name\n" + "Filter incoming routing updates\n" + "Filter outgoing routing updates\n") +{ + enum distribute_type type; + + /* Check of distribute list type. */ + if (strncmp (argv[1], "i", 1) == 0) + type = DISTRIBUTE_V6_IN; + else if (strncmp (argv[1], "o", 1) == 0) + type = DISTRIBUTE_V6_OUT; + else + { + vty_out (vty, "distribute list direction must be [in|out]%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + /* Get interface name corresponding distribute list. */ + distribute_list_set (NULL, type, argv[0]); + + return CMD_SUCCESS; +} + +ALIAS (ipv6_distribute_list_all, + ipv6_as_v4_distribute_list_all_cmd, "distribute-list WORD (in|out)", "Filter networks in routing updates\n" "Access-list name\n" @@ -340,9 +317,9 @@ DEFUN (no_distribute_list_all, /* Check of distribute list type. */ if (strncmp (argv[1], "i", 1) == 0) - type = DISTRIBUTE_IN; + type = DISTRIBUTE_V4_IN; else if (strncmp (argv[1], "o", 1) == 0) - type = DISTRIBUTE_OUT; + type = DISTRIBUTE_V4_OUT; else { vty_out (vty, "distribute list direction must be [in|out]%s", @@ -359,8 +336,41 @@ DEFUN (no_distribute_list_all, return CMD_SUCCESS; } -ALIAS (no_distribute_list_all, +DEFUN (no_ipv6_distribute_list_all, no_ipv6_distribute_list_all_cmd, + "no ipv6 distribute-list WORD (in|out)", + NO_STR + "Filter networks in routing updates\n" + "Access-list name\n" + "Filter incoming routing updates\n" + "Filter outgoing routing updates\n") +{ + int ret; + enum distribute_type type; + + /* Check of distribute list type. */ + if (strncmp (argv[1], "i", 1) == 0) + type = DISTRIBUTE_V6_IN; + else if (strncmp (argv[1], "o", 1) == 0) + type = DISTRIBUTE_V6_OUT; + else + { + vty_out (vty, "distribute list direction must be [in|out]%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + ret = distribute_list_unset (NULL, type, argv[0]); + if (! ret) + { + vty_out (vty, "distribute list doesn't exist%s", VTY_NEWLINE); + return CMD_WARNING; + } + return CMD_SUCCESS; +} + +ALIAS (no_ipv6_distribute_list_all, + no_ipv6_as_v4_distribute_list_all_cmd, "no distribute-list WORD (in|out)", NO_STR "Filter networks in routing updates\n" @@ -381,9 +391,9 @@ DEFUN (distribute_list, /* Check of distribute list type. */ if (strncmp (argv[1], "i", 1) == 0) - type = DISTRIBUTE_IN; + type = DISTRIBUTE_V4_IN; else if (strncmp (argv[1], "o", 1) == 0) - type = DISTRIBUTE_OUT; + type = DISTRIBUTE_V4_OUT; else { vty_out (vty, "distribute list direction must be [in|out]%s", VTY_NEWLINE); @@ -394,10 +404,38 @@ DEFUN (distribute_list, distribute_list_set (argv[2], type, argv[0]); return CMD_SUCCESS; -} +} -ALIAS (distribute_list, +DEFUN (ipv6_distribute_list, ipv6_distribute_list_cmd, + "ipv6 distribute-list WORD (in|out) WORD", + "Filter networks in routing updates\n" + "Access-list name\n" + "Filter incoming routing updates\n" + "Filter outgoing routing updates\n" + "Interface name\n") +{ + enum distribute_type type; + + /* Check of distribute list type. */ + if (strncmp (argv[1], "i", 1) == 0) + type = DISTRIBUTE_V6_IN; + else if (strncmp (argv[1], "o", 1) == 0) + type = DISTRIBUTE_V6_OUT; + else + { + vty_out (vty, "distribute list direction must be [in|out]%s", VTY_NEWLINE); + return CMD_WARNING; + } + + /* Get interface name corresponding distribute list. */ + distribute_list_set (argv[2], type, argv[0]); + + return CMD_SUCCESS; +} + +ALIAS (ipv6_distribute_list, + ipv6_as_v4_distribute_list_cmd, "distribute-list WORD (in|out) WORD", "Filter networks in routing updates\n" "Access-list name\n" @@ -419,9 +457,9 @@ DEFUN (no_distribute_list, no_distribute_list_cmd, /* Check of distribute list type. */ if (strncmp (argv[1], "i", 1) == 0) - type = DISTRIBUTE_IN; + type = DISTRIBUTE_V4_IN; else if (strncmp (argv[1], "o", 1) == 0) - type = DISTRIBUTE_OUT; + type = DISTRIBUTE_V4_OUT; else { vty_out (vty, "distribute list direction must be [in|out]%s", VTY_NEWLINE); @@ -435,9 +473,43 @@ DEFUN (no_distribute_list, no_distribute_list_cmd, return CMD_WARNING; } return CMD_SUCCESS; -} +} + +DEFUN (no_ipv6_distribute_list, + no_ipv6_distribute_list_cmd, + "no ipv6 distribute-list WORD (in|out) WORD", + NO_STR + "Filter networks in routing updates\n" + "Access-list name\n" + "Filter incoming routing updates\n" + "Filter outgoing routing updates\n" + "Interface name\n") +{ + int ret; + enum distribute_type type; + + /* Check of distribute list type. */ + if (strncmp (argv[1], "i", 1) == 0) + type = DISTRIBUTE_V6_IN; + else if (strncmp (argv[1], "o", 1) == 0) + type = DISTRIBUTE_V6_OUT; + else + { + vty_out (vty, "distribute list direction must be [in|out]%s", VTY_NEWLINE); + return CMD_WARNING; + } + + ret = distribute_list_unset (argv[2], type, argv[0]); + if (! ret) + { + vty_out (vty, "distribute list doesn't exist%s", VTY_NEWLINE); + return CMD_WARNING; + } + return CMD_SUCCESS; +} -ALIAS (no_distribute_list, no_ipv6_distribute_list_cmd, +ALIAS (no_ipv6_distribute_list, + no_ipv6_as_v4_distribute_list_cmd, "no distribute-list WORD (in|out) WORD", NO_STR "Filter networks in routing updates\n" @@ -459,12 +531,12 @@ DEFUN (distribute_list_prefix_all, /* Check of distribute list type. */ if (strncmp (argv[1], "i", 1) == 0) - type = DISTRIBUTE_IN; + type = DISTRIBUTE_V4_IN; else if (strncmp (argv[1], "o", 1) == 0) - type = DISTRIBUTE_OUT; + type = DISTRIBUTE_V4_OUT; else { - vty_out (vty, "distribute list direction must be [in|out]%s", + vty_out (vty, "distribute list direction must be [in|out]%s", VTY_NEWLINE); return CMD_WARNING; } @@ -473,10 +545,39 @@ DEFUN (distribute_list_prefix_all, distribute_list_prefix_set (NULL, type, argv[0]); return CMD_SUCCESS; -} +} -ALIAS (distribute_list_prefix_all, +DEFUN (ipv6_distribute_list_prefix_all, ipv6_distribute_list_prefix_all_cmd, + "ipv6 distribute-list prefix WORD (in|out)", + "Filter networks in routing updates\n" + "Filter prefixes in routing updates\n" + "Name of an IP prefix-list\n" + "Filter incoming routing updates\n" + "Filter outgoing routing updates\n") +{ + enum distribute_type type; + + /* Check of distribute list type. */ + if (strncmp (argv[1], "i", 1) == 0) + type = DISTRIBUTE_V6_IN; + else if (strncmp (argv[1], "o", 1) == 0) + type = DISTRIBUTE_V6_OUT; + else + { + vty_out (vty, "distribute list direction must be [in|out]%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + /* Get interface name corresponding distribute list. */ + distribute_list_prefix_set (NULL, type, argv[0]); + + return CMD_SUCCESS; +} + +ALIAS (ipv6_distribute_list_prefix_all, + ipv6_as_v4_distribute_list_prefix_all_cmd, "distribute-list prefix WORD (in|out)", "Filter networks in routing updates\n" "Filter prefixes in routing updates\n" @@ -499,12 +600,12 @@ DEFUN (no_distribute_list_prefix_all, /* Check of distribute list type. */ if (strncmp (argv[1], "i", 1) == 0) - type = DISTRIBUTE_IN; + type = DISTRIBUTE_V4_IN; else if (strncmp (argv[1], "o", 1) == 0) - type = DISTRIBUTE_OUT; + type = DISTRIBUTE_V4_OUT; else { - vty_out (vty, "distribute list direction must be [in|out]%s", + vty_out (vty, "distribute list direction must be [in|out]%s", VTY_NEWLINE); return CMD_WARNING; } @@ -516,10 +617,44 @@ DEFUN (no_distribute_list_prefix_all, return CMD_WARNING; } return CMD_SUCCESS; -} +} -ALIAS (no_distribute_list_prefix_all, +DEFUN (no_ipv6_distribute_list_prefix_all, no_ipv6_distribute_list_prefix_all_cmd, + "no ipv6 distribute-list prefix WORD (in|out)", + NO_STR + "Filter networks in routing updates\n" + "Filter prefixes in routing updates\n" + "Name of an IP prefix-list\n" + "Filter incoming routing updates\n" + "Filter outgoing routing updates\n") +{ + int ret; + enum distribute_type type; + + /* Check of distribute list type. */ + if (strncmp (argv[1], "i", 1) == 0) + type = DISTRIBUTE_V6_IN; + else if (strncmp (argv[1], "o", 1) == 0) + type = DISTRIBUTE_V6_OUT; + else + { + vty_out (vty, "distribute list direction must be [in|out]%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + ret = distribute_list_prefix_unset (NULL, type, argv[0]); + if (! ret) + { + vty_out (vty, "distribute list doesn't exist%s", VTY_NEWLINE); + return CMD_WARNING; + } + return CMD_SUCCESS; +} + +ALIAS (no_ipv6_distribute_list_prefix_all, + no_ipv6_as_v4_distribute_list_prefix_all_cmd, "no distribute-list prefix WORD (in|out)", NO_STR "Filter networks in routing updates\n" @@ -541,12 +676,12 @@ DEFUN (distribute_list_prefix, distribute_list_prefix_cmd, /* Check of distribute list type. */ if (strncmp (argv[1], "i", 1) == 0) - type = DISTRIBUTE_IN; + type = DISTRIBUTE_V4_IN; else if (strncmp (argv[1], "o", 1) == 0) - type = DISTRIBUTE_OUT; + type = DISTRIBUTE_V4_OUT; else { - vty_out (vty, "distribute list direction must be [in|out]%s", + vty_out (vty, "distribute list direction must be [in|out]%s", VTY_NEWLINE); return CMD_WARNING; } @@ -555,9 +690,40 @@ DEFUN (distribute_list_prefix, distribute_list_prefix_cmd, distribute_list_prefix_set (argv[2], type, argv[0]); return CMD_SUCCESS; -} +} + +DEFUN (ipv6_distribute_list_prefix, + ipv6_distribute_list_prefix_cmd, + "ipv6 distribute-list prefix WORD (in|out) WORD", + "Filter networks in routing updates\n" + "Filter prefixes in routing updates\n" + "Name of an IP prefix-list\n" + "Filter incoming routing updates\n" + "Filter outgoing routing updates\n" + "Interface name\n") +{ + enum distribute_type type; + + /* Check of distribute list type. */ + if (strncmp (argv[1], "i", 1) == 0) + type = DISTRIBUTE_V6_IN; + else if (strncmp (argv[1], "o", 1) == 0) + type = DISTRIBUTE_V6_OUT; + else + { + vty_out (vty, "distribute list direction must be [in|out]%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + /* Get interface name corresponding distribute list. */ + distribute_list_prefix_set (argv[2], type, argv[0]); + + return CMD_SUCCESS; +} -ALIAS (distribute_list_prefix, ipv6_distribute_list_prefix_cmd, +ALIAS (ipv6_distribute_list_prefix, + ipv6_as_v4_distribute_list_prefix_cmd, "distribute-list prefix WORD (in|out) WORD", "Filter networks in routing updates\n" "Filter prefixes in routing updates\n" @@ -581,12 +747,12 @@ DEFUN (no_distribute_list_prefix, no_distribute_list_prefix_cmd, /* Check of distribute list type. */ if (strncmp (argv[1], "i", 1) == 0) - type = DISTRIBUTE_IN; + type = DISTRIBUTE_V4_IN; else if (strncmp (argv[1], "o", 1) == 0) - type = DISTRIBUTE_OUT; + type = DISTRIBUTE_V4_OUT; else { - vty_out (vty, "distribute list direction must be [in|out]%s", + vty_out (vty, "distribute list direction must be [in|out]%s", VTY_NEWLINE); return CMD_WARNING; } @@ -598,9 +764,45 @@ DEFUN (no_distribute_list_prefix, no_distribute_list_prefix_cmd, return CMD_WARNING; } return CMD_SUCCESS; -} +} + +DEFUN (no_ipv6_distribute_list_prefix, + no_ipv6_distribute_list_prefix_cmd, + "no ipv6 distribute-list prefix WORD (in|out) WORD", + NO_STR + "Filter networks in routing updates\n" + "Filter prefixes in routing updates\n" + "Name of an IP prefix-list\n" + "Filter incoming routing updates\n" + "Filter outgoing routing updates\n" + "Interface name\n") +{ + int ret; + enum distribute_type type; + + /* Check of distribute list type. */ + if (strncmp (argv[1], "i", 1) == 0) + type = DISTRIBUTE_V6_IN; + else if (strncmp (argv[1], "o", 1) == 0) + type = DISTRIBUTE_V6_OUT; + else + { + vty_out (vty, "distribute list direction must be [in|out]%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + ret = distribute_list_prefix_unset (argv[2], type, argv[0]); + if (! ret) + { + vty_out (vty, "distribute list doesn't exist%s", VTY_NEWLINE); + return CMD_WARNING; + } + return CMD_SUCCESS; +} -ALIAS (no_distribute_list_prefix, no_ipv6_distribute_list_prefix_cmd, +ALIAS (no_ipv6_distribute_list_prefix, + no_ipv6_as_v4_distribute_list_prefix_cmd, "no distribute-list prefix WORD (in|out) WORD", NO_STR "Filter networks in routing updates\n" @@ -610,80 +812,113 @@ ALIAS (no_distribute_list_prefix, no_ipv6_distribute_list_prefix_cmd, "Filter outgoing routing updates\n" "Interface name\n") +static int +distribute_print (struct vty *vty, char *tab[], int is_prefix, + enum distribute_type type, int has_print) +{ + if (tab[type]) { + vty_out (vty, "%s %s%s", + has_print ? "," : "", + is_prefix ? "(prefix-list) " : "", + tab[type]); + return 1; + } + return has_print; +} + int config_show_distribute (struct vty *vty) { unsigned int i; + int has_print = 0; struct hash_backet *mp; struct distribute *dist; /* Output filter configuration. */ dist = distribute_lookup (NULL); - if (dist && (dist->list[DISTRIBUTE_OUT] || dist->prefix[DISTRIBUTE_OUT])) + vty_out(vty, " Outgoing update filter list for all interface is"); + has_print = 0; + if (dist) { - vty_out (vty, " Outgoing update filter list for all interface is"); - if (dist->list[DISTRIBUTE_OUT]) - vty_out (vty, " %s", dist->list[DISTRIBUTE_OUT]); - if (dist->prefix[DISTRIBUTE_OUT]) - vty_out (vty, "%s (prefix-list) %s", - dist->list[DISTRIBUTE_OUT] ? "," : "", - dist->prefix[DISTRIBUTE_OUT]); - vty_out (vty, "%s", VTY_NEWLINE); + has_print = distribute_print(vty, dist->list, 0, + DISTRIBUTE_V4_OUT, has_print); + has_print = distribute_print(vty, dist->prefix, 1, + DISTRIBUTE_V4_OUT, has_print); + has_print = distribute_print(vty, dist->list, 0, + DISTRIBUTE_V6_OUT, has_print); + has_print = distribute_print(vty, dist->prefix, 1, + DISTRIBUTE_V6_OUT, has_print); } + if (has_print) + vty_out (vty, "%s", VTY_NEWLINE); else - vty_out (vty, " Outgoing update filter list for all interface is not set%s", VTY_NEWLINE); + vty_out (vty, " not set%s", VTY_NEWLINE); for (i = 0; i < disthash->size; i++) for (mp = disthash->index[i]; mp; mp = mp->next) { dist = mp->data; if (dist->ifname) - if (dist->list[DISTRIBUTE_OUT] || dist->prefix[DISTRIBUTE_OUT]) - { - vty_out (vty, " %s filtered by", dist->ifname); - if (dist->list[DISTRIBUTE_OUT]) - vty_out (vty, " %s", dist->list[DISTRIBUTE_OUT]); - if (dist->prefix[DISTRIBUTE_OUT]) - vty_out (vty, "%s (prefix-list) %s", - dist->list[DISTRIBUTE_OUT] ? "," : "", - dist->prefix[DISTRIBUTE_OUT]); - vty_out (vty, "%s", VTY_NEWLINE); - } + { + vty_out (vty, " %s filtered by", dist->ifname); + has_print = 0; + has_print = distribute_print(vty, dist->list, 0, + DISTRIBUTE_V4_OUT, has_print); + has_print = distribute_print(vty, dist->prefix, 1, + DISTRIBUTE_V4_OUT, has_print); + has_print = distribute_print(vty, dist->list, 0, + DISTRIBUTE_V6_OUT, has_print); + has_print = distribute_print(vty, dist->prefix, 1, + DISTRIBUTE_V6_OUT, has_print); + if (has_print) + vty_out (vty, "%s", VTY_NEWLINE); + else + vty_out(vty, " nothing%s", VTY_NEWLINE); + } } /* Input filter configuration. */ dist = distribute_lookup (NULL); - if (dist && (dist->list[DISTRIBUTE_IN] || dist->prefix[DISTRIBUTE_IN])) + vty_out(vty, " Incoming update filter list for all interface is"); + has_print = 0; + if (dist) { - vty_out (vty, " Incoming update filter list for all interface is"); - if (dist->list[DISTRIBUTE_IN]) - vty_out (vty, " %s", dist->list[DISTRIBUTE_IN]); - if (dist->prefix[DISTRIBUTE_IN]) - vty_out (vty, "%s (prefix-list) %s", - dist->list[DISTRIBUTE_IN] ? "," : "", - dist->prefix[DISTRIBUTE_IN]); - vty_out (vty, "%s", VTY_NEWLINE); + has_print = distribute_print(vty, dist->list, 0, + DISTRIBUTE_V4_IN, has_print); + has_print = distribute_print(vty, dist->prefix, 1, + DISTRIBUTE_V4_IN, has_print); + has_print = distribute_print(vty, dist->list, 0, + DISTRIBUTE_V6_IN, has_print); + has_print = distribute_print(vty, dist->prefix, 1, + DISTRIBUTE_V6_IN, has_print); } + if (has_print) + vty_out (vty, "%s", VTY_NEWLINE); else - vty_out (vty, " Incoming update filter list for all interface is not set%s", VTY_NEWLINE); + vty_out (vty, " not set%s", VTY_NEWLINE); for (i = 0; i < disthash->size; i++) for (mp = disthash->index[i]; mp; mp = mp->next) { dist = mp->data; - if (dist->ifname) - if (dist->list[DISTRIBUTE_IN] || dist->prefix[DISTRIBUTE_IN]) - { - vty_out (vty, " %s filtered by", dist->ifname); - if (dist->list[DISTRIBUTE_IN]) - vty_out (vty, " %s", dist->list[DISTRIBUTE_IN]); - if (dist->prefix[DISTRIBUTE_IN]) - vty_out (vty, "%s (prefix-list) %s", - dist->list[DISTRIBUTE_IN] ? "," : "", - dist->prefix[DISTRIBUTE_IN]); - vty_out (vty, "%s", VTY_NEWLINE); - } + if (dist->ifname) + { + vty_out (vty, " %s filtered by", dist->ifname); + has_print = 0; + has_print = distribute_print(vty, dist->list, 0, + DISTRIBUTE_V4_IN, has_print); + has_print = distribute_print(vty, dist->prefix, 1, + DISTRIBUTE_V4_IN, has_print); + has_print = distribute_print(vty, dist->list, 0, + DISTRIBUTE_V6_IN, has_print); + has_print = distribute_print(vty, dist->prefix, 1, + DISTRIBUTE_V6_IN, has_print); + if (has_print) + vty_out (vty, "%s", VTY_NEWLINE); + else + vty_out(vty, " nothing%s", VTY_NEWLINE); + } } return 0; } @@ -693,6 +928,8 @@ int config_write_distribute (struct vty *vty) { unsigned int i; + int j; + int output, v6; struct hash_backet *mp; int write = 0; @@ -703,38 +940,27 @@ config_write_distribute (struct vty *vty) dist = mp->data; - if (dist->list[DISTRIBUTE_IN]) - { - vty_out (vty, " distribute-list %s in %s%s", - dist->list[DISTRIBUTE_IN], - dist->ifname ? dist->ifname : "", - VTY_NEWLINE); - write++; - } - - if (dist->list[DISTRIBUTE_OUT]) - { - vty_out (vty, " distribute-list %s out %s%s", - - dist->list[DISTRIBUTE_OUT], + for (j=0; j < DISTRIBUTE_MAX; j++) + if (dist->list[j]) { + output = j == DISTRIBUTE_V4_OUT || j == DISTRIBUTE_V6_OUT; + v6 = j == DISTRIBUTE_V6_IN || j == DISTRIBUTE_V6_OUT; + vty_out (vty, " %sdistribute-list %s %s %s%s", + v6 ? "ipv6 " : "", + dist->list[j], + output ? "out" : "in", dist->ifname ? dist->ifname : "", VTY_NEWLINE); write++; } - if (dist->prefix[DISTRIBUTE_IN]) - { - vty_out (vty, " distribute-list prefix %s in %s%s", - dist->prefix[DISTRIBUTE_IN], - dist->ifname ? dist->ifname : "", - VTY_NEWLINE); - write++; - } - - if (dist->prefix[DISTRIBUTE_OUT]) - { - vty_out (vty, " distribute-list prefix %s out %s%s", - dist->prefix[DISTRIBUTE_OUT], + for (j=0; j < DISTRIBUTE_MAX; j++) + if (dist->prefix[j]) { + output = j == DISTRIBUTE_V4_OUT || j == DISTRIBUTE_V6_OUT; + v6 = j == DISTRIBUTE_V6_IN || j == DISTRIBUTE_V6_OUT; + vty_out (vty, " %sdistribute-list prefix %s %s %s%s", + v6 ? "ipv6 " : "", + dist->prefix[j], + output ? "out" : "in", dist->ifname ? dist->ifname : "", VTY_NEWLINE); write++; @@ -756,8 +982,8 @@ distribute_list_init (int node) { disthash = hash_create (distribute_hash_make, (int (*) (const void *, const void *)) distribute_cmp); - - if(node==RIP_NODE) { + /* install v4 */ + if (node == RIP_NODE || node == BABEL_NODE) { install_element (node, &distribute_list_all_cmd); install_element (node, &no_distribute_list_all_cmd); install_element (node, &distribute_list_cmd); @@ -766,10 +992,10 @@ distribute_list_init (int node) install_element (node, &no_distribute_list_prefix_all_cmd); install_element (node, &distribute_list_prefix_cmd); install_element (node, &no_distribute_list_prefix_cmd); - } else if (node == RIPNG_NODE || node == BABEL_NODE) { - /* WARNING: two identical commands installed do a crash, so be worry with - aliases. For this reason, and because all these commands are aliases, Babel - is not set with RIP. */ + } + + /* install v6 */ + if (node == RIPNG_NODE || node == BABEL_NODE) { install_element (node, &ipv6_distribute_list_all_cmd); install_element (node, &no_ipv6_distribute_list_all_cmd); install_element (node, &ipv6_distribute_list_cmd); @@ -779,4 +1005,16 @@ distribute_list_init (int node) install_element (node, &ipv6_distribute_list_prefix_cmd); install_element (node, &no_ipv6_distribute_list_prefix_cmd); } + + /* install v4 syntax command for v6 only protocols. */ + if (node == RIPNG_NODE) { + install_element (node, &ipv6_as_v4_distribute_list_all_cmd); + install_element (node, &no_ipv6_as_v4_distribute_list_all_cmd); + install_element (node, &ipv6_as_v4_distribute_list_cmd); + install_element (node, &no_ipv6_as_v4_distribute_list_cmd); + install_element (node, &ipv6_as_v4_distribute_list_prefix_all_cmd); + install_element (node, &no_ipv6_as_v4_distribute_list_prefix_all_cmd); + install_element (node, &ipv6_as_v4_distribute_list_prefix_cmd); + install_element (node, &no_ipv6_as_v4_distribute_list_prefix_cmd); + } } diff --git a/lib/distribute.h b/lib/distribute.h index 5072016fd..e9625a354 100644 --- a/lib/distribute.h +++ b/lib/distribute.h @@ -24,12 +24,15 @@ #include #include "if.h" +#include "filter.h" /* Disctirubte list types. */ enum distribute_type { - DISTRIBUTE_IN, - DISTRIBUTE_OUT, + DISTRIBUTE_V4_IN, + DISTRIBUTE_V6_IN, + DISTRIBUTE_V4_OUT, + DISTRIBUTE_V6_OUT, DISTRIBUTE_MAX }; diff --git a/lib/event_counter.c b/lib/event_counter.c new file mode 100644 index 000000000..e94aa4c0f --- /dev/null +++ b/lib/event_counter.c @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2016 Christian Franke + * + * This file is part of Quagga. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Alternatively, you can use, redistribute and/or modify it under the + * following terms: + * + * 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 "event_counter.h" + +void +event_counter_inc (struct event_counter *counter) +{ + counter->count++; + counter->last = time (NULL); +} + +const char * +event_counter_format (const struct event_counter *counter) +{ + struct tm last_change_store; + struct tm *last_change; + char timebuf[sizeof ("Thu, 01 Jan 1970 00:00:00 +0000")]; + static char rv[20 + sizeof (" last: ") + sizeof (timebuf)]; + + last_change = localtime_r (&counter->last, &last_change_store); + if (!last_change || strftime (timebuf, sizeof (timebuf), + "%a, %d %b %Y %T %z", last_change) == 0) + { + strncpy (timebuf, "???", sizeof (timebuf)); + } + + snprintf (rv, sizeof (rv), "%5llu last: %s", counter->count, + counter->last ? timebuf : "(never)"); + return rv; +} diff --git a/lib/event_counter.h b/lib/event_counter.h new file mode 100644 index 000000000..f40c6cde6 --- /dev/null +++ b/lib/event_counter.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2016 Christian Franke + * + * This file is part of Quagga. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Alternatively, you can use, redistribute and/or modify it under the + * following terms: + * + * 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 _ZEBRA_EVENT_COUNTER_H +#define _ZEBRA_EVENT_COUNTER_H + +struct event_counter +{ + unsigned long long count; + time_t last; +}; + +void event_counter_inc (struct event_counter *counter); +const char *event_counter_format (const struct event_counter *counter); + +#endif diff --git a/lib/fifo.h b/lib/fifo.h new file mode 100644 index 000000000..47a1e888e --- /dev/null +++ b/lib/fifo.h @@ -0,0 +1,63 @@ +/* FIFO common header. + Copyright (C) 2015 Kunihiro Ishiguro + +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 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 __LIB_FIFO_H__ +#define __LIB_FIFO_H__ + +/* FIFO -- first in first out structure and macros. */ +struct fifo +{ + struct fifo *next; + struct fifo *prev; + u_int32_t count; +}; + +#define FIFO_INIT(F) \ + do { \ + struct fifo *Xfifo = (struct fifo *)(F); \ + Xfifo->next = Xfifo->prev = Xfifo; \ + } while (0) + +#define FIFO_ADD(F,N) \ + do { \ + struct fifo *Xfifo = (struct fifo *)(F); \ + struct fifo *Xnode = (struct fifo *)(N); \ + Xnode->next = Xfifo; \ + Xnode->prev = Xfifo->prev; \ + Xfifo->prev = Xfifo->prev->next = Xnode; \ + } while (0) + +#define FIFO_DEL(N) \ + do { \ + struct fifo *Xnode = (struct fifo *)(N); \ + Xnode->prev->next = Xnode->next; \ + Xnode->next->prev = Xnode->prev; \ + } while (0) + +#define FIFO_HEAD(F) \ + ((((struct fifo *)(F))->next == (struct fifo *)(F)) \ + ? NULL : (F)->next) + +#define FIFO_EMPTY(F) \ + (((struct fifo *)(F))->next == (struct fifo *)(F)) + +#define FIFO_TOP(F) \ + (FIFO_EMPTY(F) ? NULL : ((struct fifo *)(F))->next) + +#endif /* __LIB_FIFO_H__ */ diff --git a/lib/filter.c b/lib/filter.c index 693418242..a47294140 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) @@ -493,15 +493,15 @@ access_list_filter_delete (struct access_list *access, struct filter *filter) filter_free (filter); - /* If access_list becomes empty delete it from access_master. */ - if (access_list_empty (access)) - access_list_delete (access); - /* Run hook function. */ if (master->delete_hook) (*master->delete_hook) (access); + + /* If access_list becomes empty delete it from access_master. */ + if (access_list_empty (access)) + access_list_delete (access); } - + /* deny Specify packets to reject permit Specify packets to forward diff --git a/lib/filter.h b/lib/filter.h index 37535cb13..e6ccd33b3 100644 --- a/lib/filter.h +++ b/lib/filter.h @@ -25,6 +25,11 @@ #include "if.h" +/* Filter direction. */ +#define FILTER_IN 0 +#define FILTER_OUT 1 +#define FILTER_MAX 2 + /* Filter type is made by `permit', `deny' and `dynamic'. */ enum filter_type { diff --git a/lib/getopt.c b/lib/getopt.c index c784fb6cc..7a58a8a8c 100644 --- a/lib/getopt.c +++ b/lib/getopt.c @@ -23,17 +23,13 @@ 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 # define _NO_PROTO #endif -#ifdef HAVE_CONFIG_H -# include -#endif - #include #if !defined __STDC__ || !__STDC__ @@ -193,7 +189,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 +239,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 +452,7 @@ _getopt_initialize (argc, argv, optstring) return optstring; } - + /* Scan elements of ARGV (whose length is ARGC) for option characters given in OPTSTRING. @@ -986,7 +982,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 985f12c58..bd3099e79 100644 --- a/lib/getopt1.c +++ b/lib/getopt1.c @@ -19,10 +19,6 @@ 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 #include #include "getopt.h" @@ -95,7 +91,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 000000000..8ddd9ffa5 --- /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 4cb772e57..920c6685f 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 1e99ffbca..0fc4b609c 100644 --- a/lib/if.c +++ b/lib/if.c @@ -27,6 +27,7 @@ #include "vector.h" #include "vty.h" #include "command.h" +#include "vrf.h" #include "if.h" #include "sockunion.h" #include "prefix.h" @@ -35,8 +36,8 @@ #include "buffer.h" #include "str.h" #include "log.h" - -/* Master list of interfaces. */ + +/* List of interfaces in only the default VRF */ struct list *iflist; /* One for each program. This structure is needed to store hooks. */ @@ -44,8 +45,8 @@ struct if_master { int (*if_new_hook) (struct interface *); int (*if_delete_hook) (struct interface *); -} if_master; - +} if_master = {0,}; + /* 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 @@ -113,9 +114,10 @@ if_cmp_func (struct interface *ifp1, struct interface *ifp2) /* Create new interface structure. */ struct interface * -if_create (const char *name, int namelen) +if_create_vrf (const char *name, int namelen, vrf_id_t vrf_id) { struct interface *ifp; + struct list *intf_list = vrf_iflist_get (vrf_id); ifp = XCALLOC (MTYPE_IF, sizeof (struct interface)); ifp->ifindex = IFINDEX_INTERNAL; @@ -124,11 +126,12 @@ if_create (const char *name, int namelen) assert (namelen <= INTERFACE_NAMSIZ); /* Need space for '\0' at end. */ strncpy (ifp->name, name, namelen); ifp->name[namelen] = '\0'; - if (if_lookup_by_name(ifp->name) == NULL) - listnode_add_sort (iflist, ifp); + ifp->vrf_id = vrf_id; + if (if_lookup_by_name_vrf (ifp->name, vrf_id) == NULL) + listnode_add_sort (intf_list, ifp); else zlog_err("if_create(%s): corruption detected -- interface with this " - "name exists already!", ifp->name); + "name exists already in VRF %u!", ifp->name, vrf_id); ifp->connected = list_new (); ifp->connected->del = (void (*) (void *)) connected_free; @@ -138,6 +141,12 @@ if_create (const char *name, int namelen) return ifp; } +struct interface * +if_create (const char *name, int namelen) +{ + return if_create_vrf (name, namelen, VRF_DEFAULT); +} + /* Delete interface structure. */ void if_delete_retain (struct interface *ifp) @@ -146,17 +155,21 @@ if_delete_retain (struct interface *ifp) (*if_master.if_delete_hook) (ifp); /* Free connected address list */ - list_delete (ifp->connected); + list_delete_all_node (ifp->connected); } /* Delete and free interface structure. */ void if_delete (struct interface *ifp) { - listnode_delete (iflist, ifp); + listnode_delete (vrf_iflist (ifp->vrf_id), ifp); if_delete_retain(ifp); + list_free (ifp->connected); + + if_link_params_free (ifp); + XFREE (MTYPE_IF, ifp); } @@ -178,46 +191,64 @@ if_add_hook (int type, int (*func)(struct interface *ifp)) /* Interface existance check by index. */ struct interface * -if_lookup_by_index (unsigned int index) +if_lookup_by_index_vrf (ifindex_t ifindex, vrf_id_t vrf_id) { struct listnode *node; struct interface *ifp; - for (ALL_LIST_ELEMENTS_RO(iflist, node, ifp)) + for (ALL_LIST_ELEMENTS_RO (vrf_iflist (vrf_id), node, ifp)) { - if (ifp->ifindex == index) + if (ifp->ifindex == ifindex) return ifp; } return NULL; } +struct interface * +if_lookup_by_index (ifindex_t ifindex) +{ + return if_lookup_by_index_vrf (ifindex, VRF_DEFAULT); +} + const char * -ifindex2ifname (unsigned int index) +ifindex2ifname_vrf (ifindex_t ifindex, vrf_id_t vrf_id) { struct interface *ifp; - return ((ifp = if_lookup_by_index(index)) != NULL) ? + return ((ifp = if_lookup_by_index_vrf (ifindex, vrf_id)) != NULL) ? ifp->name : "unknown"; } -unsigned int -ifname2ifindex (const char *name) +const char * +ifindex2ifname (ifindex_t ifindex) +{ + return ifindex2ifname_vrf (ifindex, VRF_DEFAULT); +} + +ifindex_t +ifname2ifindex_vrf (const char *name, vrf_id_t vrf_id) { struct interface *ifp; - return ((ifp = if_lookup_by_name(name)) != NULL) ? ifp->ifindex + return ((ifp = if_lookup_by_name_vrf (name, vrf_id)) != NULL) ? ifp->ifindex : IFINDEX_INTERNAL; } +ifindex_t +ifname2ifindex (const char *name) +{ + return ifname2ifindex_vrf (name, VRF_DEFAULT); +} + /* Interface existance check by interface name. */ struct interface * -if_lookup_by_name (const char *name) +if_lookup_by_name_vrf (const char *name, vrf_id_t vrf_id) { struct listnode *node; struct interface *ifp; if (name) - for (ALL_LIST_ELEMENTS_RO (iflist, node, ifp)) + for (ALL_LIST_ELEMENTS_RO (vrf_iflist (vrf_id), node, ifp)) { if (strcmp(name, ifp->name) == 0) return ifp; @@ -226,7 +257,13 @@ if_lookup_by_name (const char *name) } struct interface * -if_lookup_by_name_len(const char *name, size_t namelen) +if_lookup_by_name (const char *name) +{ + return if_lookup_by_name_vrf (name, VRF_DEFAULT); +} + +struct interface * +if_lookup_by_name_len_vrf (const char *name, size_t namelen, vrf_id_t vrf_id) { struct listnode *node; struct interface *ifp; @@ -234,7 +271,7 @@ if_lookup_by_name_len(const char *name, size_t namelen) if (namelen > INTERFACE_NAMSIZ) return NULL; - for (ALL_LIST_ELEMENTS_RO (iflist, node, ifp)) + for (ALL_LIST_ELEMENTS_RO (vrf_iflist (vrf_id), node, ifp)) { if (!memcmp(name, ifp->name, namelen) && (ifp->name[namelen] == '\0')) return ifp; @@ -242,9 +279,15 @@ if_lookup_by_name_len(const char *name, size_t namelen) return NULL; } +struct interface * +if_lookup_by_name_len(const char *name, size_t namelen) +{ + return if_lookup_by_name_len_vrf (name, namelen, VRF_DEFAULT); +} + /* Lookup interface by IPv4 address. */ struct interface * -if_lookup_exact_address (struct in_addr src) +if_lookup_exact_address_vrf (struct in_addr src, vrf_id_t vrf_id) { struct listnode *node; struct listnode *cnode; @@ -252,7 +295,7 @@ if_lookup_exact_address (struct in_addr src) struct prefix *p; struct connected *c; - for (ALL_LIST_ELEMENTS_RO (iflist, node, ifp)) + for (ALL_LIST_ELEMENTS_RO (vrf_iflist (vrf_id), node, ifp)) { for (ALL_LIST_ELEMENTS_RO (ifp->connected, cnode, c)) { @@ -268,9 +311,15 @@ if_lookup_exact_address (struct in_addr src) return NULL; } +struct interface * +if_lookup_exact_address (struct in_addr src) +{ + return if_lookup_exact_address_vrf (src, VRF_DEFAULT); +} + /* Lookup interface by IPv4 address. */ struct interface * -if_lookup_address (struct in_addr src) +if_lookup_address_vrf (struct in_addr src, vrf_id_t vrf_id) { struct listnode *node; struct prefix addr; @@ -286,7 +335,7 @@ if_lookup_address (struct in_addr src) match = NULL; - for (ALL_LIST_ELEMENTS_RO (iflist, node, ifp)) + for (ALL_LIST_ELEMENTS_RO (vrf_iflist (vrf_id), node, ifp)) { for (ALL_LIST_ELEMENTS_RO (ifp->connected, cnode, c)) { @@ -302,24 +351,70 @@ if_lookup_address (struct in_addr src) return match; } +struct interface * +if_lookup_address (struct in_addr src) +{ + return if_lookup_address_vrf (src, VRF_DEFAULT); +} + +/* Lookup interface by prefix */ +struct interface * +if_lookup_prefix_vrf (struct prefix *prefix, vrf_id_t vrf_id) +{ + struct listnode *node; + struct listnode *cnode; + struct interface *ifp; + struct connected *c; + + for (ALL_LIST_ELEMENTS_RO (vrf_iflist (vrf_id), node, ifp)) + { + for (ALL_LIST_ELEMENTS_RO (ifp->connected, cnode, c)) + { + if (prefix_cmp(c->address, prefix) == 0) + { + return ifp; + } + } + } + return NULL; +} + +struct interface * +if_lookup_prefix (struct prefix *prefix) +{ + return if_lookup_prefix_vrf (prefix, VRF_DEFAULT); +} + /* Get interface by name if given name interface doesn't exist create one. */ struct interface * -if_get_by_name (const char *name) +if_get_by_name_vrf (const char *name, vrf_id_t vrf_id) { struct interface *ifp; - return ((ifp = if_lookup_by_name(name)) != NULL) ? ifp : - if_create(name, strlen(name)); + return ((ifp = if_lookup_by_name_vrf (name, vrf_id)) != NULL) ? ifp : + if_create_vrf (name, strlen(name), vrf_id); +} + +struct interface * +if_get_by_name (const char *name) +{ + return if_get_by_name_vrf (name, VRF_DEFAULT); } struct interface * -if_get_by_name_len(const char *name, size_t namelen) +if_get_by_name_len_vrf (const char *name, size_t namelen, vrf_id_t vrf_id) { struct interface *ifp; - return ((ifp = if_lookup_by_name_len(name, namelen)) != NULL) ? ifp : - if_create(name, namelen); + return ((ifp = if_lookup_by_name_len_vrf (name, namelen, vrf_id)) != NULL) ? \ + ifp : if_create_vrf (name, namelen, vrf_id); +} + +struct interface * +if_get_by_name_len (const char *name, size_t namelen) +{ + return if_get_by_name_len_vrf (name, namelen, VRF_DEFAULT); } /* Does interface up ? */ @@ -427,15 +522,15 @@ 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 " + zlog_info ("Interface %s vrf %u index %d metric %d mtu %d " #ifdef HAVE_IPV6 "mtu6 %d " #endif /* HAVE_IPV6 */ "%s", - ifp->name, ifp->ifindex, ifp->metric, ifp->mtu, + ifp->name, ifp->vrf_id, ifp->ifindex, ifp->metric, ifp->mtu, #ifdef HAVE_IPV6 ifp->mtu6, #endif /* HAVE_IPV6 */ @@ -446,11 +541,15 @@ if_dump (const struct interface *ifp) void if_dump_all (void) { + struct list *intf_list; struct listnode *node; void *p; + vrf_iter_t iter; - for (ALL_LIST_ELEMENTS_RO (iflist, node, p)) - if_dump (p); + for (iter = vrf_first (); iter != VRF_ITER_INVALID; iter = vrf_next (iter)) + if ((intf_list = vrf_iter2iflist (iter)) != NULL) + for (ALL_LIST_ELEMENTS_RO (intf_list, node, p)) + if_dump (p); } DEFUN (interface_desc, @@ -487,7 +586,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 @@ -510,12 +609,12 @@ DEFUN (no_interface_desc, * - no idea, just get the name in its entirety. */ static struct interface * -if_sunwzebra_get (const char *name, size_t nlen) +if_sunwzebra_get (const char *name, size_t nlen, vrf_id_t vrf_id) { struct interface *ifp; size_t seppos = 0; - if ( (ifp = if_lookup_by_name_len(name, nlen)) != NULL) + if ( (ifp = if_lookup_by_name_len_vrf (name, nlen, vrf_id)) != NULL) return ifp; /* hunt the primary interface name... */ @@ -524,12 +623,12 @@ if_sunwzebra_get (const char *name, size_t nlen) /* Wont catch seperator as last char, e.g. 'foo0:' but thats invalid */ if (seppos < nlen) - return if_get_by_name_len (name, seppos); + return if_get_by_name_len_vrf (name, seppos, vrf_id); else - return if_get_by_name_len (name, nlen); + return if_get_by_name_len_vrf (name, nlen, vrf_id); } #endif /* SUNOS_5 */ - + DEFUN (interface, interface_cmd, "interface IFNAME", @@ -538,6 +637,7 @@ DEFUN (interface, { struct interface *ifp; size_t sl; + vrf_id_t vrf_id = VRF_DEFAULT; if ((sl = strlen(argv[0])) > INTERFACE_NAMSIZ) { @@ -547,10 +647,13 @@ DEFUN (interface, return CMD_WARNING; } + if (argc > 1) + VTY_GET_INTEGER ("VRF ID", vrf_id, argv[1]); + #ifdef SUNOS_5 - ifp = if_sunwzebra_get (argv[0], sl); + ifp = if_sunwzebra_get (argv[0], sl, vrf_id); #else - ifp = if_get_by_name_len(argv[0], sl); + ifp = if_get_by_name_len_vrf (argv[0], sl, vrf_id); #endif /* SUNOS_5 */ vty->index = ifp; @@ -559,6 +662,13 @@ DEFUN (interface, return CMD_SUCCESS; } +ALIAS (interface, + interface_vrf_cmd, + "interface IFNAME " VRF_CMD_STR, + "Select an interface to configure\n" + "Interface's name\n" + VRF_CMD_HELP_STR) + DEFUN_NOSH (no_interface, no_interface_cmd, "no interface IFNAME", @@ -568,8 +678,12 @@ DEFUN_NOSH (no_interface, { // deleting interface struct interface *ifp; + vrf_id_t vrf_id = VRF_DEFAULT; - ifp = if_lookup_by_name (argv[0]); + if (argc > 1) + VTY_GET_INTEGER ("VRF ID", vrf_id, argv[1]); + + ifp = if_lookup_by_name_vrf (argv[0], vrf_id); if (ifp == NULL) { @@ -589,6 +703,14 @@ DEFUN_NOSH (no_interface, return CMD_SUCCESS; } +ALIAS (no_interface, + no_interface_vrf_cmd, + "no interface IFNAME " VRF_CMD_STR, + NO_STR + "Delete a pseudo interface's configuration\n" + "Interface's name\n" + VRF_CMD_HELP_STR) + /* For debug purpose. */ DEFUN (show_address, show_address_cmd, @@ -601,8 +723,12 @@ DEFUN (show_address, struct interface *ifp; struct connected *ifc; struct prefix *p; + vrf_id_t vrf_id = VRF_DEFAULT; + + if (argc > 0) + VTY_GET_INTEGER ("VRF ID", vrf_id, argv[0]); - for (ALL_LIST_ELEMENTS_RO (iflist, node, ifp)) + for (ALL_LIST_ELEMENTS_RO (vrf_iflist (vrf_id), node, ifp)) { for (ALL_LIST_ELEMENTS_RO (ifp->connected, node2, ifc)) { @@ -616,6 +742,52 @@ DEFUN (show_address, return CMD_SUCCESS; } +ALIAS (show_address, + show_address_vrf_cmd, + "show address " VRF_CMD_STR, + SHOW_STR + "address\n" + VRF_CMD_HELP_STR) + +DEFUN (show_address_vrf_all, + show_address_vrf_all_cmd, + "show address " VRF_ALL_CMD_STR, + SHOW_STR + "address\n" + VRF_ALL_CMD_HELP_STR) +{ + struct list *intf_list; + struct listnode *node; + struct listnode *node2; + struct interface *ifp; + struct connected *ifc; + struct prefix *p; + vrf_iter_t iter; + + for (iter = vrf_first (); iter != VRF_ITER_INVALID; iter = vrf_next (iter)) + { + intf_list = vrf_iter2iflist (iter); + if (!intf_list || !listcount (intf_list)) + continue; + + vty_out (vty, "%sVRF %u%s%s", VTY_NEWLINE, vrf_iter2id (iter), + VTY_NEWLINE, VTY_NEWLINE); + + for (ALL_LIST_ELEMENTS_RO (intf_list, node, ifp)) + { + for (ALL_LIST_ELEMENTS_RO (ifp->connected, node2, ifc)) + { + p = ifc->address; + + if (p->family == AF_INET) + vty_out (vty, "%s/%d%s", inet_ntoa (p->u.prefix4), p->prefixlen, + VTY_NEWLINE); + } + } + } + return CMD_SUCCESS; +} + /* Allocate connected structure. */ struct connected * connected_new (void) @@ -651,8 +823,8 @@ connected_log (struct connected *connected, char *str) ifp = connected->ifp; p = connected->address; - snprintf (logbuf, BUFSIZ, "%s interface %s %s %s/%d ", - str, ifp->name, prefix_family_str (p), + snprintf (logbuf, BUFSIZ, "%s interface %s vrf %u %s %s/%d ", + str, ifp->name, ifp->vrf_id, prefix_family_str (p), inet_ntop (p->family, &p->u.prefix, buf, BUFSIZ), p->prefixlen); @@ -758,7 +930,7 @@ connected_add_by_prefix (struct interface *ifp, struct prefix *p, } #ifndef HAVE_IF_NAMETOINDEX -unsigned int +ifindex_t if_nametoindex (const char *name) { struct interface *ifp; @@ -770,7 +942,7 @@ if_nametoindex (const char *name) #ifndef HAVE_IF_INDEXTONAME char * -if_indextoname (unsigned int ifindex, char *name) +if_indextoname (ifindex_t ifindex, char *name) { struct interface *ifp; @@ -780,7 +952,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.. @@ -835,7 +1007,7 @@ ifaddr_ipv4_delete (struct in_addr *ifaddr, struct interface *ifp) /* Lookup interface by interface's IP address or interface index. */ static struct interface * -ifaddr_ipv4_lookup (struct in_addr *addr, unsigned int ifindex) +ifaddr_ipv4_lookup (struct in_addr *addr, ifindex_t ifindex) { struct prefix_ipv4 p; struct route_node *rn; @@ -862,35 +1034,138 @@ ifaddr_ipv4_lookup (struct in_addr *addr, unsigned int ifindex) /* Initialize interface list. */ void -if_init (void) +if_init (vrf_id_t vrf_id, struct list **intf_list) { - iflist = list_new (); + *intf_list = list_new (); #if 0 ifaddr_ipv4_table = route_table_init (); #endif /* ifaddr_ipv4_table */ - if (iflist) { - iflist->cmp = (int (*)(void *, void *))if_cmp_func; - return; - } + (*intf_list)->cmp = (int (*)(void *, void *))if_cmp_func; - memset (&if_master, 0, sizeof if_master); + if (vrf_id == VRF_DEFAULT) + iflist = *intf_list; } void -if_terminate (void) +if_terminate (vrf_id_t vrf_id, struct list **intf_list) { for (;;) { struct interface *ifp; - ifp = listnode_head (iflist); + ifp = listnode_head (*intf_list); if (ifp == NULL) break; if_delete (ifp); } - list_delete (iflist); - iflist = NULL; + list_delete (*intf_list); + *intf_list = NULL; + + if (vrf_id == VRF_DEFAULT) + iflist = NULL; +} + +const char * +if_link_type_str (enum zebra_link_type llt) +{ + switch (llt) + { +#define llts(T,S) case (T): return (S) + llts(ZEBRA_LLT_UNKNOWN, "Unknown"); + llts(ZEBRA_LLT_ETHER, "Ethernet"); + llts(ZEBRA_LLT_EETHER, "Experimental Ethernet"); + llts(ZEBRA_LLT_AX25, "AX.25 Level 2"); + llts(ZEBRA_LLT_PRONET, "PROnet token ring"); + llts(ZEBRA_LLT_IEEE802, "IEEE 802.2 Ethernet/TR/TB"); + llts(ZEBRA_LLT_ARCNET, "ARCnet"); + llts(ZEBRA_LLT_APPLETLK, "AppleTalk"); + llts(ZEBRA_LLT_DLCI, "Frame Relay DLCI"); + llts(ZEBRA_LLT_ATM, "ATM"); + llts(ZEBRA_LLT_METRICOM, "Metricom STRIP"); + llts(ZEBRA_LLT_IEEE1394, "IEEE 1394 IPv4"); + llts(ZEBRA_LLT_EUI64, "EUI-64"); + llts(ZEBRA_LLT_INFINIBAND, "InfiniBand"); + llts(ZEBRA_LLT_SLIP, "SLIP"); + llts(ZEBRA_LLT_CSLIP, "Compressed SLIP"); + llts(ZEBRA_LLT_SLIP6, "SLIPv6"); + llts(ZEBRA_LLT_CSLIP6, "Compressed SLIPv6"); + llts(ZEBRA_LLT_ROSE, "ROSE packet radio"); + llts(ZEBRA_LLT_X25, "CCITT X.25"); + llts(ZEBRA_LLT_PPP, "PPP"); + llts(ZEBRA_LLT_CHDLC, "Cisco HDLC"); + llts(ZEBRA_LLT_RAWHDLC, "Raw HDLC"); + llts(ZEBRA_LLT_LAPB, "LAPB"); + llts(ZEBRA_LLT_IPIP, "IPIP Tunnel"); + llts(ZEBRA_LLT_IPIP6, "IPIP6 Tunnel"); + llts(ZEBRA_LLT_FRAD, "FRAD"); + llts(ZEBRA_LLT_SKIP, "SKIP vif"); + llts(ZEBRA_LLT_LOOPBACK, "Loopback"); + llts(ZEBRA_LLT_LOCALTLK, "Localtalk"); + llts(ZEBRA_LLT_FDDI, "FDDI"); + llts(ZEBRA_LLT_SIT, "IPv6-in-IPv4 SIT"); + llts(ZEBRA_LLT_IPDDP, "IP-in-DDP tunnel"); + llts(ZEBRA_LLT_IPGRE, "GRE over IP"); + llts(ZEBRA_LLT_PIMREG, "PIMSM registration"); + llts(ZEBRA_LLT_HIPPI, "HiPPI"); + llts(ZEBRA_LLT_IRDA, "IrDA"); + llts(ZEBRA_LLT_FCPP, "Fibre-Channel PtP"); + llts(ZEBRA_LLT_FCAL, "Fibre-Channel Arbitrated Loop"); + llts(ZEBRA_LLT_FCPL, "Fibre-Channel Public Loop"); + llts(ZEBRA_LLT_FCFABRIC, "Fibre-Channel Fabric"); + llts(ZEBRA_LLT_IEEE802_TR, "IEEE 802.2 Token Ring"); + llts(ZEBRA_LLT_IEEE80211, "IEEE 802.11"); + llts(ZEBRA_LLT_IEEE80211_RADIOTAP, "IEEE 802.11 Radiotap"); + llts(ZEBRA_LLT_IEEE802154, "IEEE 802.15.4"); + llts(ZEBRA_LLT_IEEE802154_PHY, "IEEE 802.15.4 Phy"); + default: + zlog_warn ("Unknown value %d", llt); + return "Unknown type!"; +#undef llts + } + return NULL; +} + +struct if_link_params * +if_link_params_get (struct interface *ifp) +{ + int i; + + if (ifp->link_params != NULL) + return ifp->link_params; + + struct if_link_params *iflp = XCALLOC(MTYPE_IF_LINK_PARAMS, + sizeof (struct if_link_params)); + if (iflp == NULL) return NULL; + + /* Set TE metric == standard metric */ + iflp->te_metric = ifp->metric; + + /* Compute default bandwidth based on interface */ + int bw = (float)((ifp->bandwidth ? ifp->bandwidth : DEFAULT_BANDWIDTH) + * TE_KILO_BIT / TE_BYTE); + + /* Set Max, Reservable and Unreserved Bandwidth */ + iflp->max_bw = bw; + iflp->max_rsv_bw = bw; + for (i = 0; i < MAX_CLASS_TYPE; i++) + iflp->unrsv_bw[i] = bw; + + /* Update Link parameters status */ + iflp->lp_status = LP_TE | LP_MAX_BW | LP_MAX_RSV_BW | LP_UNRSV_BW; + + /* Finally attach newly created Link Parameters */ + ifp->link_params = iflp; + + return iflp; +} + +void +if_link_params_free (struct interface *ifp) +{ + if (ifp->link_params == NULL) return; + XFREE(MTYPE_IF_LINK_PARAMS, ifp->link_params); + ifp->link_params = NULL; } diff --git a/lib/if.h b/lib/if.h index 841ce51ec..862f7d471 100644 --- a/lib/if.h +++ b/lib/if.h @@ -21,8 +21,69 @@ Boston, MA 02111-1307, USA. */ #ifndef _ZEBRA_IF_H #define _ZEBRA_IF_H +#include "zebra.h" #include "linklist.h" +/* Interface link-layer type, if known. Derived from: + * + * net/if_arp.h on various platforms - Linux especially. + * http://www.iana.org/assignments/arp-parameters/arp-parameters.xhtml + * + * Some of the more obviously defunct technologies left out. + */ +enum zebra_link_type { + ZEBRA_LLT_UNKNOWN = 0, + ZEBRA_LLT_ETHER, + ZEBRA_LLT_EETHER, + ZEBRA_LLT_AX25, + ZEBRA_LLT_PRONET, + ZEBRA_LLT_IEEE802, + ZEBRA_LLT_ARCNET, + ZEBRA_LLT_APPLETLK, + ZEBRA_LLT_DLCI, + ZEBRA_LLT_ATM, + ZEBRA_LLT_METRICOM, + ZEBRA_LLT_IEEE1394, + ZEBRA_LLT_EUI64, + ZEBRA_LLT_INFINIBAND, + ZEBRA_LLT_SLIP, + ZEBRA_LLT_CSLIP, + ZEBRA_LLT_SLIP6, + ZEBRA_LLT_CSLIP6, + ZEBRA_LLT_RSRVD, + ZEBRA_LLT_ADAPT, + ZEBRA_LLT_ROSE, + ZEBRA_LLT_X25, + ZEBRA_LLT_PPP, + ZEBRA_LLT_CHDLC, + ZEBRA_LLT_LAPB, + ZEBRA_LLT_RAWHDLC, + ZEBRA_LLT_IPIP, + ZEBRA_LLT_IPIP6, + ZEBRA_LLT_FRAD, + ZEBRA_LLT_SKIP, + ZEBRA_LLT_LOOPBACK, + ZEBRA_LLT_LOCALTLK, + ZEBRA_LLT_FDDI, + ZEBRA_LLT_SIT, + ZEBRA_LLT_IPDDP, + ZEBRA_LLT_IPGRE, + ZEBRA_LLT_IP6GRE, + ZEBRA_LLT_PIMREG, + ZEBRA_LLT_HIPPI, + ZEBRA_LLT_ECONET, + ZEBRA_LLT_IRDA, + ZEBRA_LLT_FCPP, + ZEBRA_LLT_FCAL, + ZEBRA_LLT_FCPL, + ZEBRA_LLT_FCFABRIC, + ZEBRA_LLT_IEEE802_TR, + ZEBRA_LLT_IEEE80211, + ZEBRA_LLT_IEEE80211_RADIOTAP, + ZEBRA_LLT_IEEE802154, + ZEBRA_LLT_IEEE802154_PHY, +}; + /* Interface name length. @@ -36,6 +97,8 @@ Boston, MA 02111-1307, USA. */ #define INTERFACE_NAMSIZ 20 #define INTERFACE_HWADDR_MAX 20 +typedef signed int ifindex_t; + #ifdef HAVE_PROC_NET_DEV struct if_stats { @@ -68,6 +131,63 @@ struct if_stats }; #endif /* HAVE_PROC_NET_DEV */ +/* Here are "non-official" architectural constants. */ +#define TE_EXT_MASK 0x0FFFFFFF +#define TE_EXT_ANORMAL 0x80000000 +#define LOSS_PRECISION 0.000003 +#define TE_KILO_BIT 1000 +#define TE_BYTE 8 +#define DEFAULT_BANDWIDTH 10000 +#define MAX_CLASS_TYPE 8 +#define MAX_PKT_LOSS 50.331642 + +/* Link Parameters Status: 0: unset, 1: set, */ +#define LP_UNSET 0x0000 +#define LP_TE 0x0001 +#define LP_MAX_BW 0x0002 +#define LP_MAX_RSV_BW 0x0004 +#define LP_UNRSV_BW 0x0008 +#define LP_ADM_GRP 0x0010 +#define LP_RMT_AS 0x0020 +#define LP_DELAY 0x0040 +#define LP_MM_DELAY 0x0080 +#define LP_DELAY_VAR 0x0100 +#define LP_PKT_LOSS 0x0200 +#define LP_RES_BW 0x0400 +#define LP_AVA_BW 0x0800 +#define LP_USE_BW 0x1000 + +#define IS_PARAM_UNSET(lp, st) !(lp->lp_status & st) +#define IS_PARAM_SET(lp, st) (lp->lp_status & st) +#define IS_LINK_PARAMS_SET(lp) (lp->lp_status != LP_UNSET) + +#define SET_PARAM(lp, st) (lp->lp_status) |= (st) +#define UNSET_PARAM(lp, st) (lp->lp_status) &= ~(st) +#define RESET_LINK_PARAM(lp) (lp->lp_status = LP_UNSET) + +/* Link Parameters for Traffic Engineering */ +struct if_link_params { + u_int32_t lp_status; /* Status of Link Parameters: */ + u_int32_t te_metric; /* Traffic Engineering metric */ + float max_bw; /* Maximum Bandwidth */ + float max_rsv_bw; /* Maximum Reservable Bandwidth */ + float unrsv_bw[MAX_CLASS_TYPE]; /* Unreserved Bandwidth per Class Type (8) */ + u_int32_t admin_grp; /* Administrative group */ + u_int32_t rmt_as; /* Remote AS number */ + struct in_addr rmt_ip; /* Remote IP address */ + u_int32_t av_delay; /* Link Average Delay */ + u_int32_t min_delay; /* Link Min Delay */ + u_int32_t max_delay; /* Link Max Delay */ + u_int32_t delay_var; /* Link Delay Variation */ + float pkt_loss; /* Link Packet Loss */ + float res_bw; /* Residual Bandwidth */ + float ava_bw; /* Available Bandwidth */ + float use_bw; /* Utilized Bandwidth */ +}; + +#define INTERFACE_LINK_PARAMS_SIZE sizeof(struct if_link_params) +#define HAS_LINK_PARAMS(ifp) ((ifp)->link_params != NULL) + /* Interface structure */ struct interface { @@ -82,7 +202,7 @@ struct interface /* Interface index (should be IFINDEX_INTERNAL for non-kernel or deleted interfaces). */ - unsigned int ifindex; + ifindex_t ifindex; #define IFINDEX_INTERNAL 0 /* Zebra internal interface status */ @@ -101,18 +221,17 @@ struct interface unsigned int mtu; /* IPv4 MTU */ unsigned int mtu6; /* IPv6 MTU - probably, but not neccessarily same as mtu */ - /* Hardware address. */ -#ifdef HAVE_STRUCT_SOCKADDR_DL - struct sockaddr_dl sdl; -#else - unsigned short hw_type; + /* Link-layer information and hardware address */ + enum zebra_link_type ll_type; u_char hw_addr[INTERFACE_HWADDR_MAX]; int hw_addr_len; -#endif /* HAVE_STRUCT_SOCKADDR_DL */ /* interface bandwidth, kbits */ unsigned int bandwidth; + /* Link parameters for Traffic Engineering */ + struct if_link_params *link_params; + /* description of the interface. */ char *desc; @@ -133,6 +252,8 @@ struct interface #ifdef HAVE_NET_RT_IFLIST struct if_data stats; #endif /* HAVE_NET_RT_IFLIST */ + + vrf_id_t vrf_id; }; /* Connected address structure. */ @@ -145,17 +266,23 @@ 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. */ u_char flags; #define ZEBRA_IFA_SECONDARY (1 << 0) #define ZEBRA_IFA_PEER (1 << 1) +#define ZEBRA_IFA_UNNUMBERED (1 << 2) /* N.B. the ZEBRA_IFA_PEER flag should be set if and only if a peer address has been configured. If this flag is set, the destination field must contain the peer address. @@ -231,21 +358,42 @@ struct connected /* Prototypes. */ extern int if_cmp_func (struct interface *, struct interface *); extern struct interface *if_create (const char *name, int namelen); -extern struct interface *if_lookup_by_index (unsigned int); +extern struct interface *if_lookup_by_index (ifindex_t); 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); + +extern struct interface *if_create_vrf (const char *name, int namelen, + vrf_id_t vrf_id); +extern struct interface *if_lookup_by_index_vrf (ifindex_t, vrf_id_t vrf_id); +extern struct interface *if_lookup_exact_address_vrf (struct in_addr, + vrf_id_t vrf_id); +extern struct interface *if_lookup_address_vrf (struct in_addr, + vrf_id_t vrf_id); +extern struct interface *if_lookup_prefix_vrf (struct prefix *prefix, + vrf_id_t vrf_id); /* These 2 functions are to be used when the ifname argument is terminated by a '\0' character: */ extern struct interface *if_lookup_by_name (const char *ifname); extern struct interface *if_get_by_name (const char *ifname); +extern struct interface *if_lookup_by_name_vrf (const char *ifname, + vrf_id_t vrf_id); +extern struct interface *if_get_by_name_vrf (const char *ifname, + vrf_id_t vrf_id); + /* For these 2 functions, the namelen argument should be the precise length of the ifname string (not counting any optional trailing '\0' character). In most cases, strnlen should be used to calculate the namelen value. */ extern struct interface *if_lookup_by_name_len(const char *ifname, - size_t namelen); -extern struct interface *if_get_by_name_len(const char *ifname, size_t namelen); + size_t namelen); +extern struct interface *if_get_by_name_len(const char *ifname,size_t namelen); + +extern struct interface *if_lookup_by_name_len_vrf(const char *ifname, + size_t namelen, vrf_id_t vrf_id); +extern struct interface *if_get_by_name_len_vrf(const char *ifname, + size_t namelen, vrf_id_t vrf_id); /* Delete the interface, but do not free the structure, and leave it in the @@ -265,20 +413,23 @@ extern int if_is_broadcast (struct interface *); extern int if_is_pointopoint (struct interface *); extern int if_is_multicast (struct interface *); extern void if_add_hook (int, int (*)(struct interface *)); -extern void if_init (void); -extern void if_terminate (void); +extern void if_init (vrf_id_t, struct list **); +extern void if_terminate (vrf_id_t, struct list **); extern void if_dump_all (void); extern const char *if_flag_dump(unsigned long); +extern const char *if_link_type_str (enum zebra_link_type); /* Please use ifindex2ifname instead of if_indextoname where possible; ifindex2ifname uses internal interface info, whereas if_indextoname must make a system call. */ -extern const char *ifindex2ifname (unsigned int); +extern const char *ifindex2ifname (ifindex_t); +extern const char *ifindex2ifname_vrf (ifindex_t, vrf_id_t vrf_id); /* Please use ifname2ifindex instead of if_nametoindex where possible; ifname2ifindex uses internal interface info, whereas if_nametoindex must make a system call. */ -extern unsigned int ifname2ifindex(const char *ifname); +extern ifindex_t ifname2ifindex(const char *ifname); +extern ifindex_t ifname2ifindex_vrf(const char *ifname, vrf_id_t vrf_id); /* Connected address functions. */ extern struct connected *connected_new (void); @@ -293,20 +444,28 @@ extern struct connected *connected_lookup_address (struct interface *, struct in_addr); #ifndef HAVE_IF_NAMETOINDEX -extern unsigned int if_nametoindex (const char *); +extern ifindex_t if_nametoindex (const char *); #endif #ifndef HAVE_IF_INDEXTONAME -extern char *if_indextoname (unsigned int, char *); +extern char *if_indextoname (ifindex_t, char *); #endif +/* link parameters */ +struct if_link_params *if_link_params_get (struct interface *); +void if_link_params_free (struct interface *); + /* Exported variables. */ extern struct list *iflist; extern struct cmd_element interface_desc_cmd; extern struct cmd_element no_interface_desc_cmd; extern struct cmd_element interface_cmd; extern struct cmd_element no_interface_cmd; +extern struct cmd_element interface_vrf_cmd; +extern struct cmd_element no_interface_vrf_cmd; extern struct cmd_element interface_pseudo_cmd; extern struct cmd_element no_interface_pseudo_cmd; extern struct cmd_element show_address_cmd; +extern struct cmd_element show_address_vrf_cmd; +extern struct cmd_element show_address_vrf_all_cmd; #endif /* _ZEBRA_IF_H */ diff --git a/lib/if_rmap.c b/lib/if_rmap.c index 7d049b872..e4a83de8b 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 071fed6e6..6154c3463 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 44dd1b562..985ac94e0 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 6719cebf7..8a2fdd2ed 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", @@ -381,18 +381,22 @@ key_str2time (const char *time_str, const char *day_str, const char *month_str, NULL }; -#define GET_LONG_RANGE(V,STR,MIN,MAX) \ +#define _GET_LONG_RANGE(V,STR,MMCOND) \ { \ unsigned long tmpl; \ char *endptr = NULL; \ tmpl = strtoul ((STR), &endptr, 10); \ if (*endptr != '\0' || tmpl == ULONG_MAX) \ return -1; \ - if ( tmpl < (MIN) || tmpl > (MAX)) \ + if (MMCOND) \ return -1; \ (V) = tmpl; \ } - +#define GET_LONG_RANGE(V,STR,MIN,MAX) \ + _GET_LONG_RANGE(V,STR,tmpl < (MIN) || tmpl > (MAX)) +#define GET_LONG_RANGE0(V,STR,MAX) \ + _GET_LONG_RANGE(V,STR,tmpl > (MAX)) + /* Check hour field of time_str. */ colon = strchr (time_str, ':'); if (colon == NULL) @@ -400,7 +404,7 @@ key_str2time (const char *time_str, const char *day_str, const char *month_str, *colon = '\0'; /* Hour must be between 0 and 23. */ - GET_LONG_RANGE (hour, time_str, 0, 23); + GET_LONG_RANGE0 (hour, time_str, 23); /* Check min field of time_str. */ time_str = colon + 1; @@ -410,7 +414,7 @@ key_str2time (const char *time_str, const char *day_str, const char *month_str, *colon = '\0'; /* Min must be between 0 and 59. */ - GET_LONG_RANGE (min, time_str, 0, 59); + GET_LONG_RANGE0 (min, time_str, 59); /* Check sec field of time_str. */ time_str = colon + 1; @@ -418,7 +422,7 @@ key_str2time (const char *time_str, const char *day_str, const char *month_str, return -1; /* Sec must be between 0 and 59. */ - GET_LONG_RANGE (sec, time_str, 0, 59); + GET_LONG_RANGE0 (sec, time_str, 59); /* Check day_str. Day must be <1-31>. */ GET_LONG_RANGE (day, day_str, 1, 31); @@ -531,7 +535,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 +693,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 +851,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 000000000..9265ca59f --- /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 5000 /* msec */ +#define OSPF_MIN_LS_ARRIVAL 1000 /* msec */ +#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 0 +#define OSPF_SPF_HOLDTIME_DEFAULT 50 +#define OSPF_SPF_MAX_HOLDTIME_DEFAULT 5000 + +#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 485a80bee..8b6a85282 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,51 @@ listnode_add_after (struct list *list, struct listnode *pp, void *val) list->count++; } +struct listnode * +listnode_add_before (struct list *list, struct listnode *pp, void *val) +{ + struct listnode *nn; + + assert (val != NULL); + + nn = listnode_new (); + nn->data = val; + + if (pp == NULL) + { + if (list->tail) + list->tail->next = nn; + else + list->head = nn; + + nn->prev = list->tail; + nn->next = pp; + + list->tail = nn; + } + else + { + if (pp->prev) + pp->prev->next = nn; + else + list->head = nn; + + nn->prev = pp->prev; + nn->next = pp; + + pp->prev = nn; + } + list->count++; + return nn; +} + +/* 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 +287,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 +303,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 cc6867cd4..96aaf4319 100644 --- a/lib/linklist.h +++ b/lib/linklist.h @@ -54,9 +54,9 @@ struct list void (*del) (void *val); }; -#define listnextnode(X) ((X)->next) -#define listhead(X) ((X)->head) -#define listtail(X) ((X)->tail) +#define listnextnode(X) ((X) ? ((X)->next) : NULL) +#define listhead(X) ((X) ? ((X)->head) : NULL) +#define listtail(X) ((X) ? ((X)->tail) : NULL) #define listcount(X) ((X)->count) #define list_isempty(X) ((X)->head == NULL && (X)->tail == NULL) #define listgetdata(X) (assert((X)->data != NULL), (X)->data) @@ -68,6 +68,8 @@ 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 struct listnode *listnode_add_before (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 *); @@ -88,10 +90,10 @@ extern void list_add_list (struct list *, struct list *); * It is safe to delete the listnode using this macro. */ #define ALL_LIST_ELEMENTS(list,node,nextnode,data) \ - (node) = listhead(list); \ + (node) = listhead(list), ((data) = NULL); \ (node) != NULL && \ - ((data) = listgetdata(node),(nextnode) = listnextnode(node), 1); \ - (node) = (nextnode) + ((data) = listgetdata(node),(nextnode) = node->next, 1); \ + (node) = (nextnode), ((data) = NULL) /* read-only list iteration macro. * Usage: as per ALL_LIST_ELEMENTS, but not safe to delete the listnode Only @@ -100,9 +102,9 @@ extern void list_add_list (struct list *, struct list *); * of previous macro. */ #define ALL_LIST_ELEMENTS_RO(list,node,data) \ - (node) = listhead(list); \ + (node) = listhead(list), ((data) = NULL);\ (node) != NULL && ((data) = listgetdata(node), 1); \ - (node) = listnextnode(node) + (node) = listnextnode(node), ((data) = NULL) /* these *do not* cleanup list nodes and referenced data, as the functions * do - these macros simply {de,at}tach a listnode from/to a list. @@ -112,6 +114,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 cbc935b65..d4370669c 100644 --- a/lib/log.c +++ b/lib/log.c @@ -51,7 +51,9 @@ const char *zlog_proto_names[] = "BABEL", "OSPF6", "ISIS", + "PIM", "MASC", + "NHRP", NULL, }; @@ -69,7 +71,7 @@ const char *zlog_priority[] = }; - + /* For time string format. */ size_t @@ -145,11 +147,12 @@ 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) { + int original_errno = errno; struct timestamp_control tsctl; tsctl.already_rendered = 0; @@ -168,6 +171,7 @@ vzlog (struct zlog *zl, int priority, const char *format, va_list args) fflush (stderr); /* In this case we return at here. */ + errno = original_errno; return; } tsctl.precision = zl->timestamp_precision; @@ -215,6 +219,8 @@ vzlog (struct zlog *zl, int priority, const char *format, va_list args) if (priority <= zl->maxlvl[ZLOG_DEST_MONITOR]) vty_log ((zl->record_priority ? zlog_priority[priority] : NULL), zlog_proto_names[zl->protocol], format, &tsctl, args); + + errno = original_errno; } static char * @@ -425,6 +431,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 +483,8 @@ zlog_backtrace_sigsafe(int priority, void *program_counter) #define LOC s,buf+sizeof(buf)-s #ifdef HAVE_GLIBC_BACKTRACE - if (((size = backtrace(array,sizeof(array)/sizeof(array[0]))) <= 0) || - ((size_t)size > sizeof(array)/sizeof(array[0]))) + size = backtrace(array, array_size(array)); + if (size <= 0 || (size_t)size > array_size(array)) return; #define DUMP(FD) { \ @@ -526,12 +566,12 @@ zlog_backtrace(int priority) int size, i; char **strings; - if (((size = backtrace(array,sizeof(array)/sizeof(array[0]))) <= 0) || - ((size_t)size > sizeof(array)/sizeof(array[0]))) + 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)", - size, (unsigned long)(sizeof(array)/sizeof(array[0]))); + size, (unsigned long)(array_size(array))); return; } zlog(NULL, priority, "Backtrace for %d stack frames:", size); @@ -604,6 +644,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 +666,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, @@ -636,7 +687,7 @@ openzlog (const char *progname, zlog_proto_t protocol, zl->syslog_options = syslog_flags; /* Set default logging levels. */ - for (i = 0; i < sizeof(zl->maxlvl)/sizeof(zl->maxlvl[0]); i++) + for (i = 0; i < array_size(zl->maxlvl); i++) zl->maxlvl[i] = ZLOG_DISABLED; zl->maxlvl[ZLOG_DEST_MONITOR] = LOG_DEBUG; zl->default_lvl = LOG_DEBUG; @@ -756,7 +807,7 @@ zlog_rotate (struct zlog *zl) return 1; } - + /* Message lookup function. */ const char * lookup (const struct message *mes, int key) @@ -845,6 +896,9 @@ static const struct zebra_desc_table command_types[] = { DESC_ENTRY (ZEBRA_ROUTER_ID_DELETE), DESC_ENTRY (ZEBRA_ROUTER_ID_UPDATE), DESC_ENTRY (ZEBRA_HELLO), + DESC_ENTRY (ZEBRA_NEXTHOP_REGISTER), + DESC_ENTRY (ZEBRA_NEXTHOP_UNREGISTER), + DESC_ENTRY (ZEBRA_NEXTHOP_UPDATE), }; #undef DESC_ENTRY @@ -855,14 +909,14 @@ zroute_lookup(u_int zroute) { u_int i; - if (zroute >= sizeof(route_types)/sizeof(route_types[0])) + if (zroute >= array_size(route_types)) { zlog_err("unknown zebra route type: %u", zroute); return &unknown; } if (zroute == route_types[zroute].type) return &route_types[zroute]; - for (i = 0; i < sizeof(route_types)/sizeof(route_types[0]); i++) + for (i = 0; i < array_size(route_types); i++) { if (zroute == route_types[i].type) { @@ -890,7 +944,7 @@ zebra_route_char(u_int zroute) const char * zserv_command_string (unsigned int command) { - if (command >= sizeof(command_types)/sizeof(command_types[0])) + if (command >= array_size(command_types)) { zlog_err ("unknown zserv command type: %u", command); return unknown.string; @@ -898,21 +952,17 @@ zserv_command_string (unsigned int command) return command_types[command].string; } -#define RTSIZE (sizeof(route_types)/sizeof(route_types[0])) - int proto_name2num(const char *s) { unsigned i; - for (i=0; i= len) /* end of block, not really printing */ + s += sprintf(s, " "); + + else if(isprint((int)((char*)mem)[j])) /* printable char */ + s += sprintf(s, "%c", 0xFF & ((char*)mem)[j]); + + else /* other char */ + s += sprintf(s, "."); + } + s += sprintf(s, "\n"); + } + } + zlog_debug("\n%s", buf); +} diff --git a/lib/log.h b/lib/log.h index 27f21b310..59968aeec 100644 --- a/lib/log.h +++ b/lib/log.h @@ -24,6 +24,7 @@ #define _ZEBRA_LOG_H #include +#include /* Here is some guidance on logging levels to use: * @@ -53,7 +54,9 @@ typedef enum ZLOG_BABEL, ZLOG_OSPF6, ZLOG_ISIS, - ZLOG_MASC + ZLOG_PIM, + ZLOG_MASC, + ZLOG_NHRP, } zlog_proto_t; /* If maxlvl is set to ZLOG_DISABLED, then no messages will be sent @@ -132,6 +135,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 +151,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, @@ -182,15 +188,18 @@ extern void zlog_backtrace_sigsafe(int priority, void *program_counter); It caches the most recent localtime result and can therefore avoid multiple calls within the same second. If buflen is too small, *buf will be set to '\0', and 0 will be returned. */ +#define QUAGGA_TIMESTAMP_LEN 40 extern size_t quagga_timestamp(int timestamp_precision /* # subsecond digits */, char *buf, size_t buflen); +extern void zlog_hexdump(void *mem, unsigned int len); + /* structure useful for avoiding repeated rendering of the same timestamp */ struct timestamp_control { size_t len; /* length of rendered timestamp */ int precision; /* configuration parameter */ int already_rendered; /* should be initialized to 0 */ - char buf[40]; /* will contain the rendered timestamp */ + char buf[QUAGGA_TIMESTAMP_LEN]; /* will contain the rendered timestamp */ }; /* Defines for use in command construction: */ diff --git a/lib/md5.c b/lib/md5.c index 2fc36e179..ce459bbe0 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 3ce83a63a..4e5ffbd9e 100644 --- a/lib/md5.h +++ b/lib/md5.h @@ -83,6 +83,7 @@ do { \ } while (0) /* From RFC 2104 */ -void hmac_md5(unsigned char* text, int text_len, unsigned char* key, int key_len, caddr_t digest); +void hmac_md5(unsigned char* text, int text_len, unsigned char* key, + int key_len, uint8_t *digest); #endif /* ! _LIBZEBRA_MD5_H_*/ diff --git a/lib/memory.c b/lib/memory.c index 684ebcff5..b8305dde4 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) @@ -80,9 +80,11 @@ zmalloc (int type, size_t size) /* * Allocate memory as in zmalloc, and also clear the memory. + * Add an extra 'z' prefix to function name to avoid collision when linking + * statically with zlib that exports the 'zcalloc' symbol. */ void * -zcalloc (int type, size_t size) +zzcalloc (int type, size_t size) { void *memory; @@ -97,9 +99,9 @@ zcalloc (int type, size_t size) } /* - * Given a pointer returned by zmalloc or zcalloc, free it and + * Given a pointer returned by zmalloc or zzcalloc, free it and * return a pointer to a new size, basically acting like realloc(). - * Requires: ptr was returned by zmalloc, zcalloc, or zrealloc with the + * Requires: ptr was returned by zmalloc, zzcalloc, or zrealloc with the * same type. * Effects: Returns a pointer to the new memory, or aborts. */ @@ -108,6 +110,9 @@ zrealloc (int type, void *ptr, size_t size) { void *memory; + if (ptr == NULL) /* is really alloc */ + return zzcalloc(type, size); + memory = realloc (ptr, size); if (memory == NULL) zerror ("realloc", type, size); @@ -119,7 +124,7 @@ zrealloc (int type, void *ptr, size_t size) /* * Free memory allocated by z*alloc or zstrdup. - * Requires: ptr was returned by zmalloc, zcalloc, or zrealloc with the + * Requires: ptr was returned by zmalloc, zzcalloc, or zrealloc with the * same type. * Effects: The memory is freed and may no longer be referenced. */ @@ -150,7 +155,7 @@ zstrdup (int type, const char *str) alloc_inc (type); return dup; } - + #ifdef MEMORY_LOG static struct { @@ -193,7 +198,7 @@ mtype_zcalloc (const char *file, int line, int type, size_t size) mstat[type].c_calloc++; mstat[type].t_calloc++; - memory = zcalloc (type, size); + memory = zzcalloc (type, size); mtype_log ("xcalloc", memory, file, line, type); return memory; @@ -259,7 +264,7 @@ alloc_dec (int type) { mstat[type].alloc--; } - + /* Looking up memory status from vty interface. */ #include "vector.h" #include "vty.h" @@ -392,12 +397,11 @@ show_memory_mallinfo (struct vty *vty) } #endif /* HAVE_MALLINFO */ -DEFUN (show_memory_all, - show_memory_all_cmd, - "show memory all", +DEFUN (show_memory, + show_memory_cmd, + "show memory", "Show running system information\n" - "Memory statistics\n" - "All memory statistics\n") + "Memory statistics\n") { struct mlist *ml; int needsep = 0; @@ -416,149 +420,15 @@ DEFUN (show_memory_all, return CMD_SUCCESS; } -ALIAS (show_memory_all, - show_memory_cmd, - "show memory", - "Show running system information\n" - "Memory statistics\n") - -DEFUN (show_memory_lib, - show_memory_lib_cmd, - "show memory lib", - SHOW_STR - "Memory statistics\n" - "Library memory\n") -{ - show_memory_vty (vty, memory_list_lib); - return CMD_SUCCESS; -} - -DEFUN (show_memory_zebra, - show_memory_zebra_cmd, - "show memory zebra", - SHOW_STR - "Memory statistics\n" - "Zebra memory\n") -{ - show_memory_vty (vty, memory_list_zebra); - return CMD_SUCCESS; -} - -DEFUN (show_memory_rip, - show_memory_rip_cmd, - "show memory rip", - SHOW_STR - "Memory statistics\n" - "RIP memory\n") -{ - show_memory_vty (vty, memory_list_rip); - return CMD_SUCCESS; -} - -DEFUN (show_memory_ripng, - show_memory_ripng_cmd, - "show memory ripng", - SHOW_STR - "Memory statistics\n" - "RIPng memory\n") -{ - show_memory_vty (vty, memory_list_ripng); - return CMD_SUCCESS; -} - -DEFUN (show_memory_babel, - show_memory_babel_cmd, - "show memory babel", - SHOW_STR - "Memory statistics\n" - "Babel memory\n") -{ - show_memory_vty (vty, memory_list_babel); - return CMD_SUCCESS; -} - -DEFUN (show_memory_bgp, - show_memory_bgp_cmd, - "show memory bgp", - SHOW_STR - "Memory statistics\n" - "BGP memory\n") -{ - show_memory_vty (vty, memory_list_bgp); - return CMD_SUCCESS; -} - -DEFUN (show_memory_ospf, - show_memory_ospf_cmd, - "show memory ospf", - SHOW_STR - "Memory statistics\n" - "OSPF memory\n") -{ - show_memory_vty (vty, memory_list_ospf); - return CMD_SUCCESS; -} - -DEFUN (show_memory_ospf6, - show_memory_ospf6_cmd, - "show memory ospf6", - SHOW_STR - "Memory statistics\n" - "OSPF6 memory\n") -{ - show_memory_vty (vty, memory_list_ospf6); - return CMD_SUCCESS; -} - -DEFUN (show_memory_isis, - show_memory_isis_cmd, - "show memory isis", - SHOW_STR - "Memory statistics\n" - "ISIS memory\n") -{ - show_memory_vty (vty, memory_list_isis); - return CMD_SUCCESS; -} void memory_init (void) { install_element (RESTRICTED_NODE, &show_memory_cmd); - install_element (RESTRICTED_NODE, &show_memory_all_cmd); - install_element (RESTRICTED_NODE, &show_memory_lib_cmd); - install_element (RESTRICTED_NODE, &show_memory_rip_cmd); - install_element (RESTRICTED_NODE, &show_memory_ripng_cmd); - install_element (RESTRICTED_NODE, &show_memory_babel_cmd); - install_element (RESTRICTED_NODE, &show_memory_bgp_cmd); - install_element (RESTRICTED_NODE, &show_memory_ospf_cmd); - install_element (RESTRICTED_NODE, &show_memory_ospf6_cmd); - install_element (RESTRICTED_NODE, &show_memory_isis_cmd); install_element (VIEW_NODE, &show_memory_cmd); - install_element (VIEW_NODE, &show_memory_all_cmd); - install_element (VIEW_NODE, &show_memory_lib_cmd); - install_element (VIEW_NODE, &show_memory_rip_cmd); - install_element (VIEW_NODE, &show_memory_ripng_cmd); - install_element (VIEW_NODE, &show_memory_babel_cmd); - install_element (VIEW_NODE, &show_memory_bgp_cmd); - 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 (ENABLE_NODE, &show_memory_cmd); - install_element (ENABLE_NODE, &show_memory_all_cmd); - install_element (ENABLE_NODE, &show_memory_lib_cmd); - install_element (ENABLE_NODE, &show_memory_zebra_cmd); - install_element (ENABLE_NODE, &show_memory_rip_cmd); - install_element (ENABLE_NODE, &show_memory_ripng_cmd); - install_element (ENABLE_NODE, &show_memory_babel_cmd); - install_element (ENABLE_NODE, &show_memory_bgp_cmd); - install_element (ENABLE_NODE, &show_memory_ospf_cmd); - install_element (ENABLE_NODE, &show_memory_ospf6_cmd); - install_element (ENABLE_NODE, &show_memory_isis_cmd); } - + /* Stats querying from users */ /* Return a pointer to a human friendly string describing * the byte count passed in. E.g: @@ -570,41 +440,28 @@ memory_init (void) const char * mtype_memstr (char *buf, size_t len, unsigned long bytes) { - unsigned int t, g, m, k; - + unsigned int m, k; + /* easy cases */ if (!bytes) return "0 bytes"; if (bytes == 1) return "1 byte"; - - if (sizeof (unsigned long) >= 8) - /* Hacked to make it not warn on ILP32 machines - * Shift will always be 40 at runtime. See below too */ - t = bytes >> (sizeof (unsigned long) >= 8 ? 40 : 0); - else - t = 0; - g = bytes >> 30; + + /* + * When we pass the 2gb barrier mallinfo() can no longer report + * correct data so it just does something odd... + * Reporting like Terrabytes of data. Which makes users... + * edgy.. yes edgy that's the term for it. + * So let's just give up gracefully + */ + if (bytes > 0x7fffffff) + return "> 2GB"; + m = bytes >> 20; k = bytes >> 10; - - if (t > 10) - { - /* The shift will always be 39 at runtime. - * Just hacked to make it not warn on 'smaller' machines. - * Static compiler analysis should mean no extra code - */ - if (bytes & (1UL << (sizeof (unsigned long) >= 8 ? 39 : 0))) - t++; - snprintf (buf, len, "%4d TiB", t); - } - else if (g > 10) - { - if (bytes & (1 << 29)) - g++; - snprintf (buf, len, "%d GiB", g); - } - else if (m > 10) + + if (m > 10) { if (bytes & (1 << 19)) m++; diff --git a/lib/memory.h b/lib/memory.h index a7eddce4e..501352993 100644 --- a/lib/memory.h +++ b/lib/memory.h @@ -21,6 +21,8 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #ifndef _ZEBRA_MEMORY_H #define _ZEBRA_MEMORY_H +#define array_size(ar) (sizeof(ar) / sizeof(ar[0])) + /* For pretty printing of memory allocate information. */ struct memory_list { @@ -54,7 +56,7 @@ extern struct mlist mlists[]; mtype_zstrdup (__FILE__, __LINE__, (mtype), (str)) #else #define XMALLOC(mtype, size) zmalloc ((mtype), (size)) -#define XCALLOC(mtype, size) zcalloc ((mtype), (size)) +#define XCALLOC(mtype, size) zzcalloc ((mtype), (size)) #define XREALLOC(mtype, ptr, size) zrealloc ((mtype), (ptr), (size)) #define XFREE(mtype, ptr) do { \ zfree ((mtype), (ptr)); \ @@ -65,7 +67,7 @@ extern struct mlist mlists[]; /* Prototypes of memory function. */ extern void *zmalloc (int type, size_t size); -extern void *zcalloc (int type, size_t size); +extern void *zzcalloc (int type, size_t size); extern void *zrealloc (int type, void *ptr, size_t size); extern void zfree (int type, void *ptr); extern char *zstrdup (int type, const char *str); diff --git a/lib/memtypes.awk b/lib/memtypes.awk index 5429f6e8e..bd13327db 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 cd39c9969..e5b35465e 100644 --- a/lib/memtypes.c +++ b/lib/memtypes.c @@ -21,7 +21,6 @@ struct memory_list memory_list_lib[] = { MTYPE_THREAD, "Thread" }, { MTYPE_THREAD_MASTER, "Thread master" }, { MTYPE_THREAD_STATS, "Thread stats" }, - { MTYPE_THREAD_FUNCNAME, "Thread function name" }, { MTYPE_VTY, "VTY" }, { MTYPE_VTY_OUT_BUF, "VTY output buffer" }, { MTYPE_VTY_HIST, "VTY history" }, @@ -55,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" }, @@ -70,19 +69,26 @@ struct memory_list memory_list_lib[] = { MTYPE_PQUEUE, "Priority queue" }, { MTYPE_PQUEUE_DATA, "Priority queue data" }, { MTYPE_HOST, "Host config" }, + { MTYPE_VRF, "VRF" }, + { MTYPE_VRF_NAME, "VRF name" }, + { MTYPE_VRF_BITMAP, "VRF bit-map" }, + { MTYPE_IF_LINK_PARAMS, "Informational Link Parameters" }, { -1, NULL }, }; struct memory_list memory_list_zebra[] = { { MTYPE_RTADV_PREFIX, "Router Advertisement Prefix" }, - { MTYPE_VRF, "VRF" }, - { MTYPE_VRF_NAME, "VRF name" }, + { MTYPE_ZEBRA_VRF, "ZEBRA VRF" }, { MTYPE_NEXTHOP, "Nexthop" }, { MTYPE_RIB, "RIB" }, { MTYPE_RIB_QUEUE, "RIB process work queue" }, - { MTYPE_STATIC_IPV4, "Static IPv4 route" }, - { MTYPE_STATIC_IPV6, "Static IPv6 route" }, + { MTYPE_STATIC_ROUTE, "Static route" }, + { MTYPE_RIB_DEST, "RIB destination" }, + { MTYPE_RIB_TABLE_INFO, "RIB table info" }, + { MTYPE_NETLINK_NAME, "Netlink name" }, + { MTYPE_NETLINK_RCVBUF, "Netlink receive buffer" }, + { MTYPE_RNH, "Nexthop tracking object" }, { -1, NULL }, }; @@ -113,6 +119,7 @@ struct memory_list memory_list_bgp[] = { MTYPE_BGP_SYNCHRONISE, "BGP synchronise" }, { MTYPE_BGP_ADJ_IN, "BGP adj in" }, { MTYPE_BGP_ADJ_OUT, "BGP adj out" }, + { MTYPE_BGP_MPATH_INFO, "BGP multipath info" }, { 0, NULL }, { MTYPE_AS_LIST, "BGP AS list" }, { MTYPE_AS_FILTER, "BGP AS filter" }, @@ -149,6 +156,11 @@ struct memory_list memory_list_bgp[] = { MTYPE_BGP_DAMP_ARRAY, "BGP Dampening array" }, { MTYPE_BGP_REGEXP, "BGP regexp" }, { MTYPE_BGP_AGGREGATE, "BGP aggregate" }, + { MTYPE_BGP_ADDR, "BGP own address" }, + { MTYPE_ENCAP_TLV, "ENCAP TLV", }, + { MTYPE_LCOMMUNITY, "Large Community", }, + { MTYPE_LCOMMUNITY_STR, "Large Community str", }, + { MTYPE_LCOMMUNITY_VAL, "Large Community val", }, { -1, NULL } }; @@ -208,6 +220,8 @@ struct memory_list memory_list_ospf[] = { MTYPE_OSPF_IF_INFO, "OSPF if info" }, { MTYPE_OSPF_IF_PARAMS, "OSPF if params" }, { MTYPE_OSPF_MESSAGE, "OSPF message" }, + { MTYPE_OSPF_MPLS_TE, "OSPF MPLS parameters" }, + { MTYPE_OSPF_PCE_PARAMS, "OSPF PCE parameters" }, { -1, NULL }, }; @@ -228,6 +242,7 @@ struct memory_list memory_list_ospf6[] = { MTYPE_OSPF6_NEXTHOP, "OSPF6 nexthop" }, { MTYPE_OSPF6_EXTERNAL_INFO,"OSPF6 ext. info" }, { MTYPE_OSPF6_OTHER, "OSPF6 other" }, + { MTYPE_OSPF6_DISTANCE, "OSPF6 distance" }, { -1, NULL }, }; @@ -247,9 +262,41 @@ struct memory_list memory_list_isis[] = { MTYPE_ISIS_ROUTE_INFO, "ISIS route info" }, { MTYPE_ISIS_NEXTHOP, "ISIS nexthop" }, { MTYPE_ISIS_NEXTHOP6, "ISIS nexthop6" }, + { MTYPE_ISIS_DICT, "ISIS dictionary" }, + { MTYPE_ISIS_DICT_NODE, "ISIS dictionary node" }, + { MTYPE_ISIS_MPLS_TE, "ISIS MPLS_TE parameters" }, + { -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" }, + { MTYPE_PIM_STATIC_ROUTE, "PIM Static Route" }, { -1, NULL }, }; +struct memory_list memory_list_nhrp[] = +{ + { MTYPE_NHRP_IF, "NHRP interface" }, + { MTYPE_NHRP_VC, "NHRP virtual connection" }, + { MTYPE_NHRP_PEER, "NHRP peer entry" }, + { MTYPE_NHRP_CACHE, "NHRP cache entry" }, + { MTYPE_NHRP_NHS, "NHRP next hop server" }, + { MTYPE_NHRP_REGISTRATION, "NHRP registration entries" }, + { MTYPE_NHRP_SHORTCUT, "NHRP shortcut" }, + { MTYPE_NHRP_ROUTE, "NHRP routing entry" }, + { -1, NULL } +}; + struct memory_list memory_list_vtysh[] = { { MTYPE_VTYSH_CONFIG, "Vtysh configuration", }, @@ -266,5 +313,7 @@ struct mlist mlists[] __attribute__ ((unused)) = { { memory_list_ospf6, "OSPF6" }, { memory_list_isis, "ISIS" }, { memory_list_bgp, "BGP" }, + { memory_list_pim, "PIM" }, + { memory_list_nhrp, "NHRP" }, { NULL, NULL}, }; diff --git a/lib/network.c b/lib/network.c index 3373983b3..b982640e4 100644 --- a/lib/network.c +++ b/lib/network.c @@ -93,3 +93,24 @@ set_nonblocking(int fd) } return 0; } + +float +htonf (float host) +{ +#if !defined(__STDC_IEC_559__) && __GCC_IEC_559 < 0 +#warning "Unknown floating-point format on platform, htonf may break" +#endif + u_int32_t lu1, lu2; + float convert; + + memcpy (&lu1, &host, sizeof (u_int32_t)); + lu2 = htonl (lu1); + memcpy (&convert, &lu2, sizeof (u_int32_t)); + return convert; +} + +float +ntohf (float net) +{ + return htonf (net); +} diff --git a/lib/network.h b/lib/network.h index 4d9c2284b..0fcb575d1 100644 --- a/lib/network.h +++ b/lib/network.h @@ -37,4 +37,7 @@ extern int set_nonblocking(int fd); #define ERRNO_IO_RETRY(EN) \ (((EN) == EAGAIN) || ((EN) == EWOULDBLOCK) || ((EN) == EINTR)) +extern float htonf (float); +extern float ntohf (float); + #endif /* _ZEBRA_NETWORK_H */ diff --git a/lib/nexthop.c b/lib/nexthop.c new file mode 100644 index 000000000..5eb2182de --- /dev/null +++ b/lib/nexthop.c @@ -0,0 +1,168 @@ +/* A generic nexthop structure + * Copyright (C) 2013 Cumulus Networks, 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 "table.h" +#include "memory.h" +#include "str.h" +#include "command.h" +#include "if.h" +#include "log.h" +#include "sockunion.h" +#include "linklist.h" +#include "thread.h" +#include "prefix.h" +#include "nexthop.h" + +/* check if nexthops are same, non-recursive */ +int +nexthop_same_no_recurse (struct nexthop *next1, struct nexthop *next2) +{ + if (next1->type != next2->type) + return 0; + + switch (next1->type) + { + case NEXTHOP_TYPE_IPV4: + case NEXTHOP_TYPE_IPV4_IFINDEX: + if (! IPV4_ADDR_SAME (&next1->gate.ipv4, &next2->gate.ipv4)) + return 0; + if (next1->ifindex && (next1->ifindex != next2->ifindex)) + return 0; + break; + case NEXTHOP_TYPE_IFINDEX: + case NEXTHOP_TYPE_IFNAME: + if (next1->ifindex != next2->ifindex) + return 0; + break; +#ifdef HAVE_IPV6 + case NEXTHOP_TYPE_IPV6: + if (! IPV6_ADDR_SAME (&next1->gate.ipv6, &next2->gate.ipv6)) + return 0; + break; + case NEXTHOP_TYPE_IPV6_IFINDEX: + case NEXTHOP_TYPE_IPV6_IFNAME: + if (! IPV6_ADDR_SAME (&next1->gate.ipv6, &next2->gate.ipv6)) + return 0; + if (next1->ifindex != next2->ifindex) + return 0; + break; +#endif /* HAVE_IPV6 */ + default: + /* do nothing */ + break; + } + return 1; +} + +/* + * 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]; +} + +struct nexthop * +nexthop_new (void) +{ + return XCALLOC (MTYPE_NEXTHOP, sizeof (struct nexthop)); +} + +/* Add nexthop to the end of a nexthop list. */ +void +nexthop_add (struct nexthop **target, struct nexthop *nexthop) +{ + struct nexthop *last; + + for (last = *target; last && last->next; last = last->next) + ; + if (last) + last->next = nexthop; + else + *target = nexthop; + nexthop->prev = last; +} + +void +copy_nexthops (struct nexthop **tnh, struct nexthop *nh) +{ + struct nexthop *nexthop; + struct nexthop *nh1; + + for (nh1 = nh; nh1; nh1 = nh1->next) + { + nexthop = nexthop_new(); + nexthop->flags = nh->flags; + nexthop->type = nh->type; + nexthop->ifindex = nh->ifindex; + if (nh->ifname) + nexthop->ifname = XSTRDUP(0, nh->ifname); + memcpy(&(nexthop->gate), &(nh->gate), sizeof(union g_addr)); + memcpy(&(nexthop->src), &(nh->src), sizeof(union g_addr)); + nexthop_add(tnh, nexthop); + + if (CHECK_FLAG(nh1->flags, NEXTHOP_FLAG_RECURSIVE)) + copy_nexthops(&nexthop->resolved, nh1->resolved); + } +} + +/* Free nexthop. */ +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 */ +void +nexthops_free (struct nexthop *nexthop) +{ + struct nexthop *nh, *next; + + for (nh = nexthop; nh; nh = next) + { + next = nh->next; + nexthop_free (nh); + } +} diff --git a/lib/nexthop.h b/lib/nexthop.h new file mode 100644 index 000000000..0c0dfcaba --- /dev/null +++ b/lib/nexthop.h @@ -0,0 +1,91 @@ +/* + * Nexthop structure definition. + * Copyright (C) 1997, 98, 99, 2001 Kunihiro Ishiguro + * Copyright (C) 2013 Cumulus Networks, 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. + */ + +#ifndef _LIB_NEXTHOP_H +#define _LIB_NEXTHOP_H + +#include "prefix.h" + +union g_addr { + struct in_addr ipv4; +#ifdef HAVE_IPV6 + struct in6_addr ipv6; +#endif /* HAVE_IPV6 */ +}; + +enum nexthop_types_t +{ + NEXTHOP_TYPE_IFINDEX = 1, /* Directly connected. */ + NEXTHOP_TYPE_IFNAME, /* Interface route. */ + NEXTHOP_TYPE_IPV4, /* IPv4 nexthop. */ + NEXTHOP_TYPE_IPV4_IFINDEX, /* IPv4 nexthop with ifindex. */ + NEXTHOP_TYPE_IPV4_IFNAME, /* IPv4 nexthop with ifname. */ + NEXTHOP_TYPE_IPV6, /* IPv6 nexthop. */ + NEXTHOP_TYPE_IPV6_IFINDEX, /* IPv6 nexthop with ifindex. */ + NEXTHOP_TYPE_IPV6_IFNAME, /* IPv6 nexthop with ifname. */ + NEXTHOP_TYPE_BLACKHOLE, /* Null0 nexthop. */ +}; + +/* Nexthop structure. */ +struct nexthop +{ + struct nexthop *next; + struct nexthop *prev; + + /* Interface index. */ + char *ifname; + ifindex_t ifindex; + + enum nexthop_types_t type; + + u_char flags; +#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. */ +#define NEXTHOP_FLAG_MATCHED (1 << 4) /* Already matched vs a nexthop */ + + /* Nexthop address */ + union g_addr gate; + 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; +}; + +struct nexthop *nexthop_new (void); +void nexthop_add (struct nexthop **target, struct nexthop *nexthop); + +void copy_nexthops (struct nexthop **tnh, struct nexthop *nh); +void nexthop_free (struct nexthop *nexthop); +void nexthops_free (struct nexthop *nexthop); + +extern const char *nexthop_type_to_str (enum nexthop_types_t nh_type); +extern int nexthop_same_no_recurse (struct nexthop *next1, struct nexthop *next2); + +#endif /*_LIB_NEXTHOP_H */ diff --git a/lib/plist.c b/lib/plist.c index 0f802a83b..644506b01 100644 --- a/lib/plist.c +++ b/lib/plist.c @@ -30,25 +30,7 @@ #include "stream.h" #include "log.h" -/* Each prefix-list's entry. */ -struct prefix_list_entry -{ - int seq; - - int le; - int ge; - - enum prefix_list_type type; - - int any; - struct prefix prefix; - - unsigned long refcnt; - unsigned long hitcnt; - - struct prefix_list_entry *next; - struct prefix_list_entry *prev; -}; +#include "plist_int.h" /* List of struct prefix_list. */ struct prefix_list_list @@ -102,32 +84,43 @@ static struct prefix_master prefix_master_ipv6 = #endif /* HAVE_IPV6*/ /* Static structure of BGP ORF prefix_list's master. */ -static struct prefix_master prefix_master_orf = -{ +static struct prefix_master prefix_master_orf_v4 = +{ + {NULL, NULL}, + {NULL, NULL}, + 1, + NULL, + NULL, +}; + +/* Static structure of BGP ORF prefix_list's master. */ +static struct prefix_master prefix_master_orf_v6 = +{ {NULL, NULL}, {NULL, NULL}, 1, NULL, NULL, }; - + static struct prefix_master * -prefix_master_get (afi_t afi) +prefix_master_get (afi_t afi, int orf) { if (afi == AFI_IP) - return &prefix_master_ipv4; -#ifdef HAVE_IPV6 - else if (afi == AFI_IP6) - return &prefix_master_ipv6; -#endif /* HAVE_IPV6 */ - else if (afi == AFI_ORF_PREFIX) - return &prefix_master_orf; + return orf ? &prefix_master_orf_v4 : &prefix_master_ipv4; + if (afi == AFI_IP6) + return orf ? &prefix_master_orf_v6 : &prefix_master_ipv6; return NULL; } +const char *prefix_list_name (struct prefix_list *plist) +{ + return plist->name; +} + /* Lookup prefix_list from list of prefix_list by name. */ -struct prefix_list * -prefix_list_lookup (afi_t afi, const char *name) +static struct prefix_list * +prefix_list_lookup_do (afi_t afi, int orf, const char *name) { struct prefix_list *plist; struct prefix_master *master; @@ -135,7 +128,7 @@ prefix_list_lookup (afi_t afi, const char *name) if (name == NULL) return NULL; - master = prefix_master_get (afi); + master = prefix_master_get (afi, orf); if (master == NULL) return NULL; @@ -150,6 +143,18 @@ prefix_list_lookup (afi_t afi, const char *name) return NULL; } +struct prefix_list * +prefix_list_lookup (afi_t afi, const char *name) +{ + return prefix_list_lookup_do (afi, 0, name); +} + +struct prefix_list * +prefix_bgp_orf_lookup (afi_t afi, const char *name) +{ + return prefix_list_lookup_do (afi, 1, name); +} + static struct prefix_list * prefix_list_new (void) { @@ -183,7 +188,7 @@ prefix_list_entry_free (struct prefix_list_entry *pentry) /* Insert new prefix list to list of prefix_list. Each prefix_list is sorted by the name. */ static struct prefix_list * -prefix_list_insert (afi_t afi, const char *name) +prefix_list_insert (afi_t afi, int orf, const char *name) { unsigned int i; long number; @@ -192,7 +197,7 @@ prefix_list_insert (afi_t afi, const char *name) struct prefix_list_list *list; struct prefix_master *master; - master = prefix_master_get (afi); + master = prefix_master_get (afi, orf); if (master == NULL) return NULL; @@ -273,14 +278,14 @@ prefix_list_insert (afi_t afi, const char *name) } static struct prefix_list * -prefix_list_get (afi_t afi, const char *name) +prefix_list_get (afi_t afi, int orf, const char *name) { struct prefix_list *plist; - plist = prefix_list_lookup (afi, name); + plist = prefix_list_lookup_do (afi, orf, name); if (plist == NULL) - plist = prefix_list_insert (afi, name); + plist = prefix_list_insert (afi, orf, name); return plist; } @@ -621,7 +626,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, @@ -670,7 +675,16 @@ vty_prefix_list_install (struct vty *vty, afi_t afi, const char *name, int seqnum = -1; int lenum = 0; int genum = 0; - + + /* This code only works for IP. Provide a safe-guard and user-visible + * warning + */ + if (!(afi == AFI_IP || afi == AFI_IP6)) + { + vty_out (vty, "%% prefix must be IPv4 or IPv6!%s", VTY_NEWLINE); + return CMD_WARNING; + } + /* Sequential number. */ if (seq) seqnum = atoi (seq); @@ -693,8 +707,9 @@ vty_prefix_list_install (struct vty *vty, afi_t afi, const char *name, } /* "any" is special token for matching any IPv4 addresses. */ - if (afi == AFI_IP) + switch (afi) { + case AFI_IP: if (strncmp ("any", prefix, strlen (prefix)) == 0) { ret = str2prefix_ipv4 ("0.0.0.0/0", (struct prefix_ipv4 *) &p); @@ -710,10 +725,8 @@ vty_prefix_list_install (struct vty *vty, afi_t afi, const char *name, vty_out (vty, "%% Malformed IPv4 prefix%s", VTY_NEWLINE); return CMD_WARNING; } - } -#ifdef HAVE_IPV6 - else if (afi == AFI_IP6) - { + break; + case AFI_IP6: if (strncmp ("any", prefix, strlen (prefix)) == 0) { ret = str2prefix_ipv6 ("::/0", (struct prefix_ipv6 *) &p); @@ -729,24 +742,29 @@ vty_prefix_list_install (struct vty *vty, afi_t afi, const char *name, vty_out (vty, "%% Malformed IPv6 prefix%s", VTY_NEWLINE); return CMD_WARNING; } + break; + case AFI_ETHER: + default: + vty_out (vty, "%% Unrecognized AFI (%d)%s", afi, VTY_NEWLINE); + return CMD_WARNING; + break; } -#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. */ - plist = prefix_list_get (afi, name); + plist = prefix_list_get (afi, 0, name); /* Make prefix entry. */ pentry = prefix_list_entry_make (&p, type, seqnum, lenum, genum, any); @@ -1001,7 +1019,7 @@ vty_show_prefix_list (struct vty *vty, afi_t afi, const char *name, struct prefix_master *master; int seqnum = 0; - master = prefix_master_get (afi); + master = prefix_master_get (afi, 0); if (master == NULL) return CMD_WARNING; @@ -1119,7 +1137,7 @@ vty_clear_prefix_list (struct vty *vty, afi_t afi, const char *name, int ret; struct prefix p; - master = prefix_master_get (afi); + master = prefix_master_get (afi, 0); if (master == NULL) return CMD_WARNING; @@ -1165,7 +1183,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)", @@ -1574,7 +1592,7 @@ DEFUN (ip_prefix_list_description, { struct prefix_list *plist; - plist = prefix_list_get (AFI_IP, argv[0]); + plist = prefix_list_get (AFI_IP, 0, argv[0]); if (plist->desc) { @@ -1759,7 +1777,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, @@ -2170,7 +2188,7 @@ DEFUN (ipv6_prefix_list_description, { struct prefix_list *plist; - plist = prefix_list_get (AFI_IP6, argv[0]); + plist = prefix_list_get (AFI_IP6, 0, argv[0]); if (plist->desc) { @@ -2355,7 +2373,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) @@ -2365,7 +2383,7 @@ config_write_prefix_afi (afi_t afi, struct vty *vty) struct prefix_master *master; int write = 0; - master = prefix_master_get (afi); + master = prefix_master_get (afi, 0); if (master == NULL) return 0; @@ -2508,7 +2526,7 @@ prefix_bgp_orf_set (char *name, afi_t afi, struct orf_prefix *orfp, if (orfp->ge && orfp->le == (afi == AFI_IP ? 32 : 128)) orfp->le = 0; - plist = prefix_list_get (AFI_ORF_PREFIX, name); + plist = prefix_list_get (afi, 1, name); if (! plist) return CMD_WARNING; @@ -2542,11 +2560,11 @@ prefix_bgp_orf_set (char *name, afi_t afi, struct orf_prefix *orfp, } void -prefix_bgp_orf_remove_all (char *name) +prefix_bgp_orf_remove_all (afi_t afi, char *name) { struct prefix_list *plist; - plist = prefix_list_lookup (AFI_ORF_PREFIX, name); + plist = prefix_bgp_orf_lookup (afi, name); if (plist) prefix_list_delete (plist); } @@ -2558,7 +2576,7 @@ prefix_bgp_show_prefix_list (struct vty *vty, afi_t afi, char *name) struct prefix_list *plist; struct prefix_list_entry *pentry; - plist = prefix_list_lookup (AFI_ORF_PREFIX, name); + plist = prefix_bgp_orf_lookup (afi, name); if (! plist) return 0; @@ -2590,13 +2608,13 @@ prefix_bgp_show_prefix_list (struct vty *vty, afi_t afi, char *name) } static void -prefix_list_reset_orf (void) +prefix_list_reset_afi (afi_t afi, int orf) { struct prefix_list *plist; struct prefix_list *next; struct prefix_master *master; - master = prefix_master_get (AFI_ORF_PREFIX); + master = prefix_master_get (afi, orf); if (master == NULL) return; @@ -2636,38 +2654,6 @@ config_write_prefix_ipv4 (struct vty *vty) return config_write_prefix_afi (AFI_IP, vty); } -static void -prefix_list_reset_ipv4 (void) -{ - struct prefix_list *plist; - struct prefix_list *next; - struct prefix_master *master; - - master = prefix_master_get (AFI_IP); - if (master == NULL) - return; - - for (plist = master->num.head; plist; plist = next) - { - next = plist->next; - prefix_list_delete (plist); - } - for (plist = master->str.head; plist; plist = next) - { - next = plist->next; - prefix_list_delete (plist); - } - - assert (master->num.head == NULL); - assert (master->num.tail == NULL); - - assert (master->str.head == NULL); - assert (master->str.tail == NULL); - - master->seqnum = 1; - master->recent = NULL; -} - static void prefix_list_init_ipv4 (void) { @@ -2714,23 +2700,11 @@ prefix_list_init_ipv4 (void) install_element (VIEW_NODE, &show_ip_prefix_list_detail_cmd); install_element (VIEW_NODE, &show_ip_prefix_list_detail_name_cmd); - install_element (ENABLE_NODE, &show_ip_prefix_list_cmd); - install_element (ENABLE_NODE, &show_ip_prefix_list_name_cmd); - install_element (ENABLE_NODE, &show_ip_prefix_list_name_seq_cmd); - install_element (ENABLE_NODE, &show_ip_prefix_list_prefix_cmd); - install_element (ENABLE_NODE, &show_ip_prefix_list_prefix_longer_cmd); - install_element (ENABLE_NODE, &show_ip_prefix_list_prefix_first_match_cmd); - install_element (ENABLE_NODE, &show_ip_prefix_list_summary_cmd); - install_element (ENABLE_NODE, &show_ip_prefix_list_summary_name_cmd); - install_element (ENABLE_NODE, &show_ip_prefix_list_detail_cmd); - install_element (ENABLE_NODE, &show_ip_prefix_list_detail_name_cmd); - install_element (ENABLE_NODE, &clear_ip_prefix_list_cmd); install_element (ENABLE_NODE, &clear_ip_prefix_list_name_cmd); install_element (ENABLE_NODE, &clear_ip_prefix_list_name_prefix_cmd); } -#ifdef HAVE_IPV6 /* Prefix-list node. */ static struct cmd_node prefix_ipv6_node = { @@ -2745,38 +2719,6 @@ config_write_prefix_ipv6 (struct vty *vty) return config_write_prefix_afi (AFI_IP6, vty); } -static void -prefix_list_reset_ipv6 (void) -{ - struct prefix_list *plist; - struct prefix_list *next; - struct prefix_master *master; - - master = prefix_master_get (AFI_IP6); - if (master == NULL) - return; - - for (plist = master->num.head; plist; plist = next) - { - next = plist->next; - prefix_list_delete (plist); - } - for (plist = master->str.head; plist; plist = next) - { - next = plist->next; - prefix_list_delete (plist); - } - - assert (master->num.head == NULL); - assert (master->num.tail == NULL); - - assert (master->str.head == NULL); - assert (master->str.tail == NULL); - - master->seqnum = 1; - master->recent = NULL; -} - static void prefix_list_init_ipv6 (void) { @@ -2823,22 +2765,10 @@ prefix_list_init_ipv6 (void) install_element (VIEW_NODE, &show_ipv6_prefix_list_detail_cmd); install_element (VIEW_NODE, &show_ipv6_prefix_list_detail_name_cmd); - install_element (ENABLE_NODE, &show_ipv6_prefix_list_cmd); - install_element (ENABLE_NODE, &show_ipv6_prefix_list_name_cmd); - install_element (ENABLE_NODE, &show_ipv6_prefix_list_name_seq_cmd); - install_element (ENABLE_NODE, &show_ipv6_prefix_list_prefix_cmd); - install_element (ENABLE_NODE, &show_ipv6_prefix_list_prefix_longer_cmd); - install_element (ENABLE_NODE, &show_ipv6_prefix_list_prefix_first_match_cmd); - install_element (ENABLE_NODE, &show_ipv6_prefix_list_summary_cmd); - install_element (ENABLE_NODE, &show_ipv6_prefix_list_summary_name_cmd); - install_element (ENABLE_NODE, &show_ipv6_prefix_list_detail_cmd); - install_element (ENABLE_NODE, &show_ipv6_prefix_list_detail_name_cmd); - install_element (ENABLE_NODE, &clear_ipv6_prefix_list_cmd); install_element (ENABLE_NODE, &clear_ipv6_prefix_list_name_cmd); install_element (ENABLE_NODE, &clear_ipv6_prefix_list_name_prefix_cmd); } -#endif /* HAVE_IPV6 */ void prefix_list_init () @@ -2852,9 +2782,8 @@ prefix_list_init () void prefix_list_reset () { - prefix_list_reset_ipv4 (); -#ifdef HAVE_IPV6 - prefix_list_reset_ipv6 (); -#endif /* HAVE_IPV6 */ - prefix_list_reset_orf (); + prefix_list_reset_afi (AFI_IP, 0); + prefix_list_reset_afi (AFI_IP6, 0); + prefix_list_reset_afi (AFI_IP, 1); + prefix_list_reset_afi (AFI_IP6, 1); } diff --git a/lib/plist.h b/lib/plist.h index fb3168a6e..aa14e74be 100644 --- a/lib/plist.h +++ b/lib/plist.h @@ -23,38 +23,13 @@ #ifndef _QUAGGA_PLIST_H #define _QUAGGA_PLIST_H -#define AFI_ORF_PREFIX 65535 - enum prefix_list_type { PREFIX_DENY, PREFIX_PERMIT, }; -enum prefix_name_type -{ - PREFIX_TYPE_STRING, - PREFIX_TYPE_NUMBER -}; - -struct prefix_list -{ - char *name; - char *desc; - - struct prefix_master *master; - - enum prefix_name_type type; - - int count; - int rangecount; - - struct prefix_list_entry *head; - struct prefix_list_entry *tail; - - struct prefix_list *next; - struct prefix_list *prev; -}; +struct prefix_list; struct orf_prefix { @@ -70,14 +45,16 @@ extern void prefix_list_reset (void); extern void prefix_list_add_hook (void (*func) (struct prefix_list *)); extern void prefix_list_delete_hook (void (*func) (struct prefix_list *)); +extern const char *prefix_list_name (struct prefix_list *); extern struct prefix_list *prefix_list_lookup (afi_t, const char *); extern enum prefix_list_type prefix_list_apply (struct prefix_list *, void *); +extern struct prefix_list *prefix_bgp_orf_lookup (afi_t, const char *); extern struct stream * prefix_bgp_orf_entry (struct stream *, struct prefix_list *, u_char, u_char, u_char); extern int prefix_bgp_orf_set (char *, afi_t, struct orf_prefix *, int, int); -extern void prefix_bgp_orf_remove_all (char *); +extern void prefix_bgp_orf_remove_all (afi_t, char *); extern int prefix_bgp_show_prefix_list (struct vty *, afi_t, char *); #endif /* _QUAGGA_PLIST_H */ diff --git a/lib/plist_int.h b/lib/plist_int.h new file mode 100644 index 000000000..64595797e --- /dev/null +++ b/lib/plist_int.h @@ -0,0 +1,71 @@ +/* + * Prefix list internal definitions. + * 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. + */ + +#ifndef _QUAGGA_PLIST_INT_H +#define _QUAGGA_PLIST_INT_H + +enum prefix_name_type +{ + PREFIX_TYPE_STRING, + PREFIX_TYPE_NUMBER +}; + +struct prefix_list +{ + char *name; + char *desc; + + struct prefix_master *master; + + enum prefix_name_type type; + + int count; + int rangecount; + + struct prefix_list_entry *head; + struct prefix_list_entry *tail; + + struct prefix_list *next; + struct prefix_list *prev; +}; + +/* Each prefix-list's entry. */ +struct prefix_list_entry +{ + int seq; + + int le; + int ge; + + enum prefix_list_type type; + + int any; + struct prefix prefix; + + unsigned long refcnt; + unsigned long hitcnt; + + struct prefix_list_entry *next; + struct prefix_list_entry *prev; +}; + +#endif /* _QUAGGA_PLIST_INT_H */ diff --git a/lib/pqueue.c b/lib/pqueue.c index 12a779f2a..69ab8e65d 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 be37f98da..8bb6961d8 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 a3b1adf8f..43fc31713 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,19 @@ prefix6_bit (const struct in6_addr *prefix, const u_char prefixlen) { return prefix_bit((const u_char *) &prefix->s6_addr, prefixlen); } - + +int +str2family(const char *string) +{ + if (!strcmp("ipv4", string)) + return AF_INET; + else if (!strcmp("ipv6", string)) + return AF_INET6; + else if (!strcmp("ethernet", string)) + return AF_ETHERNET; + return -1; +} + /* Address Famiy Identifier to Address Family converter. */ int afi2family (afi_t afi) @@ -197,6 +209,8 @@ afi2family (afi_t afi) else if (afi == AFI_IP6) return AF_INET6; #endif /* HAVE_IPV6 */ + else if (afi == AFI_ETHER) + return AF_ETHERNET; return 0; } @@ -209,9 +223,41 @@ family2afi (int family) else if (family == AF_INET6) return AFI_IP6; #endif /* HAVE_IPV6 */ + else if (family == AF_ETHERNET) + return AFI_ETHER; return 0; } +const char * +afi2str(afi_t afi) +{ + switch (afi) { + case AFI_IP: + return "IPv4"; + case AFI_IP6: + return "IPv6"; + case AFI_ETHER: + return "ethernet"; + } + return NULL; +} + +const char * +safi2str(safi_t safi) +{ + switch (safi) { + case SAFI_UNICAST: + return "unicast"; + case SAFI_MULTICAST: + return "multicast"; + case SAFI_ENCAP: + return "encap"; + case SAFI_MPLS_VPN: + return "vpn"; + } + return NULL; +} + /* If n includes p prefix then return 1 else return 0. */ int prefix_match (const struct prefix *n, const struct prefix *p) @@ -259,6 +305,10 @@ prefix_copy (struct prefix *dest, const struct prefix *src) dest->u.lp.id = src->u.lp.id; dest->u.lp.adv_router = src->u.lp.adv_router; } + else if (src->family == AF_ETHERNET) + { + dest->u.prefix_eth = src->u.prefix_eth; + } else { zlog (NULL, LOG_ERR, "prefix_copy(): Unknown address family %d", @@ -288,6 +338,10 @@ prefix_same (const struct prefix *p1, const struct prefix *p2) if (IPV6_ADDR_SAME (&p1->u.prefix6.s6_addr, &p2->u.prefix6.s6_addr)) return 1; #endif /* HAVE_IPV6 */ + if (p1->family == AF_ETHERNET) { + if (!memcmp(p1->u.prefix_eth.octet, p2->u.prefix_eth.octet, ETHER_ADDR_LEN)) + return 1; + } } return 0; } @@ -379,6 +433,8 @@ prefix_family_str (const struct prefix *p) if (p->family == AF_INET6) return "inet6"; #endif /* HAVE_IPV6 */ + if (p->family == AF_ETHERNET) + return "ether"; return "unspec"; } @@ -449,6 +505,60 @@ str2prefix_ipv4 (const char *str, struct prefix_ipv4 *p) return ret; } +/* When string format is invalid return 0. */ +int +str2prefix_eth (const char *str, struct prefix_eth *p) +{ + int ret = 0; + int plen = 48; + char *pnt; + char *cp = NULL; + const char *str_addr = str; + unsigned int a[6]; + int i; + + /* Find slash inside string. */ + pnt = strchr (str, '/'); + + if (pnt) + { + /* Get prefix length. */ + plen = (u_char) atoi (++pnt); + if (plen > 48) + { + ret = 0; + goto done; + } + + cp = XMALLOC (MTYPE_TMP, (pnt - str) + 1); + strncpy (cp, str, pnt - str); + *(cp + (pnt - str)) = '\0'; + + str_addr = cp; + } + + /* Convert string to prefix. */ + if (sscanf(str_addr, "%2x:%2x:%2x:%2x:%2x:%2x", + a+0, a+1, a+2, a+3, a+4, a+5) != 6) + { + ret = 0; + goto done; + } + for (i = 0; i < 6; ++i) + { + p->eth_addr.octet[i] = a[i] & 0xff; + } + p->prefixlen = plen; + p->family = AF_ETHERNET; + ret = 1; + +done: + if (cp) + XFREE (MTYPE_TMP, cp); + + return ret; +} + /* Convert masklen into IP address's netmask (network byte order). */ void masklen2ip (const int masklen, struct in_addr *netmask) @@ -494,7 +604,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 */ @@ -539,7 +649,7 @@ str2prefix_ipv6 (const char *str, struct prefix_ipv6 *p) { int plen; - cp = XMALLOC (0, (pnt - str) + 1); + cp = XMALLOC (MTYPE_TMP, (pnt - str) + 1); strncpy (cp, str, pnt - str); *(cp + (pnt - str)) = '\0'; ret = inet_pton (AF_INET6, cp, &p->prefix); @@ -681,13 +791,13 @@ sockunion2prefix (const union sockunion *dest, /* Utility function of convert between struct prefix <=> union sockunion. */ struct prefix * -sockunion2hostprefix (const union sockunion *su) +sockunion2hostprefix (const union sockunion *su, struct prefix *prefix) { if (su->sa.sa_family == AF_INET) { struct prefix_ipv4 *p; - p = prefix_ipv4_new (); + p = prefix ? (struct prefix_ipv4 *) prefix : prefix_ipv4_new (); p->family = AF_INET; p->prefix = su->sin.sin_addr; p->prefixlen = IPV4_MAX_BITLEN; @@ -698,7 +808,7 @@ sockunion2hostprefix (const union sockunion *su) { struct prefix_ipv6 *p; - p = prefix_ipv6_new (); + p = prefix ? (struct prefix_ipv6 *) prefix : prefix_ipv6_new (); p->family = AF_INET6; p->prefixlen = IPV6_MAX_BITLEN; memcpy (&p->prefix, &su->sin6.sin6_addr, sizeof (struct in6_addr)); @@ -735,6 +845,8 @@ prefix_blen (const struct prefix *p) return IPV6_MAX_BYTELEN; break; #endif /* HAVE_IPV6 */ + case AF_ETHERNET: + return ETHER_ADDR_LEN; } return 0; } @@ -757,17 +869,41 @@ str2prefix (const char *str, struct prefix *p) return ret; #endif /* HAVE_IPV6 */ + /* Next we try to convert string to struct prefix_eth. */ + ret = str2prefix_eth (str, (struct prefix_eth *) p); + if (ret) + return ret; + return 0; } -int -prefix2str (const struct prefix *p, char *str, int size) +const char * +prefix2str (union prefix46constptr pu, char *str, int size) { + const struct prefix *p = pu.p; char buf[BUFSIZ]; + if (p->family == AF_ETHERNET) { + int i; + char *s = str; + + assert(size > (3*ETHER_ADDR_LEN) + 1 /* slash */ + 3 /* plen */ ); + for (i = 0; i < ETHER_ADDR_LEN; ++i) { + sprintf(s, "%02x", p->u.prefix_eth.octet[i]); + if (i < (ETHER_ADDR_LEN - 1)) { + *(s+2) = ':'; + s += 3; + } else { + s += 2; + } + } + sprintf(s, "/%d", p->prefixlen); + return 0; + } + inet_ntop (p->family, &p->u.prefix, buf, BUFSIZ); snprintf (str, size, "%s/%d", buf, p->prefixlen); - return 0; + return str; } struct prefix * diff --git a/lib/prefix.h b/lib/prefix.h index 7f0d36070..2cf0b20b0 100644 --- a/lib/prefix.h +++ b/lib/prefix.h @@ -23,8 +23,30 @@ #ifndef _ZEBRA_PREFIX_H #define _ZEBRA_PREFIX_H +#ifdef SUNOS_5 +# include +#else +# ifdef GNU_LINUX +# include +# else +# include +# endif +#endif #include "sockunion.h" +#ifndef ETHER_ADDR_LEN +#define ETHER_ADDR_LEN ETHERADDRL +#endif + +/* + * there isn't a portable ethernet address type. We define our + * own to simplify internal handling + */ +struct ethaddr { + u_char octet[ETHER_ADDR_LEN]; +} __packed; + + /* * A struct prefix contains an address family, a prefix length, and an * address. This can represent either a 'network prefix' as defined @@ -34,6 +56,15 @@ * interface. */ +/* different OSes use different names */ +#if defined(AF_PACKET) +#define AF_ETHERNET AF_PACKET +#else +#if defined(AF_LINK) +#define AF_ETHERNET AF_LINK +#endif +#endif + /* IPv4 and IPv6 unified prefix structure. */ struct prefix { @@ -51,7 +82,9 @@ struct prefix struct in_addr id; struct in_addr adv_router; } lp; + struct ethaddr prefix_eth; /* AF_ETHERNET */ u_char val[8]; + uintptr_t ptr; } u __attribute__ ((aligned (8))); }; @@ -89,6 +122,41 @@ struct prefix_rd u_char val[8] __attribute__ ((aligned (8))); }; +/* Prefix for ethernet. */ +struct prefix_eth +{ + u_char family; + u_char prefixlen; + struct ethaddr eth_addr __attribute__ ((aligned (8))); /* AF_ETHERNET */ +}; + +/* 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 */ @@ -101,6 +169,9 @@ struct prefix_rd #define INET6_BUFSIZ 51 #endif /* INET6_BUFSIZ */ +/* Maximum prefix string length (IPv6) */ +#define PREFIX_STRLEN 51 + /* Max bit/byte length of IPv4 address. */ #define IPV4_MAX_BYTELEN 4 #define IPV4_MAX_BITLEN 32 @@ -128,9 +199,22 @@ struct prefix_rd /* Prefix's family member. */ #define PREFIX_FAMILY(p) ((p)->family) +/* glibc defines s6_addr32 to __in6_u.__u6_addr32 if __USE_{MISC || GNU} */ +#ifndef s6_addr32 +#if defined(SUNOS_5) +/* Some SunOS define s6_addr32 only to kernel */ +#define s6_addr32 _S6_un._S6_u32 +#else +#define s6_addr32 __u6_addr.__u6_addr32 +#endif /* SUNOS_5 */ +#endif /*s6_addr32*/ + /* Prototypes. */ +extern int str2family(const char *); extern int afi2family (afi_t); extern afi_t family2afi (int); +extern const char *safi2str(safi_t safi); +extern const char *afi2str(afi_t afi); /* Check bit of the prefix. */ extern unsigned int prefix_bit (const u_char *prefix, const u_char prefixlen); @@ -141,7 +225,7 @@ extern void prefix_free (struct prefix *); extern const char *prefix_family_str (const struct prefix *); extern int prefix_blen (const struct prefix *); extern int str2prefix (const char *, struct prefix *); -extern int prefix2str (const struct prefix *, char *, int); +extern const char *prefix2str (union prefix46constptr, char *, int); extern int prefix_match (const struct prefix *, const struct prefix *); extern int prefix_same (const struct prefix *, const struct prefix *); extern int prefix_cmp (const struct prefix *, const struct prefix *); @@ -151,9 +235,11 @@ extern void apply_mask (struct prefix *); extern struct prefix *sockunion2prefix (const union sockunion *dest, const union sockunion *mask); -extern struct prefix *sockunion2hostprefix (const union sockunion *); +extern struct prefix *sockunion2hostprefix (const union sockunion *, struct prefix *p); extern void prefix2sockunion (const struct prefix *, union sockunion *); +extern int str2prefix_eth (const char *, struct prefix_eth *); + extern struct prefix_ipv4 *prefix_ipv4_new (void); extern void prefix_ipv4_free (struct prefix_ipv4 *); extern int str2prefix_ipv4 (const char *, struct prefix_ipv4 *); @@ -196,4 +282,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 69606f57c..3fb96aed1 100644 --- a/lib/privs.c +++ b/lib/privs.c @@ -2,7 +2,7 @@ * Zebra privileges. * * Copyright (C) 2003 Paul Jakma. - * Copyright (C) 2005 Sun Microsystems, Inc. + * Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved. * * This file is part of GNU Zebra. * @@ -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. */ @@ -102,8 +102,7 @@ static struct #ifdef HAVE_LCAPS /* Quagga -> Linux capabilities mappings */ [ZCAP_SETID] = { 2, (pvalue_t []) { CAP_SETGID, CAP_SETUID }, }, - [ZCAP_BIND] = { 2, (pvalue_t []) { CAP_NET_BIND_SERVICE, - CAP_NET_BROADCAST }, }, + [ZCAP_BIND] = { 2, (pvalue_t []) { CAP_NET_BIND_SERVICE }, }, [ZCAP_NET_ADMIN] = { 1, (pvalue_t []) { CAP_NET_ADMIN }, }, [ZCAP_NET_RAW] = { 1, (pvalue_t []) { CAP_NET_RAW }, }, [ZCAP_CHROOT] = { 1, (pvalue_t []) { CAP_SYS_CHROOT, }, }, @@ -139,7 +138,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 */ @@ -299,7 +298,28 @@ zprivs_caps_init (struct zebra_privs_t *zprivs) */ if ( cap_set_proc (zprivs_state.caps) ) { - fprintf (stderr, "privs_init: initial cap_set_proc failed\n"); + cap_t current_caps; + char *current_caps_text = NULL; + char *wanted_caps_text = NULL; + + fprintf(stderr, "privs_init: initial cap_set_proc failed: %s\n", + safe_strerror(errno)); + + current_caps = cap_get_proc(); + if (current_caps) + { + current_caps_text = cap_to_text(current_caps, NULL); + cap_free(current_caps); + } + + wanted_caps_text = cap_to_text(zprivs_state.caps, NULL); + fprintf(stderr, "Wanted caps: %s\n", wanted_caps_text ? wanted_caps_text : "???"); + fprintf(stderr, "Have caps: %s\n", current_caps_text ? current_caps_text : "???"); + if (current_caps_text) + cap_free(current_caps_text); + if (wanted_caps_text) + cap_free(wanted_caps_text); + exit (1); } @@ -339,7 +359,7 @@ zprivs_caps_terminate (void) cap_free (zprivs_state.caps); } #elif defined (HAVE_SOLARIS_CAPABILITIES) /* !HAVE_LCAPS */ - + /* Solaris specific capability/privilege methods * * Resources: @@ -348,6 +368,26 @@ zprivs_caps_terminate (void) * - http://blogs.sun.com/roller/page/gbrunett?entry=privilege_enabling_set_id_programs1 */ +static pset_t * +zprivs_caps_minimal () +{ + pset_t *minimal; + + if ((minimal = priv_str_to_set("basic", ",", NULL)) == NULL) + { + fprintf (stderr, "%s: couldn't get basic set!\n", __func__); + exit (1); + } + + /* create a minimal privilege set from the basic set */ + (void) priv_delset(minimal, PRIV_PROC_EXEC); + (void) priv_delset(minimal, PRIV_PROC_INFO); + (void) priv_delset(minimal, PRIV_PROC_SESSION); + (void) priv_delset(minimal, PRIV_FILE_LINK_ANY); + + return minimal; +} + /* convert zebras privileges to system capabilities */ static pset_t * zcaps2sys (zebra_capabilities_t *zcaps, int num) @@ -376,26 +416,34 @@ zcaps2sys (zebra_capabilities_t *zcaps, int num) int zprivs_change_caps (zebra_privs_ops_t op) { + pset_t *privset; /* should be no possibility of being called without valid caps */ assert (zprivs_state.syscaps_p); if (!zprivs_state.syscaps_p) + { + fprintf (stderr, "%s: Eek, missing privileged caps!", __func__); + exit (1); + } + + assert (zprivs_state.caps); + if (!zprivs_state.caps) { fprintf (stderr, "%s: Eek, missing caps!", __func__); exit (1); } - - /* to raise: copy original permitted into our working effective set - * to lower: just clear the working effective set + + /* to raise: copy original permitted as our working effective set + * to lower: copy regular effective set stored in zprivs_state.caps */ if (op == ZPRIVS_RAISE) - priv_copyset (zprivs_state.syscaps_p, zprivs_state.caps); + privset = zprivs_state.syscaps_p; else if (op == ZPRIVS_LOWER) - priv_emptyset (zprivs_state.caps); + privset = zprivs_state.caps; else return -1; - if (setppriv (PRIV_SET, PRIV_EFFECTIVE, zprivs_state.caps) != 0) + if (setppriv (PRIV_SET, PRIV_EFFECTIVE, privset) != 0) return -1; return 0; @@ -423,15 +471,15 @@ zprivs_state_caps (void) } else { - if (priv_isemptyset (effective) == B_TRUE) + if (priv_isequalset (effective, zprivs_state.syscaps_p)) + result = ZPRIVS_RAISED; + else if (priv_isequalset (effective, zprivs_state.caps)) result = ZPRIVS_LOWERED; else - result = ZPRIVS_RAISED; + result = ZPRIVS_UNKNOWN; } - if (effective) - priv_freeset (effective); - + priv_freeset (effective); return result; } @@ -439,7 +487,7 @@ static void zprivs_caps_init (struct zebra_privs_t *zprivs) { pset_t *basic; - pset_t *empty; + pset_t *minimal; /* the specified sets */ zprivs_state.syscaps_p = zcaps2sys (zprivs->caps_p, zprivs->cap_num_p); @@ -467,14 +515,6 @@ zprivs_caps_init (struct zebra_privs_t *zprivs) priv_union (basic, zprivs_state.syscaps_p); priv_freeset (basic); - /* we need an empty set for 'effective', potentially for inheritable too */ - if ( (empty = priv_allocset()) == NULL) - { - fprintf (stderr, "%s: couldn't get empty set!\n", __func__); - exit (1); - } - priv_emptyset (empty); - /* Hey kernel, we know about privileges! * this isn't strictly required, use of setppriv should have same effect */ @@ -517,16 +557,19 @@ zprivs_caps_init (struct zebra_privs_t *zprivs) exit (1); } - /* now clear the effective set and we're ready to go */ - if (setppriv (PRIV_SET, PRIV_EFFECTIVE, empty)) + /* we need a minimal basic set for 'effective', potentially for inheritable too */ + minimal = zprivs_caps_minimal(); + + /* now set the effective set with a subset of basic privileges */ + if (setppriv (PRIV_SET, PRIV_EFFECTIVE, minimal)) { fprintf (stderr, "%s: error setting effective set!, %s\n", __func__, safe_strerror (errno) ); exit (1); } - /* we'll use this as our working-storage privset */ - zprivs_state.caps = empty; + /* we'll use the minimal set as our working-storage privset */ + zprivs_state.caps = minimal; /* set methods for the caller to use */ zprivs->change = zprivs_change_caps; @@ -538,8 +581,7 @@ zprivs_caps_terminate (void) { assert (zprivs_state.caps); - /* clear all capabilities */ - priv_emptyset (zprivs_state.caps); + /* clear all capabilities by using working-storage privset */ setppriv (PRIV_SET, PRIV_EFFECTIVE, zprivs_state.caps); setppriv (PRIV_SET, PRIV_PERMITTED, zprivs_state.caps); setppriv (PRIV_SET, PRIV_INHERITABLE, zprivs_state.caps); @@ -556,7 +598,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) { @@ -587,11 +629,49 @@ zprivs_state_null (void) return zprivs_null_state; } +#ifndef HAVE_GETGROUPLIST +/* Solaris 11 has no getgrouplist() */ +static int +getgrouplist(const char *user, gid_t group, gid_t *groups, int *ngroups) +{ + struct group *grp; + size_t usridx; + int pos = 0, ret; + + if (pos < *ngroups) + groups[pos] = group; + pos++; + + setgrent(); + while ((grp = getgrent())) + { + if (grp->gr_gid == group) + continue; + for (usridx = 0; grp->gr_mem[usridx] != NULL; usridx++) + if (!strcmp (grp->gr_mem[usridx], user)) + { + if (pos < *ngroups) + groups[pos] = grp->gr_gid; + pos++; + break; + } + } + endgrent(); + + ret = (pos <= *ngroups) ? pos : -1; + *ngroups = pos; + return ret; +} +#endif /* HAVE_GETGROUPLIST */ + void zprivs_init(struct zebra_privs_t *zprivs) { struct passwd *pwentry = NULL; struct group *grentry = NULL; + gid_t groups[NGROUPS_MAX]; + int i, ngroups = 0; + int found = 0; if (!zprivs) { @@ -610,33 +690,68 @@ zprivs_init(struct zebra_privs_t *zprivs) if (zprivs->user) { - if ( (pwentry = getpwnam (zprivs->user)) ) - { - zprivs_state.zuid = pwentry->pw_uid; - } - else + if ( (pwentry = getpwnam (zprivs->user)) == NULL ) { /* cant use log.h here as it depends on vty */ fprintf (stderr, "privs_init: could not lookup user %s\n", zprivs->user); exit (1); } + + zprivs_state.zuid = pwentry->pw_uid; + zprivs_state.zgid = pwentry->pw_gid; } grentry = NULL; + if (zprivs->group) + { + if ( (grentry = getgrnam (zprivs->group)) == NULL ) + { + fprintf (stderr, "privs_init: could not lookup group %s\n", + zprivs->group); + exit (1); + } + + zprivs_state.zgid = grentry->gr_gid; + } + + if (zprivs->user) + { + ngroups = sizeof(groups); + if ( (ngroups = getgrouplist (zprivs->user, zprivs_state.zgid, groups, &ngroups )) < 0 ) + { + /* cant use log.h here as it depends on vty */ + fprintf (stderr, "privs_init: could not getgrouplist for user %s\n", + zprivs->user); + exit (1); + } + } + if (zprivs->vty_group) /* Add the vty_group to the supplementary groups so it can be chowned to */ { if ( (grentry = getgrnam (zprivs->vty_group)) ) { zprivs_state.vtygrp = grentry->gr_gid; - if ( setgroups (1, &zprivs_state.vtygrp) ) + + for ( i = 0; i < ngroups; i++ ) + if ( groups[i] == zprivs_state.vtygrp ) + { + found++; + break; + } + + if (!found) { - fprintf (stderr, "privs_init: could not setgroups, %s\n", - safe_strerror (errno) ); + fprintf (stderr, "privs_init: user(%s) is not part of vty group specified(%s)\n", + zprivs->user, zprivs->vty_group); exit (1); - } + } + if ( i >= ngroups && ngroups < (int) ZEBRA_NUM_OF(groups) ) + { + groups[i] = zprivs_state.vtygrp; + } } else { @@ -645,19 +760,19 @@ zprivs_init(struct zebra_privs_t *zprivs) exit (1); } } - - if (zprivs->group) + + if (ngroups) { - if ( (grentry = getgrnam (zprivs->group)) ) + if ( setgroups (ngroups, groups) ) { - zprivs_state.zgid = grentry->gr_gid; - } - else - { - fprintf (stderr, "privs_init: could not lookup group %s\n", - zprivs->group); + fprintf (stderr, "privs_init: could not setgroups, %s\n", + safe_strerror (errno) ); exit (1); } + } + + if (zprivs_state.zgid) + { /* change group now, forever. uid we do later */ if ( setregid (zprivs_state.zgid, zprivs_state.zgid) ) { diff --git a/lib/queue.h b/lib/queue.h new file mode 100644 index 000000000..48b363e24 --- /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 d88ab92bd..4cee464f2 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 a22e03f6b..122f44764 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 cebf01fca..811d24e09 100644 --- a/lib/route_types.txt +++ b/lib/route_types.txt @@ -51,6 +51,7 @@ 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" +ZEBRA_ROUTE_PIM, pim, pimd, 'P', 1, 0, "PIM" # 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 @@ -59,6 +60,7 @@ ZEBRA_ROUTE_BGP, bgp, bgpd, 'B', 1, 1, "BGP" ZEBRA_ROUTE_HSLS, hsls, hslsd, 'H', 0, 0, "HSLS" ZEBRA_ROUTE_OLSR, olsr, olsrd, 'o', 0, 0, "OLSR" ZEBRA_ROUTE_BABEL, babel, babeld, 'A', 1, 1, "Babel" +ZEBRA_ROUTE_NHRP, nhrp, nhrpd, 'N', 1, 1, "NHRP" ## help strings ZEBRA_ROUTE_SYSTEM, "Reserved route type, for internal use only" @@ -71,6 +73,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_BABEL, "Babel routing protocol (Babel)" +ZEBRA_ROUTE_NHRP, "Next Hop Resolution Protocol (NHRP)" diff --git a/lib/routemap.c b/lib/routemap.c index 4f4e6d620..c39222670 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) @@ -897,8 +897,11 @@ route_map_finish (void) route_match_vec = NULL; vector_free (route_set_vec); route_set_vec = NULL; + /* cleanup route_map */ + while (route_map_master.head) + route_map_delete (route_map_master.head); } - + /* VTY related functions. */ DEFUN (route_map, route_map_cmd, @@ -1296,6 +1299,32 @@ static struct cmd_node rmap_node = 1 }; +/* Common route map rules */ + +void * +route_map_rule_tag_compile (const char *arg) +{ + unsigned long int tmp; + char *endptr; + route_tag_t *tag; + + errno = 0; + tmp = strtoul(arg, &endptr, 0); + if (arg[0] == '\0' || *endptr != '\0' || errno || tmp > ROUTE_TAG_MAX) + return NULL; + + tag = XMALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(*tag)); + *tag = tmp; + + return tag; +} + +void +route_map_rule_tag_free (void *rule) +{ + XFREE (MTYPE_ROUTE_MAP_COMPILED, rule); +} + /* Initialization of route map vector. */ void route_map_init_vty (void) diff --git a/lib/routemap.h b/lib/routemap.h index ba64553f3..68129e1d9 100644 --- a/lib/routemap.h +++ b/lib/routemap.h @@ -22,6 +22,8 @@ #ifndef _ZEBRA_ROUTEMAP_H #define _ZEBRA_ROUTEMAP_H +#include "prefix.h" + /* Route map's type. */ enum route_map_type { @@ -47,7 +49,8 @@ typedef enum RMAP_OSPF, RMAP_OSPF6, RMAP_BGP, - RMAP_ZEBRA + RMAP_ZEBRA, + RMAP_ISIS, } route_map_object_t; typedef enum @@ -195,4 +198,7 @@ extern void route_map_add_hook (void (*func) (const char *)); extern void route_map_delete_hook (void (*func) (const char *)); extern void route_map_event_hook (void (*func) (route_map_event_t, const char *)); +extern void *route_map_rule_tag_compile (const char *arg); +extern void route_map_rule_tag_free (void *rule); + #endif /* _ZEBRA_ROUTEMAP_H */ diff --git a/lib/sigevent.c b/lib/sigevent.c index 30e9a3d10..a120028d8 100644 --- a/lib/sigevent.c +++ b/lib/sigevent.c @@ -22,6 +22,7 @@ #include #include #include +#include #ifdef SA_SIGINFO #ifdef HAVE_UCONTEXT_H @@ -174,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; } @@ -212,6 +237,8 @@ core_handler(int signo , siginfo, program_counter(context) #endif ); + /* dump memory stats on core */ + log_memstats_stderr ("core_handler"); abort(); } @@ -266,13 +293,13 @@ trap_default_signals(void) #endif ); } sigmap[] = { - { core_signals, sizeof(core_signals)/sizeof(core_signals[0]), core_handler}, - { exit_signals, sizeof(exit_signals)/sizeof(exit_signals[0]), exit_handler}, - { ignore_signals, sizeof(ignore_signals)/sizeof(ignore_signals[0]), NULL}, + { core_signals, array_size(core_signals), core_handler}, + { exit_signals, array_size(exit_signals), exit_handler}, + { ignore_signals, array_size(ignore_signals), NULL}, }; u_int i; - for (i = 0; i < sizeof(sigmap)/sizeof(sigmap[0]); i++) + for (i = 0; i < array_size(sigmap); i++) { u_int j; diff --git a/lib/sigevent.h b/lib/sigevent.h index 62b944a7c..248fa2c05 100644 --- a/lib/sigevent.h +++ b/lib/sigevent.h @@ -27,7 +27,6 @@ #include #define QUAGGA_SIGNAL_TIMER_INTERVAL 2L -#define Q_SIGC(sig) (sizeof(sig)/sizeof(sig[0])) struct quagga_signal_t { diff --git a/lib/smux.c b/lib/smux.c index b7cd18d14..03da99f94 100644 --- a/lib/smux.c +++ b/lib/smux.c @@ -21,15 +21,9 @@ #include -#ifdef HAVE_SNMP -#ifdef HAVE_NETSNMP +#if defined HAVE_SNMP && defined SNMP_SMUX #include #include -#else -#include -#include -#include -#endif #include "log.h" #include "thread.h" @@ -40,12 +34,51 @@ #include "sockunion.h" #include "smux.h" +#define SMUX_PORT_DEFAULT 199 + +#define SMUXMAXPKTSIZE 1500 +#define SMUXMAXSTRLEN 256 + +#define SMUX_OPEN (ASN_APPLICATION | ASN_CONSTRUCTOR | 0) +#define SMUX_CLOSE (ASN_APPLICATION | ASN_PRIMITIVE | 1) +#define SMUX_RREQ (ASN_APPLICATION | ASN_CONSTRUCTOR | 2) +#define SMUX_RRSP (ASN_APPLICATION | ASN_PRIMITIVE | 3) +#define SMUX_SOUT (ASN_APPLICATION | ASN_PRIMITIVE | 4) + +#define SMUX_GET (ASN_CONTEXT | ASN_CONSTRUCTOR | 0) +#define SMUX_GETNEXT (ASN_CONTEXT | ASN_CONSTRUCTOR | 1) +#define SMUX_GETRSP (ASN_CONTEXT | ASN_CONSTRUCTOR | 2) +#define SMUX_SET (ASN_CONTEXT | ASN_CONSTRUCTOR | 3) +#define SMUX_TRAP (ASN_CONTEXT | ASN_CONSTRUCTOR | 4) + +#define SMUX_MAX_FAILURE 3 + +/* SNMP tree. */ +struct subtree +{ + /* Tree's oid. */ + oid name[MAX_OID_LEN]; + u_char name_len; + + /* List of the variables. */ + struct variable *variables; + + /* Length of the variables list. */ + int variables_num; + + /* Width of the variables list. */ + int variables_width; + + /* Registered flag. */ + int registered; +}; + #define min(A,B) ((A) < (B) ? (A) : (B)) enum smux_event {SMUX_SCHEDULE, SMUX_CONNECT, SMUX_READ}; void smux_event (enum smux_event, int); - + /* SMUX socket. */ int smux_sock = -1; @@ -80,63 +113,7 @@ static struct cmd_node smux_node = }; /* thread master */ -static struct thread_master *master; - -void * -oid_copy (void *dest, const void *src, size_t size) -{ - return memcpy (dest, src, size * sizeof (oid)); -} - -void -oid2in_addr (oid oid[], int len, struct in_addr *addr) -{ - int i; - u_char *pnt; - - if (len == 0) - return; - - pnt = (u_char *) addr; - - for (i = 0; i < len; i++) - *pnt++ = oid[i]; -} - -void -oid_copy_addr (oid oid[], struct in_addr *addr, int len) -{ - int i; - u_char *pnt; - - if (len == 0) - return; - - pnt = (u_char *) addr; - - for (i = 0; i < len; i++) - oid[i] = *pnt++; -} - -int -oid_compare (oid *o1, int o1_len, oid *o2, int o2_len) -{ - int i; - - for (i = 0; i < min (o1_len, o2_len); i++) - { - if (o1[i] < o2[i]) - return -1; - else if (o1[i] > o2[i]) - return 1; - } - if (o1_len < o2_len) - return -1; - if (o1_len > o2_len) - return 1; - - return 0; -} +static struct thread_master *smux_master; static int oid_compare_part (oid *o1, int o1_len, oid *o2, int o2_len) @@ -155,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) { @@ -479,7 +456,7 @@ smux_set (oid *reqid, size_t *reqid_len, if (write_method) { return (*write_method)(action, val, val_type, val_len, - statP, suffix, suffix_len, v); + statP, suffix, suffix_len); } else { @@ -991,11 +968,18 @@ smux_open (int sock) return send (sock, buf, (ptr - buf), 0); } +/* `ename` is ignored. Instead of using the provided enterprise OID, + the SMUX peer is used. This keep compatibility with the previous + versions of Quagga. + + All other fields are used as they are intended. */ int -smux_trap (const oid *name, size_t namelen, +smux_trap (struct variable *vp, size_t vp_len, + const oid *ename, size_t enamelen, + const oid *name, size_t namelen, const oid *iname, size_t inamelen, const struct trap_object *trapobj, size_t trapobjlen, - unsigned int tick, u_char sptrap) + u_char sptrap) { unsigned int i; u_char buf[BUFSIZ]; @@ -1246,7 +1230,7 @@ smux_stop (void) smux_sock = -1; } } - + void @@ -1255,19 +1239,19 @@ smux_event (enum smux_event event, int sock) switch (event) { case SMUX_SCHEDULE: - smux_connect_thread = thread_add_event (master, smux_connect, NULL, 0); + smux_connect_thread = thread_add_event (smux_master, smux_connect, NULL, 0); break; case SMUX_CONNECT: - smux_connect_thread = thread_add_timer (master, smux_connect, NULL, 10); + smux_connect_thread = thread_add_timer (smux_master, smux_connect, NULL, 10); break; case SMUX_READ: - smux_read_thread = thread_add_read (master, smux_read, NULL, sock); + smux_read_thread = thread_add_read (smux_master, smux_read, NULL, sock); break; default: break; } } - + static int smux_str2oid (const char *str, oid *oid, size_t *oid_len) { @@ -1360,32 +1344,6 @@ smux_peer_oid (struct vty *vty, const char *oid_str, const char *passwd_str) return 0; } -int -smux_header_generic (struct variable *v, oid *name, size_t *length, int exact, - size_t *var_len, WriteMethod **write_method) -{ - oid fulloid[MAX_OID_LEN]; - int ret; - - oid_copy (fulloid, v->name, v->namelen); - fulloid[v->namelen] = 0; - /* Check against full instance. */ - ret = oid_compare (name, *length, fulloid, v->namelen + 1); - - /* Check single instance. */ - if ((exact && (ret != 0)) || (!exact && (ret >= 0))) - return MATCH_FAILED; - - /* In case of getnext, fill in full instance. */ - memcpy (name, fulloid, (v->namelen + 1) * sizeof (oid)); - *length = v->namelen + 1; - - *write_method = 0; - *var_len = sizeof(long); /* default to 'long' results */ - - return MATCH_SUCCEEDED; -} - static int smux_peer_default (void) { @@ -1516,7 +1474,7 @@ void smux_init (struct thread_master *tm) { /* copy callers thread master */ - master = tm; + smux_master = tm; /* Make MIB tree. */ treelist = list_new(); diff --git a/lib/smux.h b/lib/smux.h index f5754ed90..dc91cac71 100644 --- a/lib/smux.h +++ b/lib/smux.h @@ -22,24 +22,10 @@ #ifndef _ZEBRA_SNMP_H #define _ZEBRA_SNMP_H -#define SMUX_PORT_DEFAULT 199 +#include +#include -#define SMUXMAXPKTSIZE 1500 -#define SMUXMAXSTRLEN 256 - -#define SMUX_OPEN (ASN_APPLICATION | ASN_CONSTRUCTOR | 0) -#define SMUX_CLOSE (ASN_APPLICATION | ASN_PRIMITIVE | 1) -#define SMUX_RREQ (ASN_APPLICATION | ASN_CONSTRUCTOR | 2) -#define SMUX_RRSP (ASN_APPLICATION | ASN_PRIMITIVE | 3) -#define SMUX_SOUT (ASN_APPLICATION | ASN_PRIMITIVE | 4) - -#define SMUX_GET (ASN_CONTEXT | ASN_CONSTRUCTOR | 0) -#define SMUX_GETNEXT (ASN_CONTEXT | ASN_CONSTRUCTOR | 1) -#define SMUX_GETRSP (ASN_CONTEXT | ASN_CONSTRUCTOR | 2) -#define SMUX_SET (ASN_CONTEXT | ASN_CONSTRUCTOR | 3) -#define SMUX_TRAP (ASN_CONTEXT | ASN_CONSTRUCTOR | 4) - -#define SMUX_MAX_FAILURE 3 +#include "thread.h" /* Structures here are mostly compatible with UCD SNMP 4.1.1 */ #define MATCH_FAILED (-1) @@ -55,80 +41,22 @@ #define IN_ADDR_SIZE sizeof(struct in_addr) -struct variable; - +#undef REGISTER_MIB #define REGISTER_MIB(descr, var, vartype, theoid) \ smux_register_mib(descr, (struct variable *)var, sizeof(struct vartype), \ sizeof(var)/sizeof(struct vartype), \ theoid, sizeof(theoid)/sizeof(oid)) -typedef int (WriteMethod)(int action, - u_char *var_val, - u_char var_val_type, - size_t var_val_len, - u_char *statP, - oid *name, - size_t length, - struct variable *v); - -typedef u_char *(FindVarMethod)(struct variable *v, - oid *name, - size_t *length, - int exact, - size_t *var_len, - WriteMethod **write_method); - -/* SNMP variable */ -struct variable -{ - /* Index of the MIB.*/ - u_char magic; - - /* Type of variable. */ - char type; - - /* Access control list. */ - u_short acl; - - /* Callback function. */ - FindVarMethod *findVar; - - /* Suffix of the MIB. */ - int namelen; - oid name[MAX_OID_LEN]; -}; - -/* SNMP tree. */ -struct subtree -{ - /* Tree's oid. */ - oid name[MAX_OID_LEN]; - u_char name_len; - - /* List of the variables. */ - struct variable *variables; - - /* Length of the variables list. */ - int variables_num; - - /* Width of the variables list. */ - int variables_width; - - /* Registered flag. */ - int registered; -}; - struct trap_object { - FindVarMethod *findVar; - int namelen; + int namelen; /* Negative if the object is not indexed */ oid name[MAX_OID_LEN]; }; /* Declare SMUX return value. */ #define SNMP_LOCAL_VARIABLES \ - static long snmp_int_val; \ - static struct in_addr snmp_in_addr_val; + static long snmp_int_val __attribute__ ((unused)); \ + static struct in_addr snmp_in_addr_val __attribute__ ((unused)); #define SNMP_INTEGER(V) \ ( \ @@ -145,15 +73,42 @@ struct trap_object ) extern void smux_init (struct thread_master *tm); -extern void smux_start (void); extern void smux_register_mib(const char *, struct variable *, size_t, int, oid [], size_t); extern int smux_header_generic (struct variable *, oid [], size_t *, int, size_t *, WriteMethod **); -extern int smux_trap (const oid *, size_t, const oid *, size_t, - const struct trap_object *, - size_t, unsigned int, u_char); -extern int oid_compare (oid *, int, oid *, int); +extern int smux_header_table (struct variable *, oid *, size_t *, + int, size_t *, WriteMethod **); + +/* For traps, three OID are provided: + + 1. The enterprise OID to use (the last argument will be appended to + it to form the SNMP trap OID) + + 2. The base OID for objects to be sent in traps. + + 3. The index OID for objects to be sent in traps. This index is used + to designate a particular instance of a column. + + The provided trap object contains the bindings to be sent with the + trap. The base OID will be prefixed to the provided OID and, if the + length is positive, the requested OID is assumed to be a columnar + object and the index OID will be appended. + + The two first arguments are the MIB registry used to locate the trap + objects. + + The use of the arguments may differ depending on the implementation + used. +*/ +extern int smux_trap (struct variable *, size_t, + const oid *, size_t, + const oid *, size_t, + const oid *, size_t, + const struct trap_object *, size_t, + u_char); + +extern int oid_compare (const oid *, int, const oid *, int); extern void oid2in_addr (oid [], int, struct in_addr *); extern void *oid_copy (void *, const void *, size_t); extern void oid_copy_addr (oid [], struct in_addr *, int); diff --git a/lib/snmp.c b/lib/snmp.c new file mode 100644 index 000000000..f6f9845e2 --- /dev/null +++ b/lib/snmp.c @@ -0,0 +1,133 @@ +/* SNMP 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. + */ + +#include + +#ifdef HAVE_SNMP +#include +#include + +#include "smux.h" + +#define min(A,B) ((A) < (B) ? (A) : (B)) + +int +oid_compare (const oid *o1, int o1_len, const oid *o2, int o2_len) +{ + int i; + + for (i = 0; i < min (o1_len, o2_len); i++) + { + if (o1[i] < o2[i]) + return -1; + else if (o1[i] > o2[i]) + return 1; + } + if (o1_len < o2_len) + return -1; + if (o1_len > o2_len) + return 1; + + return 0; +} + +void * +oid_copy (void *dest, const void *src, size_t size) +{ + return memcpy (dest, src, size * sizeof (oid)); +} + +void +oid2in_addr (oid oid[], int len, struct in_addr *addr) +{ + int i; + u_char *pnt; + + if (len == 0) + return; + + pnt = (u_char *) addr; + + for (i = 0; i < len; i++) + *pnt++ = oid[i]; +} + +void +oid_copy_addr (oid oid[], struct in_addr *addr, int len) +{ + int i; + u_char *pnt; + + if (len == 0) + return; + + pnt = (u_char *) addr; + + for (i = 0; i < len; i++) + oid[i] = *pnt++; +} + +int +smux_header_generic (struct variable *v, oid *name, size_t *length, int exact, + size_t *var_len, WriteMethod **write_method) +{ + oid fulloid[MAX_OID_LEN]; + int ret; + + oid_copy (fulloid, v->name, v->namelen); + fulloid[v->namelen] = 0; + /* Check against full instance. */ + ret = oid_compare (name, *length, fulloid, v->namelen + 1); + + /* Check single instance. */ + if ((exact && (ret != 0)) || (!exact && (ret >= 0))) + return MATCH_FAILED; + + /* In case of getnext, fill in full instance. */ + memcpy (name, fulloid, (v->namelen + 1) * sizeof (oid)); + *length = v->namelen + 1; + + *write_method = 0; + *var_len = sizeof(long); /* default to 'long' results */ + + return MATCH_SUCCEEDED; +} + +int +smux_header_table (struct variable *v, oid *name, size_t *length, int exact, + size_t *var_len, WriteMethod **write_method) +{ + /* If the requested OID name is less than OID prefix we + handle, adjust it to our prefix. */ + if ((oid_compare (name, *length, v->name, v->namelen)) < 0) + { + if (exact) + return MATCH_FAILED; + oid_copy(name, v->name, v->namelen); + *length = v->namelen; + } + + *write_method = 0; + *var_len = sizeof(long); + + return MATCH_SUCCEEDED; +} +#endif /* HAVE_SNMP */ diff --git a/lib/sockopt.c b/lib/sockopt.c index be22827f6..3e6ee730a 100644 --- a/lib/sockopt.c +++ b/lib/sockopt.c @@ -20,6 +20,11 @@ */ #include + +#ifdef SUNOS_5 +#include +#endif + #include "log.h" #include "sockopt.h" #include "sockunion.h" @@ -220,7 +225,7 @@ int setsockopt_ipv4_multicast(int sock, int optname, unsigned int mcast_addr, - unsigned int ifindex) + ifindex_t ifindex) { #ifdef HAVE_RFC3678 struct group_req gr; @@ -318,8 +323,7 @@ setsockopt_ipv4_multicast(int sock, * Set IP_MULTICAST_IF socket option in an OS-dependent manner. */ int -setsockopt_ipv4_multicast_if(int sock, - unsigned int ifindex) +setsockopt_ipv4_multicast_if(int sock, ifindex_t ifindex) { #ifdef HAVE_STRUCT_IP_MREQN_IMR_IFINDEX @@ -339,13 +343,42 @@ setsockopt_ipv4_multicast_if(int sock, m.s_addr = htonl(ifindex); return setsockopt (sock, IPPROTO_IP, IP_MULTICAST_IF, (void *)&m, sizeof(m)); +#elif defined(SUNOS_5) + char ifname[IF_NAMESIZE]; + struct ifaddrs *ifa, *ifap; + struct in_addr ifaddr; + + if (if_indextoname(ifindex, ifname) == NULL) + return -1; + + if (getifaddrs(&ifa) != 0) + return -1; + + for (ifap = ifa; ifap != NULL; ifap = ifap->ifa_next) + { + struct sockaddr_in *sa; + + if (strcmp(ifap->ifa_name, ifname) != 0) + continue; + if (ifap->ifa_addr->sa_family != AF_INET) + continue; + sa = (struct sockaddr_in*)ifap->ifa_addr; + memcpy(&ifaddr, &sa->sin_addr, sizeof(ifaddr)); + break; + } + + freeifaddrs(ifa); + if (!ifap) /* This means we did not find an IP */ + return -1; + + return setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF, (void *)&ifaddr, sizeof(ifaddr)); #else #error "Unsupported multicast API" #endif } static int -setsockopt_ipv4_ifindex (int sock, int val) +setsockopt_ipv4_ifindex (int sock, ifindex_t val) { int ret; @@ -381,7 +414,7 @@ setsockopt_ipv4_tos(int sock, int tos) int -setsockopt_ifindex (int af, int sock, int val) +setsockopt_ifindex (int af, int sock, ifindex_t val) { int ret = -1; @@ -408,11 +441,11 @@ setsockopt_ifindex (int af, int sock, int val) * Returns the interface index (small integer >= 1) if it can be * determined, or else 0. */ -static int +static ifindex_t getsockopt_ipv4_ifindex (struct msghdr *msgh) { /* XXX: initialize to zero? (Always overwritten, so just cosmetic.) */ - int ifindex = -1; + ifindex_t ifindex = -1; #if defined(IP_PKTINFO) /* Linux pktinfo based ifindex retrieval */ @@ -432,7 +465,7 @@ getsockopt_ipv4_ifindex (struct msghdr *msgh) struct sockaddr_dl *sdl; #else /* SUNOS_5 uses an integer with the index. */ - int *ifindex_p; + ifindex_t *ifindex_p; #endif /* SUNOS_5 */ #ifndef SUNOS_5 @@ -473,7 +506,7 @@ getsockopt_ipv4_ifindex (struct msghdr *msgh) } /* return ifindex, 0 if none found */ -int +ifindex_t getsockopt_ifindex (int af, struct msghdr *msgh) { switch (af) @@ -518,6 +551,22 @@ sockopt_iphdrincl_swab_systoh (struct ip *iph) iph->ip_id = ntohs(iph->ip_id); } +int +sockopt_tcp_rtt (int sock) +{ +#ifdef TCP_INFO + struct tcp_info ti; + socklen_t len = sizeof(ti); + + if (getsockopt (sock, IPPROTO_TCP, TCP_INFO, &ti, &len) != 0) + return 0; + + return ti.tcpi_rtt / 1000; +#else + return 0; +#endif +} + int sockopt_tcp_signature (int sock, union sockunion *su, const char *password) { diff --git a/lib/sockopt.h b/lib/sockopt.h index aced6d489..a9b8acaac 100644 --- a/lib/sockopt.h +++ b/lib/sockopt.h @@ -83,16 +83,15 @@ extern int setsockopt_ipv6_tclass (int, int); (((af) == AF_INET) : SOPT_SIZE_CMSG_IFINDEX_IPV4() \ ? SOPT_SIZE_CMSG_PKTINFO_IPV6()) -extern int setsockopt_ipv4_multicast_if(int sock, - unsigned int ifindex); +extern int setsockopt_ipv4_multicast_if(int sock, ifindex_t ifindex); extern int setsockopt_ipv4_multicast(int sock, int optname, unsigned int mcast_addr, - unsigned int ifindex); + ifindex_t ifindex); extern int setsockopt_ipv4_tos(int sock, int tos); /* Ask for, and get, ifindex, by whatever method is supported. */ -extern int setsockopt_ifindex (int, int, int); -extern int getsockopt_ifindex (int, struct msghdr *); +extern int setsockopt_ifindex (int, int, ifindex_t); +extern ifindex_t getsockopt_ifindex (int, struct msghdr *); /* swab the fields in iph between the host order and system order expected * for IP_HDRINCL. @@ -100,6 +99,7 @@ extern int getsockopt_ifindex (int, struct msghdr *); extern void sockopt_iphdrincl_swab_htosys (struct ip *iph); extern void sockopt_iphdrincl_swab_systoh (struct ip *iph); +extern int sockopt_tcp_rtt (int); extern int sockopt_tcp_signature(int sock, union sockunion *su, const char *password); #endif /*_ZEBRA_SOCKOPT_H */ diff --git a/lib/sockunion.c b/lib/sockunion.c index 597705293..8e0ec2493 100644 --- a/lib/sockunion.c +++ b/lib/sockunion.c @@ -27,6 +27,7 @@ #include "memory.h" #include "str.h" #include "log.h" +#include "jhash.h" #ifndef HAVE_INET_ATON int @@ -116,7 +117,7 @@ inet_ntop (int family, const void *addrptr, char *strptr, size_t len) #endif /* ! HAVE_INET_NTOP */ const char * -inet_sutop (union sockunion *su, char *str) +inet_sutop (const union sockunion *su, char *str) { switch (su->sa.sa_family) { @@ -163,69 +164,36 @@ str2sockunion (const char *str, union sockunion *su) } const char * -sockunion2str (union sockunion *su, char *buf, size_t len) +sockunion2str (const union sockunion *su, char *buf, size_t len) { - if (su->sa.sa_family == AF_INET) - return inet_ntop (AF_INET, &su->sin.sin_addr, buf, len); + switch (sockunion_family(su)) + { + case AF_UNSPEC: + snprintf (buf, len, "(unspec)"); + return buf; + case AF_INET: + return inet_ntop (AF_INET, &su->sin.sin_addr, buf, len); #ifdef HAVE_IPV6 - else if (su->sa.sa_family == AF_INET6) - return inet_ntop (AF_INET6, &su->sin6.sin6_addr, buf, len); + case AF_INET6: + return inet_ntop (AF_INET6, &su->sin6.sin6_addr, buf, len); #endif /* HAVE_IPV6 */ - return NULL; + } + snprintf (buf, len, "(af %d)", sockunion_family(su)); + return buf; } union sockunion * sockunion_str2su (const char *str) { - int ret; - union sockunion *su; - - su = XCALLOC (MTYPE_SOCKUNION, sizeof (union sockunion)); - - ret = inet_pton (AF_INET, str, &su->sin.sin_addr); - if (ret > 0) /* Valid IPv4 address format. */ - { - su->sin.sin_family = AF_INET; -#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN - su->sin.sin_len = sizeof(struct sockaddr_in); -#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */ - return su; - } -#ifdef HAVE_IPV6 - ret = inet_pton (AF_INET6, str, &su->sin6.sin6_addr); - if (ret > 0) /* Valid IPv6 address format. */ - { - su->sin6.sin6_family = AF_INET6; -#ifdef SIN6_LEN - su->sin6.sin6_len = sizeof(struct sockaddr_in6); -#endif /* SIN6_LEN */ - return su; - } -#endif /* HAVE_IPV6 */ - + union sockunion *su = XCALLOC (MTYPE_SOCKUNION, sizeof (union sockunion)); + + if (!str2sockunion (str, su)) + return su; + XFREE (MTYPE_SOCKUNION, su); return NULL; } -char * -sockunion_su2str (union sockunion *su) -{ - char str[SU_ADDRSTRLEN]; - - switch (su->sa.sa_family) - { - case AF_INET: - inet_ntop (AF_INET, &su->sin.sin_addr, str, sizeof (str)); - break; -#ifdef HAVE_IPV6 - case AF_INET6: - inet_ntop (AF_INET6, &su->sin6.sin6_addr, str, sizeof (str)); - break; -#endif /* HAVE_IPV6 */ - } - return XSTRDUP (MTYPE_TMP, str); -} - /* Convert IPv4 compatible IPv6 address to IPv4 address. */ static void sockunion_normalise_mapped (union sockunion *su) @@ -247,7 +215,7 @@ sockunion_normalise_mapped (union sockunion *su) /* Return socket of sockunion. */ int -sockunion_socket (union sockunion *su) +sockunion_socket (const union sockunion *su) { int sock; @@ -277,7 +245,7 @@ sockunion_accept (int sock, union sockunion *su) /* Return sizeof union sockunion. */ static int -sockunion_sizeof (union sockunion *su) +sockunion_sizeof (const union sockunion *su) { int ret; @@ -298,7 +266,7 @@ sockunion_sizeof (union sockunion *su) /* return sockunion structure : this function should be revised. */ static const char * -sockunion_log (union sockunion *su, char *buf, size_t len) +sockunion_log (const union sockunion *su, char *buf, size_t len) { switch (su->sa.sa_family) { @@ -322,8 +290,8 @@ sockunion_log (union sockunion *su, char *buf, size_t len) 0 : connect success 1 : connect is in progress */ enum connect_result -sockunion_connect (int fd, union sockunion *peersu, unsigned short port, - unsigned int ifindex) +sockunion_connect (int fd, const union sockunion *peersu, unsigned short port, + ifindex_t ifindex) { int ret; int val; @@ -344,13 +312,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; @@ -422,7 +385,7 @@ sockunion_bind (int sock, union sockunion *su, unsigned short port, su->sin.sin_len = size; #endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */ if (su_addr == NULL) - su->sin.sin_addr.s_addr = htonl (INADDR_ANY); + sockunion2ip (su) = htonl (INADDR_ANY); } #ifdef HAVE_IPV6 else if (su->sa.sa_family == AF_INET6) @@ -434,7 +397,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; @@ -592,7 +555,7 @@ sockopt_v6only (int family, int sock) /* If same family and same prefix return 1. */ int -sockunion_same (union sockunion *su1, union sockunion *su2) +sockunion_same (const union sockunion *su1, const union sockunion *su2) { int ret = 0; @@ -618,6 +581,92 @@ sockunion_same (union sockunion *su1, union sockunion *su2) return 0; } +unsigned int +sockunion_hash (const union sockunion *su) +{ + switch (sockunion_family(su)) + { + case AF_INET: + return jhash_1word(su->sin.sin_addr.s_addr, 0); +#ifdef HAVE_IPV6 + case AF_INET6: + return jhash2(su->sin6.sin6_addr.s6_addr32, ZEBRA_NUM_OF(su->sin6.sin6_addr.s6_addr32), 0); +#endif /* HAVE_IPV6 */ + } + return 0; +} + +size_t +family2addrsize(int family) +{ + switch (family) + { + case AF_INET: + return sizeof(struct in_addr); +#ifdef HAVE_IPV6 + case AF_INET6: + return sizeof(struct in6_addr); +#endif /* HAVE_IPV6 */ + } + return 0; +} + +size_t +sockunion_get_addrlen(const union sockunion *su) +{ + return family2addrsize(sockunion_family(su)); +} + +const u_char * +sockunion_get_addr(const union sockunion *su) +{ + switch (sockunion_family(su)) + { + case AF_INET: + return (const u_char *) &su->sin.sin_addr.s_addr; +#ifdef HAVE_IPV6 + case AF_INET6: + return (const u_char *) &su->sin6.sin6_addr; +#endif /* HAVE_IPV6 */ + } + return NULL; +} + +unsigned short +sockunion_get_port (const union sockunion *su) +{ + switch (sockunion_family (su)) + { + case AF_INET: + return ntohs(su->sin.sin_port); +#ifdef HAVE_IPV6 + case AF_INET6: + return ntohs(su->sin6.sin6_port); +#endif /* HAVE_IPV6 */ + } + return 0; +} + +void +sockunion_set(union sockunion *su, int family, const u_char *addr, size_t bytes) +{ + if (family2addrsize(family) != bytes) + return; + + sockunion_family(su) = family; + switch (family) + { + case AF_INET: + memcpy(&su->sin.sin_addr.s_addr, addr, bytes); + break; +#ifdef HAVE_IPV6 + case AF_INET6: + memcpy(&su->sin6.sin6_addr, addr, bytes); + break; +#endif /* HAVE_IPV6 */ + } +} + /* After TCP connection is established. Get local address and port. */ union sockunion * sockunion_getsockname (int fd) @@ -711,7 +760,7 @@ sockunion_getpeername (int fd) /* Print sockunion structure */ static void __attribute__ ((unused)) -sockunion_print (union sockunion *su) +sockunion_print (const union sockunion *su) { if (su == NULL) return; @@ -750,7 +799,7 @@ sockunion_print (union sockunion *su) #ifdef HAVE_IPV6 static int -in6addr_cmp (struct in6_addr *addr1, struct in6_addr *addr2) +in6addr_cmp (const struct in6_addr *addr1, const struct in6_addr *addr2) { unsigned int i; u_char *p1, *p2; @@ -770,7 +819,7 @@ in6addr_cmp (struct in6_addr *addr1, struct in6_addr *addr2) #endif /* HAVE_IPV6 */ int -sockunion_cmp (union sockunion *su1, union sockunion *su2) +sockunion_cmp (const union sockunion *su1, const union sockunion *su2) { if (su1->sa.sa_family > su2->sa.sa_family) return 1; @@ -779,9 +828,9 @@ sockunion_cmp (union sockunion *su1, union sockunion *su2) if (su1->sa.sa_family == AF_INET) { - if (ntohl (su1->sin.sin_addr.s_addr) == ntohl (su2->sin.sin_addr.s_addr)) + if (ntohl (sockunion2ip (su1)) == ntohl (sockunion2ip (su2))) return 0; - if (ntohl (su1->sin.sin_addr.s_addr) > ntohl (su2->sin.sin_addr.s_addr)) + if (ntohl (sockunion2ip (su1)) > ntohl (sockunion2ip (su2))) return 1; else return -1; @@ -795,7 +844,7 @@ sockunion_cmp (union sockunion *su1, union sockunion *su2) /* Duplicate sockunion. */ union sockunion * -sockunion_dup (union sockunion *su) +sockunion_dup (const union sockunion *su) { union sockunion *dup = XCALLOC (MTYPE_SOCKUNION, sizeof (union sockunion)); memcpy (dup, su, sizeof (union sockunion)); diff --git a/lib/sockunion.h b/lib/sockunion.h index 4531f620a..3613073d0 100644 --- a/lib/sockunion.h +++ b/lib/sockunion.h @@ -23,6 +23,8 @@ #ifndef _ZEBRA_SOCKUNION_H #define _ZEBRA_SOCKUNION_H +#include "if.h" + #if 0 union sockunion { struct sockinet { @@ -78,23 +80,24 @@ enum connect_result #define SET_IN6_LINKLOCAL_IFINDEX(a, i) #endif /* KAME */ -/* shortcut macro to specify address field of struct sockaddr */ -#define sock2ip(X) (((struct sockaddr_in *)(X))->sin_addr.s_addr) -#ifdef HAVE_IPV6 -#define sock2ip6(X) (((struct sockaddr_in6 *)(X))->sin6_addr.s6_addr) -#endif /* HAVE_IPV6 */ - #define sockunion_family(X) (X)->sa.sa_family +#define sockunion2ip(X) (X)->sin.sin_addr.s_addr + /* Prototypes. */ extern int str2sockunion (const char *, union sockunion *); -extern const char *sockunion2str (union sockunion *, char *, size_t); -extern int sockunion_cmp (union sockunion *, union sockunion *); -extern int sockunion_same (union sockunion *, union sockunion *); +extern const char *sockunion2str (const union sockunion *, char *, size_t); +extern int sockunion_cmp (const union sockunion *, const union sockunion *); +extern int sockunion_same (const union sockunion *, const union sockunion *); +extern unsigned int sockunion_hash (const union sockunion *); + +extern size_t family2addrsize(int family); +extern size_t sockunion_get_addrlen(const union sockunion *); +extern const u_char *sockunion_get_addr(const union sockunion *); +extern unsigned short sockunion_get_port (const union sockunion *); +extern void sockunion_set(union sockunion *, int family, const u_char *addr, size_t bytes); -extern char *sockunion_su2str (union sockunion *su); extern union sockunion *sockunion_str2su (const char *str); -extern struct in_addr sockunion_get_in_addr (union sockunion *su); extern int sockunion_accept (int sock, union sockunion *); extern int sockunion_stream_socket (union sockunion *); extern int sockopt_reuseaddr (int); @@ -105,14 +108,14 @@ extern int sockunion_bind (int sock, union sockunion *, extern int sockopt_ttl (int family, int sock, int ttl); extern int sockopt_minttl (int family, int sock, int minttl); extern int sockopt_cork (int sock, int onoff); -extern int sockunion_socket (union sockunion *su); -extern const char *inet_sutop (union sockunion *su, char *str); -extern enum connect_result sockunion_connect (int fd, union sockunion *su, +extern int sockunion_socket (const union sockunion *su); +extern const char *inet_sutop (const union sockunion *su, char *str); +extern enum connect_result sockunion_connect (int fd, const union sockunion *su, unsigned short port, - unsigned int); + ifindex_t); extern union sockunion *sockunion_getsockname (int); extern union sockunion *sockunion_getpeername (int); -extern union sockunion *sockunion_dup (union sockunion *); +extern union sockunion *sockunion_dup (const union sockunion *); extern void sockunion_free (union sockunion *); #ifndef HAVE_INET_NTOP diff --git a/lib/str.c b/lib/str.c index 4ab71e197..d8f039a09 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 983330ffb..ed677c137 100644 --- a/lib/stream.c +++ b/lib/stream.c @@ -20,8 +20,8 @@ * 02111-1307, USA. */ -#include #include +#include #include "stream.h" #include "memory.h" @@ -52,15 +52,15 @@ * using stream_put..._at() functions. */ #define STREAM_WARN_OFFSETS(S) \ - zlog_warn ("&(struct stream): %p, size: %lu, endp: %lu, getp: %lu\n", \ - (S), \ + zlog_warn ("&(struct stream): %p, size: %lu, getp: %lu, endp: %lu\n", \ + (void *)(S), \ (unsigned long) (S)->size, \ (unsigned long) (S)->getp, \ (unsigned long) (S)->endp)\ #define STREAM_VERIFY_SANE(S) \ do { \ - if ( !(GETP_VALID(S, (S)->getp)) && ENDP_VALID(S, (S)->endp) ) \ + if ( !(GETP_VALID(S, (S)->getp) && ENDP_VALID(S, (S)->endp)) ) \ STREAM_WARN_OFFSETS(S); \ assert ( GETP_VALID(S, (S)->getp) ); \ assert ( ENDP_VALID(S, (S)->endp) ); \ @@ -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) { @@ -214,6 +233,30 @@ stream_set_getp (struct stream *s, size_t pos) s->getp = pos; } +void +stream_set_endp (struct stream *s, size_t pos) +{ + STREAM_VERIFY_SANE(s); + + if (!ENDP_VALID(s, pos)) + { + STREAM_BOUND_WARN (s, "set endp"); + return; + } + + /* + * Make sure the current read pointer is not beyond the new endp. + */ + if (s->getp > pos) + { + STREAM_BOUND_WARN(s, "set endp"); + return; + } + + s->endp = pos; + STREAM_VERIFY_SANE(s); +} + /* Forward pointer. */ void stream_forward_getp (struct stream *s, size_t size) @@ -242,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) @@ -449,7 +492,37 @@ stream_get_ipv4 (struct stream *s) return l; } - + +float +stream_getf (struct stream *s) +{ +#if !defined(__STDC_IEC_559__) && __GCC_IEC_559 < 1 +#warning "Unknown floating-point format, __func__ may be wrong" +#endif +/* we assume 'float' is in the single precision IEC 60559 binary + format, in host byte order */ + union { + float r; + uint32_t d; + } u; + u.d = stream_getl (s); + return u.r; +} + +double +stream_getd (struct stream *s) +{ +#if !defined(__STDC_IEC_559__) && __GCC_IEC_559 < 1 +#warning "Unknown floating-point format, __func__ may be wrong" +#endif + union { + double r; + uint64_t d; + } u; + u.d = stream_getq (s); + return u.r; +} + /* Copy to source to stream. * * XXX: This uses CHECK_SIZE and hence has funny semantics -> Size will wrap @@ -558,6 +631,37 @@ stream_putq (struct stream *s, uint64_t q) return 8; } +int +stream_putf (struct stream *s, float f) +{ +#if !defined(__STDC_IEC_559__) && __GCC_IEC_559 < 1 +#warning "Unknown floating-point format, __func__ may be wrong" +#endif + +/* we can safely assume 'float' is in the single precision + IEC 60559 binary format in host order */ + union { + float i; + uint32_t o; + } u; + u.i = f; + return stream_putl (s, u.o); +} + +int +stream_putd (struct stream *s, double d) +{ +#if !defined(__STDC_IEC_559__) && __GCC_IEC_559 < 1 +#warning "Unknown floating-point format, __func__ may be wrong" +#endif + union { + double i; + uint64_t o; + } u; + u.i = d; + return stream_putq (s, u.o); +} + int stream_putc_at (struct stream *s, size_t putp, u_char c) { @@ -676,19 +780,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) @@ -711,32 +815,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) { @@ -882,6 +960,31 @@ stream_reset (struct stream *s) s->getp = s->endp = 0; } +/* Discard read data (prior to the getp), and move the unread data + * to the beginning of the stream. + * + * See also stream_fifo_* functions, for another approach to managing + * streams. + */ +void +stream_discard (struct stream *s) +{ + STREAM_VERIFY_SANE (s); + + if (s->getp == 0) + return; + + if (s->getp == s->endp) + { + stream_reset (s); + return; + } + + s->data = memmove (s->data, s->data + s->getp, s->endp - s->getp); + s->endp -= s->getp; + s->getp = 0; +} + /* Write stream contens to the file discriptor. */ int stream_flush (struct stream *s, int fd) @@ -894,7 +997,7 @@ stream_flush (struct stream *s, int fd) return nbytes; } - + /* Stream first in first out queue. */ struct stream_fifo * @@ -934,9 +1037,9 @@ stream_fifo_pop (struct stream_fifo *fifo) if (fifo->head == NULL) fifo->tail = NULL; - } - fifo->count--; + fifo->count--; + } return s; } diff --git a/lib/stream.h b/lib/stream.h index 3e4ba7b41..9127bcd23 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,7 +148,16 @@ 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); extern void stream_forward_endp (struct stream *, size_t); @@ -174,6 +186,12 @@ extern uint64_t stream_getq (struct stream *); extern uint64_t stream_getq_from (struct stream *, size_t); extern u_int32_t stream_get_ipv4 (struct stream *); +/* IEEE-754 floats */ +extern float stream_getf (struct stream *); +extern double stream_getd (struct stream *); +extern int stream_putf (struct stream *, float); +extern int stream_putd (struct stream *, double); + #undef stream_read #undef stream_write @@ -181,10 +199,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 @@ -204,6 +218,8 @@ extern size_t stream_write (struct stream *, const void *, size_t); /* reset the stream. See Note above */ extern void stream_reset (struct stream *); +/* move unread data to start of stream, discarding read data */ +extern void stream_discard (struct stream *); extern int stream_flush (struct stream *, int); extern int stream_empty (struct stream *); /* is the stream empty? */ diff --git a/lib/table.c b/lib/table.c index e40e67072..da2136168 100644 --- a/lib/table.c +++ b/lib/table.c @@ -27,15 +27,20 @@ #include "memory.h" #include "sockunion.h" -void route_node_delete (struct route_node *); -void route_table_free (struct route_table *); - +static void route_node_delete (struct route_node *); +static void route_table_free (struct route_table *); + + +/* + * route_table_init_with_delegate + */ struct route_table * -route_table_init (void) +route_table_init_with_delegate (route_table_delegate_t *delegate) { struct route_table *rt; rt = XCALLOC (MTYPE_ROUTE_TABLE, sizeof (struct route_table)); + rt->delegate = delegate; return rt; } @@ -47,20 +52,18 @@ route_table_finish (struct route_table *rt) /* Allocate new route node. */ static struct route_node * -route_node_new (void) +route_node_new (struct route_table *table) { - struct route_node *node; - node = XCALLOC (MTYPE_ROUTE_NODE, sizeof (struct route_node)); - return node; + return table->delegate->create_node (table->delegate, table); } /* Allocate new route node with prefix set. */ static struct route_node * -route_node_set (struct route_table *table, struct prefix *prefix) +route_node_set (struct route_table *table, const struct prefix *prefix) { struct route_node *node; - node = route_node_new (); + node = route_node_new (table); prefix_copy (&node->p, prefix); node->table = table; @@ -70,13 +73,13 @@ route_node_set (struct route_table *table, struct prefix *prefix) /* Free route node. */ static void -route_node_free (struct route_node *node) +route_node_free (struct route_table *table, struct route_node *node) { - XFREE (MTYPE_ROUTE_NODE, node); + table->delegate->destroy_node (table->delegate, table, node); } /* Free route table. */ -void +static void route_table_free (struct route_table *rt) { struct route_node *tmp_node; @@ -87,6 +90,9 @@ route_table_free (struct route_table *rt) node = rt->top; + /* Bulk deletion of nodes remaining in this table. This function is not + called until workers have completed their dependency on this table. + A final route_unlock_node() will not be called for these nodes. */ while (node) { if (node->l_left) @@ -104,22 +110,25 @@ route_table_free (struct route_table *rt) tmp_node = node; node = node->parent; + tmp_node->table->count--; + tmp_node->lock = 0; /* to cause assert if unlocked after this */ + route_node_free (rt, tmp_node); + if (node != NULL) { if (node->l_left == tmp_node) node->l_left = NULL; else node->l_right = NULL; - - route_node_free (tmp_node); } else { - route_node_free (tmp_node); break; } } + assert (rt->count == 0); + XFREE (MTYPE_ROUTE_TABLE, rt); return; } @@ -132,14 +141,14 @@ static const u_char maskbit[] = /* Common prefix route genaration. */ static void -route_common (struct prefix *n, struct prefix *p, struct prefix *new) +route_common (const struct prefix *n, const struct prefix *p, struct prefix *new) { int i; u_char diff; u_char mask; - u_char *np = (u_char *)&n->u.prefix; - u_char *pp = (u_char *)&p->u.prefix; + const u_char *np = (const u_char *)&n->u.prefix; + const u_char *pp = (const u_char *)&p->u.prefix; u_char *newp = (u_char *)&new->u.prefix; for (i = 0; i < p->prefixlen / 8; i++) @@ -186,6 +195,7 @@ route_lock_node (struct route_node *node) void route_unlock_node (struct route_node *node) { + assert (node->lock > 0); node->lock--; if (node->lock == 0) @@ -255,19 +265,21 @@ route_node_match_ipv6 (const struct route_table *table, /* Lookup same prefix node. Return NULL when we can't find route. */ struct route_node * -route_node_lookup (struct route_table *table, struct prefix *p) +route_node_lookup (const struct route_table *table, const struct prefix *p) { struct route_node *node; + u_char prefixlen = p->prefixlen; + const u_char *prefix = &p->u.prefix; node = table->top; - while (node && node->p.prefixlen <= p->prefixlen && + while (node && node->p.prefixlen <= prefixlen && prefix_match (&node->p, p)) { - if (node->p.prefixlen == p->prefixlen) + if (node->p.prefixlen == prefixlen) return node->info ? route_lock_node (node) : NULL; - node = node->link[prefix_bit(&p->u.prefix, node->p.prefixlen)]; + node = node->link[prefix_bit(prefix, node->p.prefixlen)]; } return NULL; @@ -275,22 +287,24 @@ route_node_lookup (struct route_table *table, struct prefix *p) /* Add node to routing table. */ struct route_node * -route_node_get (struct route_table *table, struct prefix *p) +route_node_get (struct route_table *const table, const struct prefix *p) { struct route_node *new; struct route_node *node; struct route_node *match; + u_char prefixlen = p->prefixlen; + const u_char *prefix = &p->u.prefix; match = NULL; node = table->top; - while (node && node->p.prefixlen <= p->prefixlen && + while (node && node->p.prefixlen <= prefixlen && prefix_match (&node->p, p)) { - if (node->p.prefixlen == p->prefixlen) + if (node->p.prefixlen == prefixlen) return route_lock_node (node); - + match = node; - node = node->link[prefix_bit(&p->u.prefix, node->p.prefixlen)]; + node = node->link[prefix_bit(prefix, node->p.prefixlen)]; } if (node == NULL) @@ -303,7 +317,7 @@ route_node_get (struct route_table *table, struct prefix *p) } else { - new = route_node_new (); + new = route_node_new (table); route_common (&node->p, p, &new->p); new->p.family = p->family; new->table = table; @@ -319,15 +333,17 @@ route_node_get (struct route_table *table, struct prefix *p) match = new; new = route_node_set (table, p); set_link (match, new); + table->count++; } } + table->count++; route_lock_node (new); return new; } /* Delete node from the routing table. */ -void +static void route_node_delete (struct route_node *node) { struct route_node *child; @@ -359,7 +375,9 @@ route_node_delete (struct route_node *node) else node->table->top = child; - route_node_free (node); + node->table->count--; + + route_node_free (node->table, node); /* If parent node is stub then delete it also. */ if (parent && parent->lock == 0) @@ -461,3 +479,331 @@ route_next_until (struct route_node *node, struct route_node *limit) route_unlock_node (start); return NULL; } + +unsigned long +route_table_count (const struct route_table *table) +{ + return table->count; +} + +/** + * route_node_create + * + * Default function for creating a route node. + */ +static struct route_node * +route_node_create (route_table_delegate_t *delegate, + struct route_table *table) +{ + struct route_node *node; + node = XCALLOC (MTYPE_ROUTE_NODE, sizeof (struct route_node)); + return node; +} + +/** + * route_node_destroy + * + * Default function for destroying a route node. + */ +static void +route_node_destroy (route_table_delegate_t *delegate, + struct route_table *table, struct route_node *node) +{ + XFREE (MTYPE_ROUTE_NODE, node); +} + +/* + * Default delegate. + */ +static route_table_delegate_t default_delegate = { + .create_node = route_node_create, + .destroy_node = route_node_destroy +}; + +/* + * route_table_init + */ +struct route_table * +route_table_init (void) +{ + return route_table_init_with_delegate (&default_delegate); +} + +/** + * route_table_prefix_iter_cmp + * + * Compare two prefixes according to the order in which they appear in + * an iteration over a tree. + * + * @return -1 if p1 occurs before p2 (p1 < p2) + * 0 if the prefixes are identical (p1 == p2) + * +1 if p1 occurs after p2 (p1 > p2) + */ +int +route_table_prefix_iter_cmp (struct prefix *p1, struct prefix *p2) +{ + struct prefix common_space; + struct prefix *common = &common_space; + + if (p1->prefixlen <= p2->prefixlen) + { + if (prefix_match (p1, p2)) + { + + /* + * p1 contains p2, or is equal to it. + */ + return (p1->prefixlen == p2->prefixlen) ? 0 : -1; + } + } + else + { + + /* + * Check if p2 contains p1. + */ + if (prefix_match (p2, p1)) + return 1; + } + + route_common (p1, p2, common); + assert (common->prefixlen < p1->prefixlen); + assert (common->prefixlen < p2->prefixlen); + + /* + * Both prefixes are longer than the common prefix. + * + * We need to check the bit after the common prefixlen to determine + * which one comes later. + */ + if (prefix_bit (&p1->u.prefix, common->prefixlen)) + { + + /* + * We branch to the right to get to p1 from the common prefix. + */ + assert (!prefix_bit (&p2->u.prefix, common->prefixlen)); + return 1; + } + + /* + * We branch to the right to get to p2 from the common prefix. + */ + assert (prefix_bit (&p2->u.prefix, common->prefixlen)); + return -1; +} + +/* + * route_get_subtree_next + * + * Helper function that returns the first node that follows the nodes + * in the sub-tree under 'node' in iteration order. + */ +static struct route_node * +route_get_subtree_next (struct route_node *node) +{ + while (node->parent) + { + if (node->parent->l_left == node && node->parent->l_right) + return node->parent->l_right; + + node = node->parent; + } + + return NULL; +} + +/** + * route_table_get_next_internal + * + * Helper function to find the node that occurs after the given prefix in + * order of iteration. + * + * @see route_table_get_next + */ +static struct route_node * +route_table_get_next_internal (const struct route_table *table, + struct prefix *p) +{ + struct route_node *node, *tmp_node; + int cmp; + + node = table->top; + + while (node) + { + int match; + + if (node->p.prefixlen < p->prefixlen) + match = prefix_match (&node->p, p); + else + match = prefix_match (p, &node->p); + + if (match) + { + if (node->p.prefixlen == p->prefixlen) + { + + /* + * The prefix p exists in the tree, just return the next + * node. + */ + route_lock_node (node); + node = route_next (node); + if (node) + route_unlock_node (node); + + return (node); + } + + if (node->p.prefixlen > p->prefixlen) + { + + /* + * Node is in the subtree of p, and hence greater than p. + */ + return node; + } + + /* + * p is in the sub-tree under node. + */ + tmp_node = node->link[prefix_bit (&p->u.prefix, node->p.prefixlen)]; + + if (tmp_node) + { + node = tmp_node; + continue; + } + + /* + * There are no nodes in the direction where p should be. If + * node has a right child, then it must be greater than p. + */ + if (node->l_right) + return node->l_right; + + /* + * No more children to follow, go upwards looking for the next + * node. + */ + return route_get_subtree_next (node); + } + + /* + * Neither node prefix nor 'p' contains the other. + */ + cmp = route_table_prefix_iter_cmp (&node->p, p); + if (cmp > 0) + { + + /* + * Node follows p in iteration order. Return it. + */ + return node; + } + + assert (cmp < 0); + + /* + * Node and the subtree under it come before prefix p in + * iteration order. Prefix p and its sub-tree are not present in + * the tree. Go upwards and find the first node that follows the + * subtree. That node will also succeed p. + */ + return route_get_subtree_next (node); + } + + return NULL; +} + +/** + * route_table_get_next + * + * Find the node that occurs after the given prefix in order of + * iteration. + */ +struct route_node * +route_table_get_next (const struct route_table *table, struct prefix *p) +{ + struct route_node *node; + + node = route_table_get_next_internal (table, p); + if (node) + { + assert (route_table_prefix_iter_cmp (&node->p, p) > 0); + route_lock_node (node); + } + return node; +} + +/* + * route_table_iter_init + */ +void +route_table_iter_init (route_table_iter_t * iter, struct route_table *table) +{ + memset (iter, 0, sizeof (*iter)); + iter->state = RT_ITER_STATE_INIT; + iter->table = table; +} + +/* + * route_table_iter_pause + * + * Pause an iteration over the table. This allows the iteration to be + * resumed point after arbitrary additions/deletions from the table. + * An iteration can be resumed by just calling route_table_iter_next() + * on the iterator. + */ +void +route_table_iter_pause (route_table_iter_t * iter) +{ + switch (iter->state) + { + + case RT_ITER_STATE_INIT: + case RT_ITER_STATE_PAUSED: + case RT_ITER_STATE_DONE: + return; + + case RT_ITER_STATE_ITERATING: + + /* + * Save the prefix that we are currently at. The next call to + * route_table_iter_next() will return the node after this prefix + * in the tree. + */ + prefix_copy (&iter->pause_prefix, &iter->current->p); + route_unlock_node (iter->current); + iter->current = NULL; + iter->state = RT_ITER_STATE_PAUSED; + return; + + default: + assert (0); + } + +} + +/* + * route_table_iter_cleanup + * + * Release any resources held by the iterator. + */ +void +route_table_iter_cleanup (route_table_iter_t * iter) +{ + if (iter->state == RT_ITER_STATE_ITERATING) + { + route_unlock_node (iter->current); + iter->current = NULL; + } + assert (!iter->current); + + /* + * Set the state to RT_ITER_STATE_DONE to make any + * route_table_iter_next() calls on this iterator return NULL. + */ + iter->state = RT_ITER_STATE_DONE; +} diff --git a/lib/table.h b/lib/table.h index 41d1fa708..2ffd79b53 100644 --- a/lib/table.h +++ b/lib/table.h @@ -23,48 +23,134 @@ #ifndef _ZEBRA_TABLE_H #define _ZEBRA_TABLE_H +/* + * Forward declarations. + */ +struct route_node; +struct route_table; + +/* + * route_table_delegate_t + * + * Function vector that can be used by a client to customize the + * behavior of one or more route tables. + */ +typedef struct route_table_delegate_t_ route_table_delegate_t; + +typedef struct route_node * (*route_table_create_node_func_t) + (route_table_delegate_t *, struct route_table *); + +typedef void (*route_table_destroy_node_func_t) + (route_table_delegate_t *, struct route_table *, struct route_node *); + +struct route_table_delegate_t_ +{ + route_table_create_node_func_t create_node; + route_table_destroy_node_func_t destroy_node; +}; + /* Routing table top structure. */ struct route_table { struct route_node *top; + + /* + * Delegate that performs certain functions for this table. + */ + route_table_delegate_t *delegate; + + unsigned long count; + + /* + * User data. + */ + void *info; }; +/* + * Macro that defines all fields in a route node. + */ +#define ROUTE_NODE_FIELDS \ + /* Actual prefix of this radix. */ \ + struct prefix p; \ + \ + /* Tree link. */ \ + struct route_table *table; \ + struct route_node *parent; \ + struct route_node *link[2]; \ + \ + /* Lock of this radix */ \ + unsigned int lock; \ + \ + /* Each node of route. */ \ + void *info; \ + \ + /* Aggregation. */ \ + void *aggregate; + + /* Each routing entry. */ struct route_node { - /* Actual prefix of this radix. */ - struct prefix p; + ROUTE_NODE_FIELDS - /* Tree link. */ - struct route_table *table; - struct route_node *parent; - struct route_node *link[2]; #define l_left link[0] #define l_right link[1] +}; - /* Lock of this radix */ - unsigned int lock; +typedef struct route_table_iter_t_ route_table_iter_t; - /* Each node of route. */ - void *info; +typedef enum +{ + RT_ITER_STATE_INIT, + RT_ITER_STATE_ITERATING, + RT_ITER_STATE_PAUSED, + RT_ITER_STATE_DONE +} route_table_iter_state_t; - /* Aggregation. */ - void *aggregate; +/* + * route_table_iter_t + * + * Structure that holds state for iterating over a route table. + */ +struct route_table_iter_t_ +{ + + route_table_iter_state_t state; + + /* + * Routing table that we are iterating over. The caller must ensure + * that that table outlives the iterator. + */ + struct route_table *table; + + /* + * The node that the iterator is currently on. + */ + struct route_node *current; + + /* + * The last prefix that the iterator processed before it was paused. + */ + struct prefix pause_prefix; }; /* Prototypes. */ extern struct route_table *route_table_init (void); + +extern struct route_table * +route_table_init_with_delegate (route_table_delegate_t *); + extern void route_table_finish (struct route_table *); extern void route_unlock_node (struct route_node *node); -extern void route_node_delete (struct route_node *node); extern struct route_node *route_top (struct route_table *); extern struct route_node *route_next (struct route_node *); extern struct route_node *route_next_until (struct route_node *, struct route_node *); -extern struct route_node *route_node_get (struct route_table *, - struct prefix *); -extern struct route_node *route_node_lookup (struct route_table *, - struct prefix *); +extern struct route_node *route_node_get (struct route_table *const, + const struct prefix *); +extern struct route_node *route_node_lookup (const struct route_table *, + const struct prefix *); extern struct route_node *route_lock_node (struct route_node *node); extern struct route_node *route_node_match (const struct route_table *, const struct prefix *); @@ -75,4 +161,94 @@ extern struct route_node *route_node_match_ipv6 (const struct route_table *, const struct in6_addr *); #endif /* HAVE_IPV6 */ +extern unsigned long route_table_count (const struct route_table *); + +extern struct route_node * +route_table_get_next (const struct route_table *table, struct prefix *p); +extern int +route_table_prefix_iter_cmp (struct prefix *p1, struct prefix *p2); + +/* + * Iterator functions. + */ +extern void route_table_iter_init (route_table_iter_t *iter, + struct route_table *table); +extern void route_table_iter_pause (route_table_iter_t *iter); +extern void route_table_iter_cleanup (route_table_iter_t *iter); + +/* + * Inline functions. + */ + +/* + * route_table_iter_next + * + * Get the next node in the tree. + */ +static inline struct route_node * +route_table_iter_next (route_table_iter_t * iter) +{ + struct route_node *node; + + switch (iter->state) + { + + case RT_ITER_STATE_INIT: + + /* + * We're just starting the iteration. + */ + node = route_top (iter->table); + break; + + case RT_ITER_STATE_ITERATING: + node = route_next (iter->current); + break; + + case RT_ITER_STATE_PAUSED: + + /* + * Start with the node following pause_prefix. + */ + node = route_table_get_next (iter->table, &iter->pause_prefix); + break; + + case RT_ITER_STATE_DONE: + return NULL; + + default: + assert (0); + } + + iter->current = node; + if (node) + iter->state = RT_ITER_STATE_ITERATING; + else + iter->state = RT_ITER_STATE_DONE; + + return node; +} + +/* + * route_table_iter_is_done + * + * Returns TRUE if the iteration is complete. + */ +static inline int +route_table_iter_is_done (route_table_iter_t *iter) +{ + return iter->state == RT_ITER_STATE_DONE; +} + +/* + * route_table_iter_started + * + * Returns TRUE if this iterator has started iterating over the tree. + */ +static inline int +route_table_iter_started (route_table_iter_t *iter) +{ + return iter->state != RT_ITER_STATE_INIT; +} + #endif /* _ZEBRA_TABLE_H */ diff --git a/lib/thread.c b/lib/thread.c index b36c43a9a..de4d76d61 100644 --- a/lib/thread.c +++ b/lib/thread.c @@ -22,14 +22,21 @@ /* #define DEBUG */ #include +#include #include "thread.h" #include "memory.h" #include "log.h" #include "hash.h" +#include "pqueue.h" #include "command.h" #include "sigevent.h" - + +#if defined(__APPLE__) +#include +#include +#endif + /* Recent absolute time of day */ struct timeval recent_time; static struct timeval last_recent_time; @@ -38,9 +45,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 @@ -86,14 +93,14 @@ 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)); } - -#ifndef HAVE_CLOCK_MONOTONIC + +#if !defined(HAVE_CLOCK_MONOTONIC) && !defined(__APPLE__) static void quagga_gettimeofday_relative_adjust (void) { @@ -112,7 +119,7 @@ quagga_gettimeofday_relative_adjust (void) } last_recent_time = recent_time; } -#endif /* !HAVE_CLOCK_MONOTONIC */ +#endif /* !HAVE_CLOCK_MONOTONIC && !__APPLE__ */ /* gettimeofday wrapper, to keep recent_time updated */ static int @@ -152,7 +159,23 @@ quagga_get_relative (struct timeval *tv) relative_time.tv_usec = tp.tv_nsec / 1000; } } -#else /* !HAVE_CLOCK_MONOTONIC */ +#elif defined(__APPLE__) + { + uint64_t ticks; + uint64_t useconds; + static mach_timebase_info_data_t timebase_info; + + ticks = mach_absolute_time(); + if (timebase_info.denom == 0) + mach_timebase_info(&timebase_info); + + useconds = ticks * timebase_info.numer / timebase_info.denom / 1000; + relative_time.tv_sec = useconds / 1000000; + relative_time.tv_usec = useconds % 1000000; + + return 0; + } +#else /* !HAVE_CLOCK_MONOTONIC && !__APPLE__ */ if (!(ret = quagga_gettimeofday (&recent_time))) quagga_gettimeofday_relative_adjust(); #endif /* HAVE_CLOCK_MONOTONIC */ @@ -215,7 +238,7 @@ recent_relative_time (void) { return relative_time; } - + static unsigned int cpu_record_hash_key (struct cpu_thread_history *a) { @@ -235,7 +258,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; - new->funcname = XSTRDUP(MTYPE_THREAD_FUNCNAME, a->funcname); + new->funcname = a->funcname; return new; } @@ -244,7 +267,6 @@ cpu_record_hash_free (void *a) { struct cpu_thread_history *hist = a; - XFREE (MTYPE_THREAD_FUNCNAME, hist->funcname); XFREE (MTYPE_THREAD_STATS, hist); } @@ -303,7 +325,7 @@ cpu_record_print(struct vty *vty, thread_type filter) void *args[3] = {&tmp, vty, &filter}; memset(&tmp, 0, sizeof tmp); - tmp.funcname = (char *)"TOTAL"; + tmp.funcname = "TOTAL"; tmp.types = filter; #ifdef HAVE_RUSAGE @@ -465,47 +487,73 @@ DEFUN(clear_thread_cpu, cpu_record_clear (filter); return CMD_SUCCESS; } - -/* List allocation and head/tail print out. */ + +static int +thread_timer_cmp(void *a, void *b) +{ + 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; +} + static void -thread_list_debug (struct thread_list *list) -{ - printf ("count [%d] head [%p] tail [%p]\n", - list->count, list->head, list->tail); -} - -/* Debug print for thread_master. */ -static void __attribute__ ((unused)) -thread_master_debug (struct thread_master *m) -{ - 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"); -} - +thread_timer_update(void *node, int actual_position) +{ + struct thread *thread = node; + + thread->index = actual_position; +} + /* Allocate new thread master. */ struct thread_master * thread_master_create () { + struct thread_master *rv; + struct rlimit limit; + + getrlimit(RLIMIT_NOFILE, &limit); + 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)); + if (rv == NULL) + { + return NULL; + } + + rv->fd_limit = (int)limit.rlim_cur; + rv->read = XCALLOC (MTYPE_THREAD, sizeof (struct thread *) * rv->fd_limit); + if (rv->read == NULL) + { + XFREE (MTYPE_THREAD_MASTER, rv); + return NULL; + } + + rv->write = XCALLOC (MTYPE_THREAD, sizeof (struct thread *) * rv->fd_limit); + if (rv->write == NULL) + { + XFREE (MTYPE_THREAD, rv->read); + XFREE (MTYPE_THREAD_MASTER, rv); + return NULL; + } + + /* 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. */ @@ -522,22 +570,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) @@ -555,6 +587,18 @@ thread_list_delete (struct thread_list *list, struct thread *thread) return thread; } +static void +thread_delete_fd (struct thread **thread_array, struct thread *thread) +{ + thread_array[thread->u.fd] = NULL; +} + +static void +thread_add_fd (struct thread **thread_array, struct thread *thread) +{ + thread_array[thread->u.fd] = thread; +} + /* Move thread to unuse list. */ static void thread_add_unuse (struct thread_master *m, struct thread *thread) @@ -564,7 +608,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. */ @@ -577,25 +620,54 @@ thread_list_free (struct thread_master *m, struct thread_list *list) for (t = list->head; t; t = next) { next = t->next; - if (t->funcname) - XFREE (MTYPE_THREAD_FUNCNAME, t->funcname); XFREE (MTYPE_THREAD, t); list->count--; m->alloc--; } } +static void +thread_array_free (struct thread_master *m, struct thread **thread_array) +{ + struct thread *t; + int index; + + for (index = 0; index < m->fd_limit; ++index) + { + t = thread_array[index]; + if (t) + { + thread_array[index] = NULL; + XFREE (MTYPE_THREAD, t); + m->alloc--; + } + } + XFREE (MTYPE_THREAD, thread_array); +} + +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_array_free (m, m->read); + thread_array_free (m, m->write); + 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); @@ -635,47 +707,25 @@ thread_timer_remain_second (struct thread *thread) return 0; } -/* Trim blankspace and "()"s */ -static char * -strip_funcname (const char *funcname) +struct timeval +thread_timer_remain(struct thread *thread) { - char buff[100]; - char tmp, *ret, *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++; + quagga_get_relative(NULL); - tmp = *e; - *e = '\0'; - ret = XSTRDUP (MTYPE_THREAD_FUNCNAME, b); - *e = tmp; - - return ret; + return timeval_subtract(thread->u.sands, relative_time); } +#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; + struct thread *thread = thread_trim_head (&m->unuse); - if (!thread_empty (&m->unuse)) - { - thread = thread_trim_head (&m->unuse); - if (thread->funcname) - XFREE(MTYPE_THREAD_FUNCNAME, thread->funcname); - } - else + if (! thread) { thread = XCALLOC (MTYPE_THREAD, sizeof (struct thread)); m->alloc++; @@ -685,78 +735,110 @@ thread_get (struct thread_master *m, u_char type, thread->master = m; thread->func = func; thread->arg = arg; - - thread->funcname = strip_funcname(funcname); + thread->index = -1; + + thread->funcname = funcname; + thread->schedfrom = schedfrom; + thread->schedfrom_line = fromln; return thread; } -/* 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) -{ - struct thread *thread; +#define fd_copy_fd_set(X) (X) - assert (m != NULL); +static int +fd_select (int size, thread_fd_set *read, thread_fd_set *write, thread_fd_set *except, struct timeval *t) +{ + return(select(size, read, write, except, t)); +} - if (FD_ISSET (fd, &m->readfd)) - { - zlog (NULL, LOG_WARNING, "There is already read fd [%d]", fd); - return NULL; - } +static int +fd_is_set (int fd, thread_fd_set *fdset) +{ + return FD_ISSET (fd, fdset); +} - thread = thread_get (m, THREAD_READ, func, arg, funcname); - FD_SET (fd, &m->readfd); - thread->u.fd = fd; - thread_list_add (&m->read, thread); +static int +fd_clear_read_write (int fd, thread_fd_set *fdset) +{ + if (!FD_ISSET (fd, fdset)) + return 0; - return thread; + FD_CLR (fd, fdset); + return 1; } -/* 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) +static struct thread * +funcname_thread_add_read_write (int dir, struct thread_master *m, + int (*func) (struct thread *), void *arg, int fd, + debugargdef) { - struct thread *thread; + struct thread *thread = NULL; + thread_fd_set *fdset = NULL; - assert (m != NULL); + if (dir == THREAD_READ) + fdset = &m->readfd; + else + fdset = &m->writefd; - if (FD_ISSET (fd, &m->writefd)) + if (FD_ISSET (fd, fdset)) { - zlog (NULL, LOG_WARNING, "There is already write fd [%d]", fd); + zlog (NULL, LOG_WARNING, "There is already %s fd [%d]", + (dir = THREAD_READ) ? "read" : "write", fd); return NULL; } - thread = thread_get (m, THREAD_WRITE, func, arg, funcname); - FD_SET (fd, &m->writefd); + FD_SET (fd, fdset); + + thread = thread_get (m, dir, func, arg, debugargpass); thread->u.fd = fd; - thread_list_add (&m->write, thread); + if (dir == THREAD_READ) + thread_add_fd (m->read, thread); + else + thread_add_fd (m->write, thread); return thread; } +/* Add new read thread. */ +struct thread * +funcname_thread_add_read (struct thread_master *m, + int (*func) (struct thread *), void *arg, int fd, + debugargdef) +{ + return funcname_thread_add_read_write (THREAD_READ, m, func, + arg, fd, debugargpass); +} + +/* Add new write thread. */ +struct thread * +funcname_thread_add_write (struct thread_master *m, + int (*func) (struct thread *), void *arg, int fd, + debugargdef) +{ + return funcname_thread_add_read_write (THREAD_WRITE, m, func, + arg, fd, debugargpass); +} + static struct thread * 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); @@ -764,16 +846,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; } @@ -782,7 +855,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; @@ -792,14 +866,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; @@ -809,15 +884,26 @@ 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 timer event thread with "millisecond" resolution */ +struct thread * +funcname_thread_add_timer_tv (struct thread_master *m, + int (*func) (struct thread *), + void *arg, struct timeval *tv, + debugargdef) +{ + return funcname_thread_add_timer_timeval (m, func, THREAD_TIMER, + arg, tv, 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; @@ -835,19 +921,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); @@ -858,22 +945,22 @@ 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; + struct thread **thread_array = NULL; switch (thread->type) { case THREAD_READ: - assert (FD_ISSET (thread->u.fd, &thread->master->readfd)); - FD_CLR (thread->u.fd, &thread->master->readfd); - list = &thread->master->read; + assert (fd_clear_read_write (thread->u.fd, &thread->master->readfd)); + thread_array = thread->master->read; break; case THREAD_WRITE: - assert (FD_ISSET (thread->u.fd, &thread->master->writefd)); - FD_CLR (thread->u.fd, &thread->master->writefd); - list = &thread->master->write; + assert (fd_clear_read_write (thread->u.fd, &thread->master->writefd)); + thread_array = thread->master->write; break; case THREAD_TIMER: - list = &thread->master->timer; + queue = thread->master->timer; break; case THREAD_EVENT: list = &thread->master->event; @@ -882,13 +969,32 @@ 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 if (thread_array) + { + thread_delete_fd (thread_array, thread); + } + else + { + assert(!"Thread should be either in queue or list or array!"); + } + thread->type = THREAD_UNUSED; thread_add_unuse (thread->master, thread); } @@ -916,15 +1022,34 @@ thread_cancel_event (struct thread_master *m, void *arg) thread_add_unuse (m, t); } } + + /* thread can be on the ready list too */ + thread = m->ready.head; + while (thread) + { + struct thread *t; + + t = thread; + thread = t->next; + + if (t->arg == arg) + { + ret++; + thread_list_delete (&m->ready, t); + t->type = THREAD_UNUSED; + thread_add_unuse (m, t); + } + } return ret; } 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; @@ -936,51 +1061,67 @@ thread_run (struct thread_master *m, struct thread *thread, { *fetch = *thread; thread->type = THREAD_UNUSED; - thread->funcname = NULL; /* thread_call will free fetch's copied pointer */ thread_add_unuse (m, thread); return fetch; } static int -thread_process_fd (struct thread_list *list, fd_set *fdset, fd_set *mfdset) +thread_process_fds_helper (struct thread_master *m, struct thread *thread, thread_fd_set *fdset) { - struct thread *thread; - struct thread *next; - int ready = 0; - - assert (list); - - for (thread = list->head; thread; thread = next) + thread_fd_set *mfdset = NULL; + struct thread **thread_array; + + if (!thread) + return 0; + + if (thread->type == THREAD_READ) { - next = thread->next; + mfdset = &m->readfd; + thread_array = m->read; + } + else + { + mfdset = &m->writefd; + thread_array = m->write; + } - if (FD_ISSET (THREAD_FD (thread), fdset)) - { - assert (FD_ISSET (THREAD_FD (thread), mfdset)); - FD_CLR(THREAD_FD (thread), mfdset); - thread_list_delete (list, thread); - thread_list_add (&thread->master->ready, thread); - thread->type = THREAD_READY; - ready++; - } + if (fd_is_set (THREAD_FD (thread), fdset)) + { + fd_clear_read_write (THREAD_FD (thread), mfdset); + thread_delete_fd (thread_array, thread); + thread_list_add (&m->ready, thread); + thread->type = THREAD_READY; + return 1; } - return ready; + return 0; +} + +static int +thread_process_fds (struct thread_master *m, thread_fd_set *rset, thread_fd_set *wset, int num) +{ + int ready = 0, index; + + for (index = 0; index < m->fd_limit && ready < num; ++index) + { + ready += thread_process_fds_helper (m, m->read[index], rset); + ready += thread_process_fds_helper (m, m->write[index], wset); + } + return num - ready; } /* 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++; @@ -1013,9 +1154,9 @@ struct thread * thread_fetch (struct thread_master *m, struct thread *fetch) { struct thread *thread; - fd_set readfd; - fd_set writefd; - fd_set exceptfd; + thread_fd_set readfd; + thread_fd_set writefd; + thread_fd_set exceptfd; struct timeval timer_val = { .tv_sec = 0, .tv_usec = 0 }; struct timeval timer_val_bg; struct timeval *timer_wait = &timer_val; @@ -1024,7 +1165,7 @@ thread_fetch (struct thread_master *m, struct thread *fetch) while (1) { int num = 0; - + /* Signals pre-empt everything */ quagga_sigevent_process (); @@ -1043,23 +1184,23 @@ thread_fetch (struct thread_master *m, struct thread *fetch) thread_process (&m->event); /* Structure copy. */ - readfd = m->readfd; - writefd = m->writefd; - exceptfd = m->exceptfd; + readfd = fd_copy_fd_set(m->readfd); + writefd = fd_copy_fd_set(m->writefd); + exceptfd = fd_copy_fd_set(m->exceptfd); /* Calculate select wait timer if nothing else to do */ 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))) timer_wait = timer_wait_bg; } - num = select (FD_SETSIZE, &readfd, &writefd, &exceptfd, timer_wait); + num = fd_select (FD_SETSIZE, &readfd, &writefd, &exceptfd, timer_wait); /* Signals should get quick treatment */ if (num < 0) @@ -1067,23 +1208,18 @@ thread_fetch (struct thread_master *m, struct thread *fetch) if (errno == EINTR) continue; /* signal received - process it */ zlog_warn ("select() error: %s", safe_strerror (errno)); - return NULL; + return NULL; } /* Check foreground timers. Historically, they have had higher 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) - { - /* Normal priority read thead. */ - thread_process_fd (&m->read, &readfd, &m->readfd); - /* Write thead. */ - thread_process_fd (&m->write, &writefd, &m->writefd); - } + thread_process_fds (m, &readfd, &writefd, num); #if 0 /* If any threads were made ready above (I/O or foreground timer), @@ -1095,7 +1231,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); @@ -1128,8 +1264,8 @@ int thread_should_yield (struct thread *thread) { quagga_get_relative (NULL); - return (timeval_elapsed(relative_time, thread->ru.real) > - THREAD_YIELD_TIME_SLOT); + unsigned long t = timeval_elapsed(relative_time, thread->real); + return ((t > THREAD_YIELD_TIME_SLOT) ? t : 0); } void @@ -1150,6 +1286,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. */ @@ -1157,7 +1295,7 @@ void thread_call (struct thread *thread) { unsigned long realtime, cputime; - RUSAGE_T ru; + RUSAGE_T before, after; /* Cache a pointer to the relevant cpu history thread, if the thread * does not have it yet. @@ -1176,13 +1314,16 @@ thread_call (struct thread *thread) (void * (*) (void *))cpu_record_hash_alloc); } - GETRUSAGE (&thread->ru); + GETRUSAGE (&before); + thread->real = before.real; + thread_current = thread; (*thread->func) (thread); + thread_current = NULL; - GETRUSAGE (&ru); + GETRUSAGE (&after); - realtime = thread_consumed_time (&ru, &thread->ru, &cputime); + realtime = thread_consumed_time (&after, &before, &cputime); thread->hist->real.total += realtime; if (thread->hist->real.max < realtime) thread->hist->real.max = realtime; @@ -1209,8 +1350,6 @@ thread_call (struct thread *thread) realtime/1000, cputime/1000); } #endif /* CONSUMED_TIME_CHECK */ - - XFREE (MTYPE_THREAD_FUNCNAME, thread->funcname); } /* Execute thread */ @@ -1219,7 +1358,7 @@ funcname_thread_execute (struct thread_master *m, int (*func)(struct thread *), void *arg, int val, - const char* funcname) + debugargdef) { struct thread dummy; @@ -1231,10 +1370,12 @@ funcname_thread_execute (struct thread_master *m, dummy.func = func; dummy.arg = arg; dummy.u.val = val; - dummy.funcname = strip_funcname (funcname); - thread_call (&dummy); - XFREE (MTYPE_THREAD_FUNCNAME, dummy.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 dfc51e283..3f162163d 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,19 +43,28 @@ struct thread_list int count; }; +struct pqueue; + +/* + * Abstract it so we can use different methodologies to + * select on data. + */ +typedef fd_set thread_fd_set; + /* Master of the theads. */ struct thread_master { - struct thread_list read; - struct thread_list write; - struct thread_list timer; + struct thread **read; + struct thread **write; + struct pqueue *timer; struct thread_list event; struct thread_list ready; struct thread_list unuse; - struct thread_list background; - fd_set readfd; - fd_set writefd; - fd_set exceptfd; + struct pqueue *background; + int fd_limit; + thread_fd_set readfd; + thread_fd_set writefd; + thread_fd_set exceptfd; unsigned long alloc; }; @@ -77,15 +85,17 @@ struct thread int fd; /* file descriptor in case of read/write. */ struct timeval sands; /* rest of time sands value. */ } u; - RUSAGE_T ru; /* Indepth usage info. */ + 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; + const char *funcname; + const char *schedfrom; + int schedfrom_line; }; struct cpu_thread_history { int (*func)(struct thread *); - char *funcname; unsigned int total_calls; struct time_stats { @@ -95,6 +105,7 @@ struct cpu_thread_history struct time_stats cpu; #endif thread_type types; + const char *funcname; }; /* Clocks supported by Quagga */ @@ -159,15 +170,18 @@ 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_timer_tv(m,f,a,v) funcname_thread_add_timer_tv(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); @@ -175,33 +189,41 @@ 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_timer_tv (struct thread_master *, + int (*)(struct thread *), + void *, struct timeval *, + 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 struct timeval thread_timer_remain(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 *); @@ -225,4 +247,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 429474d14..aef1d090b 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/vrf.c b/lib/vrf.c new file mode 100644 index 000000000..29be02a05 --- /dev/null +++ b/lib/vrf.c @@ -0,0 +1,726 @@ +/* + * VRF functions. + * Copyright (C) 2014 6WIND S.A. + * + * 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 + +#ifdef HAVE_NETNS +#undef _GNU_SOURCE +#define _GNU_SOURCE + +#include +#endif + +#include "if.h" +#include "vrf.h" +#include "prefix.h" +#include "table.h" +#include "log.h" +#include "memory.h" +#include "command.h" +#include "vty.h" + + +#ifndef CLONE_NEWNET +#define CLONE_NEWNET 0x40000000 /* New network namespace (lo, device, names sockets, etc) */ +#endif + +#ifndef HAVE_SETNS +static inline int setns(int fd, int nstype) +{ +#ifdef __NR_setns + return syscall(__NR_setns, fd, nstype); +#else + errno = ENOSYS; + return -1; +#endif +} +#endif /* HAVE_SETNS */ + +#define VRF_RUN_DIR "/var/run/netns" + +#ifdef HAVE_NETNS + +#define VRF_DEFAULT_NAME "/proc/self/ns/net" +static int have_netns_enabled = -1; + +#else /* !HAVE_NETNS */ + +#define VRF_DEFAULT_NAME "Default-IP-Routing-Table" + +#endif /* HAVE_NETNS */ + +static int have_netns(void) +{ +#ifdef HAVE_NETNS + if (have_netns_enabled < 0) + { + int fd = open (VRF_DEFAULT_NAME, O_RDONLY); + + if (fd < 0) + have_netns_enabled = 0; + else + { + have_netns_enabled = 1; + close(fd); + } + } + return have_netns_enabled; +#else + return 0; +#endif +} + +struct vrf +{ + /* Identifier, same as the vector index */ + vrf_id_t vrf_id; + /* Name */ + char *name; + /* File descriptor */ + int fd; + + /* Master list of interfaces belonging to this VRF */ + struct list *iflist; + + /* User data */ + void *info; +}; + +/* Holding VRF hooks */ +struct vrf_master +{ + int (*vrf_new_hook) (vrf_id_t, void **); + int (*vrf_delete_hook) (vrf_id_t, void **); + int (*vrf_enable_hook) (vrf_id_t, void **); + int (*vrf_disable_hook) (vrf_id_t, void **); +} vrf_master = {0,}; + +/* VRF table */ +struct route_table *vrf_table = NULL; + +static int vrf_is_enabled (struct vrf *vrf); +static int vrf_enable (struct vrf *vrf); +static void vrf_disable (struct vrf *vrf); + + +/* Build the table key */ +static void +vrf_build_key (vrf_id_t vrf_id, struct prefix *p) +{ + p->family = AF_INET; + p->prefixlen = IPV4_MAX_BITLEN; + p->u.prefix4.s_addr = vrf_id; +} + +/* Get a VRF. If not found, create one. */ +static struct vrf * +vrf_get (vrf_id_t vrf_id) +{ + struct prefix p; + struct route_node *rn; + struct vrf *vrf; + + vrf_build_key (vrf_id, &p); + rn = route_node_get (vrf_table, &p); + if (rn->info) + { + vrf = (struct vrf *)rn->info; + route_unlock_node (rn); /* get */ + return vrf; + } + + vrf = XCALLOC (MTYPE_VRF, sizeof (struct vrf)); + vrf->vrf_id = vrf_id; + vrf->fd = -1; + rn->info = vrf; + + /* Initialize interfaces. */ + if_init (vrf_id, &vrf->iflist); + + zlog_info ("VRF %u is created.", vrf_id); + + if (vrf_master.vrf_new_hook) + (*vrf_master.vrf_new_hook) (vrf_id, &vrf->info); + + return vrf; +} + +/* Delete a VRF. This is called in vrf_terminate(). */ +static void +vrf_delete (struct vrf *vrf) +{ + zlog_info ("VRF %u is to be deleted.", vrf->vrf_id); + + vrf_disable (vrf); + + if (vrf_master.vrf_delete_hook) + (*vrf_master.vrf_delete_hook) (vrf->vrf_id, &vrf->info); + + if_terminate (vrf->vrf_id, &vrf->iflist); + + if (vrf->name) + XFREE (MTYPE_VRF_NAME, vrf->name); + + XFREE (MTYPE_VRF, vrf); +} + +/* Look up a VRF by identifier. */ +static struct vrf * +vrf_lookup (vrf_id_t vrf_id) +{ + struct prefix p; + struct route_node *rn; + struct vrf *vrf = NULL; + + vrf_build_key (vrf_id, &p); + rn = route_node_lookup (vrf_table, &p); + if (rn) + { + vrf = (struct vrf *)rn->info; + route_unlock_node (rn); /* lookup */ + } + return vrf; +} + +/* + * Check whether the VRF is enabled - that is, whether the VRF + * is ready to allocate resources. Currently there's only one + * type of resource: socket. + */ +static int +vrf_is_enabled (struct vrf *vrf) +{ + if (have_netns()) + return vrf && vrf->fd >= 0; + else + return vrf && vrf->fd == -2 && vrf->vrf_id == VRF_DEFAULT; +} + +/* + * Enable a VRF - that is, let the VRF be ready to use. + * The VRF_ENABLE_HOOK callback will be called to inform + * that they can allocate resources in this VRF. + * + * RETURN: 1 - enabled successfully; otherwise, 0. + */ +static int +vrf_enable (struct vrf *vrf) +{ + + if (!vrf_is_enabled (vrf)) + { + if (have_netns()) { + vrf->fd = open (vrf->name, O_RDONLY); + } else { + vrf->fd = -2; /* Remember that vrf_enable_hook has been called */ + errno = -ENOTSUP; + } + + if (!vrf_is_enabled (vrf)) + { + zlog_err ("Can not enable VRF %u: %s!", + vrf->vrf_id, safe_strerror (errno)); + return 0; + } + + if (have_netns()) + zlog_info ("VRF %u is associated with NETNS %s.", + vrf->vrf_id, vrf->name); + + zlog_info ("VRF %u is enabled.", vrf->vrf_id); + if (vrf_master.vrf_enable_hook) + (*vrf_master.vrf_enable_hook) (vrf->vrf_id, &vrf->info); + } + + return 1; +} + +/* + * Disable a VRF - that is, let the VRF be unusable. + * The VRF_DELETE_HOOK callback will be called to inform + * that they must release the resources in the VRF. + */ +static void +vrf_disable (struct vrf *vrf) +{ + if (vrf_is_enabled (vrf)) + { + zlog_info ("VRF %u is to be disabled.", vrf->vrf_id); + + if (vrf_master.vrf_disable_hook) + (*vrf_master.vrf_disable_hook) (vrf->vrf_id, &vrf->info); + + if (have_netns()) + close (vrf->fd); + + vrf->fd = -1; + } +} + + +/* Add a VRF hook. Please add hooks before calling vrf_init(). */ +void +vrf_add_hook (int type, int (*func)(vrf_id_t, void **)) +{ + switch (type) { + case VRF_NEW_HOOK: + vrf_master.vrf_new_hook = func; + break; + case VRF_DELETE_HOOK: + vrf_master.vrf_delete_hook = func; + break; + case VRF_ENABLE_HOOK: + vrf_master.vrf_enable_hook = func; + break; + case VRF_DISABLE_HOOK: + vrf_master.vrf_disable_hook = func; + break; + default: + break; + } +} + +/* Return the iterator of the first VRF. */ +vrf_iter_t +vrf_first (void) +{ + struct route_node *rn; + + for (rn = route_top (vrf_table); rn; rn = route_next (rn)) + if (rn->info) + { + route_unlock_node (rn); /* top/next */ + return (vrf_iter_t)rn; + } + return VRF_ITER_INVALID; +} + +/* Return the next VRF iterator to the given iterator. */ +vrf_iter_t +vrf_next (vrf_iter_t iter) +{ + struct route_node *rn = NULL; + + /* Lock it first because route_next() will unlock it. */ + if (iter != VRF_ITER_INVALID) + rn = route_next (route_lock_node ((struct route_node *)iter)); + + for (; rn; rn = route_next (rn)) + if (rn->info) + { + route_unlock_node (rn); /* next */ + return (vrf_iter_t)rn; + } + return VRF_ITER_INVALID; +} + +/* Return the VRF iterator of the given VRF ID. If it does not exist, + * the iterator of the next existing VRF is returned. */ +vrf_iter_t +vrf_iterator (vrf_id_t vrf_id) +{ + struct prefix p; + struct route_node *rn; + + vrf_build_key (vrf_id, &p); + rn = route_node_get (vrf_table, &p); + if (rn->info) + { + /* OK, the VRF exists. */ + route_unlock_node (rn); /* get */ + return (vrf_iter_t)rn; + } + + /* Find the next VRF. */ + for (rn = route_next (rn); rn; rn = route_next (rn)) + if (rn->info) + { + route_unlock_node (rn); /* next */ + return (vrf_iter_t)rn; + } + + return VRF_ITER_INVALID; +} + +/* Obtain the VRF ID from the given VRF iterator. */ +vrf_id_t +vrf_iter2id (vrf_iter_t iter) +{ + struct route_node *rn = (struct route_node *) iter; + return (rn && rn->info) ? ((struct vrf *)rn->info)->vrf_id : VRF_DEFAULT; +} + +/* Obtain the data pointer from the given VRF iterator. */ +void * +vrf_iter2info (vrf_iter_t iter) +{ + struct route_node *rn = (struct route_node *) iter; + return (rn && rn->info) ? ((struct vrf *)rn->info)->info : NULL; +} + +/* Obtain the interface list from the given VRF iterator. */ +struct list * +vrf_iter2iflist (vrf_iter_t iter) +{ + struct route_node *rn = (struct route_node *) iter; + return (rn && rn->info) ? ((struct vrf *)rn->info)->iflist : NULL; +} + +/* Get the data pointer of the specified VRF. If not found, create one. */ +void * +vrf_info_get (vrf_id_t vrf_id) +{ + struct vrf *vrf = vrf_get (vrf_id); + return vrf->info; +} + +/* Look up the data pointer of the specified VRF. */ +void * +vrf_info_lookup (vrf_id_t vrf_id) +{ + struct vrf *vrf = vrf_lookup (vrf_id); + return vrf ? vrf->info : NULL; +} + +/* Look up the interface list in a VRF. */ +struct list * +vrf_iflist (vrf_id_t vrf_id) +{ + struct vrf * vrf = vrf_lookup (vrf_id); + return vrf ? vrf->iflist : NULL; +} + +/* Get the interface list of the specified VRF. Create one if not find. */ +struct list * +vrf_iflist_get (vrf_id_t vrf_id) +{ + struct vrf * vrf = vrf_get (vrf_id); + return vrf->iflist; +} + +/* + * VRF bit-map + */ + +#define VRF_BITMAP_NUM_OF_GROUPS 8 +#define VRF_BITMAP_NUM_OF_BITS_IN_GROUP \ + (UINT16_MAX / VRF_BITMAP_NUM_OF_GROUPS) +#define VRF_BITMAP_NUM_OF_BYTES_IN_GROUP \ + (VRF_BITMAP_NUM_OF_BITS_IN_GROUP / CHAR_BIT + 1) /* +1 for ensure */ + +#define VRF_BITMAP_GROUP(_id) \ + ((_id) / VRF_BITMAP_NUM_OF_BITS_IN_GROUP) +#define VRF_BITMAP_BIT_OFFSET(_id) \ + ((_id) % VRF_BITMAP_NUM_OF_BITS_IN_GROUP) + +#define VRF_BITMAP_INDEX_IN_GROUP(_bit_offset) \ + ((_bit_offset) / CHAR_BIT) +#define VRF_BITMAP_FLAG(_bit_offset) \ + (((u_char)1) << ((_bit_offset) % CHAR_BIT)) + +struct vrf_bitmap +{ + u_char *groups[VRF_BITMAP_NUM_OF_GROUPS]; +}; + +vrf_bitmap_t +vrf_bitmap_init (void) +{ + return (vrf_bitmap_t) XCALLOC (MTYPE_VRF_BITMAP, sizeof (struct vrf_bitmap)); +} + +void +vrf_bitmap_free (vrf_bitmap_t bmap) +{ + struct vrf_bitmap *bm = (struct vrf_bitmap *) bmap; + int i; + + if (bmap == VRF_BITMAP_NULL) + return; + + for (i = 0; i < VRF_BITMAP_NUM_OF_GROUPS; i++) + if (bm->groups[i]) + XFREE (MTYPE_VRF_BITMAP, bm->groups[i]); + + XFREE (MTYPE_VRF_BITMAP, bm); +} + +void +vrf_bitmap_set (vrf_bitmap_t bmap, vrf_id_t vrf_id) +{ + struct vrf_bitmap *bm = (struct vrf_bitmap *) bmap; + u_char group = VRF_BITMAP_GROUP (vrf_id); + u_char offset = VRF_BITMAP_BIT_OFFSET (vrf_id); + + if (bmap == VRF_BITMAP_NULL) + return; + + if (bm->groups[group] == NULL) + bm->groups[group] = XCALLOC (MTYPE_VRF_BITMAP, + VRF_BITMAP_NUM_OF_BYTES_IN_GROUP); + + SET_FLAG (bm->groups[group][VRF_BITMAP_INDEX_IN_GROUP (offset)], + VRF_BITMAP_FLAG (offset)); +} + +void +vrf_bitmap_unset (vrf_bitmap_t bmap, vrf_id_t vrf_id) +{ + struct vrf_bitmap *bm = (struct vrf_bitmap *) bmap; + u_char group = VRF_BITMAP_GROUP (vrf_id); + u_char offset = VRF_BITMAP_BIT_OFFSET (vrf_id); + + if (bmap == VRF_BITMAP_NULL || bm->groups[group] == NULL) + return; + + UNSET_FLAG (bm->groups[group][VRF_BITMAP_INDEX_IN_GROUP (offset)], + VRF_BITMAP_FLAG (offset)); +} + +int +vrf_bitmap_check (vrf_bitmap_t bmap, vrf_id_t vrf_id) +{ + struct vrf_bitmap *bm = (struct vrf_bitmap *) bmap; + u_char group = VRF_BITMAP_GROUP (vrf_id); + u_char offset = VRF_BITMAP_BIT_OFFSET (vrf_id); + + if (bmap == VRF_BITMAP_NULL || bm->groups[group] == NULL) + return 0; + + return CHECK_FLAG (bm->groups[group][VRF_BITMAP_INDEX_IN_GROUP (offset)], + VRF_BITMAP_FLAG (offset)) ? 1 : 0; +} + +/* + * VRF realization with NETNS + */ + +static char * +vrf_netns_pathname (struct vty *vty, const char *name) +{ + static char pathname[PATH_MAX]; + char *result; + + if (name[0] == '/') /* absolute pathname */ + result = realpath (name, pathname); + else /* relevant pathname */ + { + char tmp_name[PATH_MAX]; + snprintf (tmp_name, PATH_MAX, "%s/%s", VRF_RUN_DIR, name); + result = realpath (tmp_name, pathname); + } + + if (! result) + { + vty_out (vty, "Invalid pathname: %s%s", safe_strerror (errno), + VTY_NEWLINE); + return NULL; + } + return pathname; +} + +DEFUN (vrf_netns, + vrf_netns_cmd, + "vrf <1-65535> netns NAME", + "Enable a VRF\n" + "Specify the VRF identifier\n" + "Associate with a NETNS\n" + "The file name in " VRF_RUN_DIR ", or a full pathname\n") +{ + vrf_id_t vrf_id = VRF_DEFAULT; + struct vrf *vrf = NULL; + char *pathname = vrf_netns_pathname (vty, argv[1]); + + if (!pathname) + return CMD_WARNING; + + VTY_GET_INTEGER ("VRF ID", vrf_id, argv[0]); + vrf = vrf_get (vrf_id); + + if (vrf->name && strcmp (vrf->name, pathname) != 0) + { + vty_out (vty, "VRF %u is already configured with NETNS %s%s", + vrf->vrf_id, vrf->name, VTY_NEWLINE); + return CMD_WARNING; + } + + if (!vrf->name) + vrf->name = XSTRDUP (MTYPE_VRF_NAME, pathname); + + if (!vrf_enable (vrf)) + { + vty_out (vty, "Can not associate VRF %u with NETNS %s%s", + vrf->vrf_id, vrf->name, VTY_NEWLINE); + return CMD_WARNING; + } + + return CMD_SUCCESS; +} + +DEFUN (no_vrf_netns, + no_vrf_netns_cmd, + "no vrf <1-65535> netns NAME", + NO_STR + "Enable a VRF\n" + "Specify the VRF identifier\n" + "Associate with a NETNS\n" + "The file name in " VRF_RUN_DIR ", or a full pathname\n") +{ + vrf_id_t vrf_id = VRF_DEFAULT; + struct vrf *vrf = NULL; + char *pathname = vrf_netns_pathname (vty, argv[1]); + + if (!pathname) + return CMD_WARNING; + + VTY_GET_INTEGER ("VRF ID", vrf_id, argv[0]); + vrf = vrf_lookup (vrf_id); + + if (!vrf) + { + vty_out (vty, "VRF %u is not found%s", vrf_id, VTY_NEWLINE); + return CMD_SUCCESS; + } + + if (vrf->name && strcmp (vrf->name, pathname) != 0) + { + vty_out (vty, "Incorrect NETNS file name%s", VTY_NEWLINE); + return CMD_WARNING; + } + + vrf_disable (vrf); + + if (vrf->name) + { + XFREE (MTYPE_VRF_NAME, vrf->name); + vrf->name = NULL; + } + + return CMD_SUCCESS; +} + +/* VRF node. */ +static struct cmd_node vrf_node = +{ + VRF_NODE, + "", /* VRF node has no interface. */ + 1 +}; + +/* VRF configuration write function. */ +static int +vrf_config_write (struct vty *vty) +{ + struct route_node *rn; + struct vrf *vrf; + int write = 0; + + for (rn = route_top (vrf_table); rn; rn = route_next (rn)) + if ((vrf = rn->info) != NULL && + vrf->vrf_id != VRF_DEFAULT && vrf->name) + { + vty_out (vty, "vrf %u netns %s%s", vrf->vrf_id, vrf->name, VTY_NEWLINE); + write++; + } + + return write; +} + +/* Initialize VRF module. */ +void +vrf_init (void) +{ + struct vrf *default_vrf; + + /* Allocate VRF table. */ + vrf_table = route_table_init (); + + /* The default VRF always exists. */ + default_vrf = vrf_get (VRF_DEFAULT); + if (!default_vrf) + { + zlog_err ("vrf_init: failed to create the default VRF!"); + exit (1); + } + + /* Set the default VRF name. */ + default_vrf->name = XSTRDUP (MTYPE_VRF_NAME, VRF_DEFAULT_NAME); + + /* Enable the default VRF. */ + if (!vrf_enable (default_vrf)) + { + zlog_err ("vrf_init: failed to enable the default VRF!"); + exit (1); + } + + if (have_netns()) + { + /* Install VRF commands. */ + install_node (&vrf_node, vrf_config_write); + install_element (CONFIG_NODE, &vrf_netns_cmd); + install_element (CONFIG_NODE, &no_vrf_netns_cmd); + } +} + +/* Terminate VRF module. */ +void +vrf_terminate (void) +{ + struct route_node *rn; + struct vrf *vrf; + + for (rn = route_top (vrf_table); rn; rn = route_next (rn)) + if ((vrf = rn->info) != NULL) + vrf_delete (vrf); + + route_table_finish (vrf_table); + vrf_table = NULL; +} + +/* Create a socket for the VRF. */ +int +vrf_socket (int domain, int type, int protocol, vrf_id_t vrf_id) +{ + struct vrf *vrf = vrf_lookup (vrf_id); + int ret = -1; + + if (!vrf_is_enabled (vrf)) + { + errno = ENOSYS; + return -1; + } + + if (have_netns()) + { + ret = (vrf_id != VRF_DEFAULT) ? setns (vrf->fd, CLONE_NEWNET) : 0; + if (ret >= 0) + { + ret = socket (domain, type, protocol); + if (vrf_id != VRF_DEFAULT) + setns (vrf_lookup (VRF_DEFAULT)->fd, CLONE_NEWNET); + } + } + else + ret = socket (domain, type, protocol); + + return ret; +} diff --git a/lib/vrf.h b/lib/vrf.h new file mode 100644 index 000000000..8b29b8093 --- /dev/null +++ b/lib/vrf.h @@ -0,0 +1,140 @@ +/* + * VRF related header. + * Copyright (C) 2014 6WIND S.A. + * + * 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_VRF_H +#define _ZEBRA_VRF_H + +#include "linklist.h" + +/* The default VRF ID */ +#define VRF_DEFAULT 0 + +/* + * The command strings + */ + +#define VRF_CMD_STR "vrf <0-65535>" +#define VRF_CMD_HELP_STR "Specify the VRF\nThe VRF ID\n" + +#define VRF_ALL_CMD_STR "vrf all" +#define VRF_ALL_CMD_HELP_STR "Specify the VRF\nAll VRFs\n" + +/* + * VRF hooks + */ + +#define VRF_NEW_HOOK 0 /* a new VRF is just created */ +#define VRF_DELETE_HOOK 1 /* a VRF is to be deleted */ +#define VRF_ENABLE_HOOK 2 /* a VRF is ready to use */ +#define VRF_DISABLE_HOOK 3 /* a VRF is to be unusable */ + +/* + * Add a specific hook to VRF module. + * @param1: hook type + * @param2: the callback function + * - param 1: the VRF ID + * - param 2: the address of the user data pointer (the user data + * can be stored in or freed from there) + */ +extern void vrf_add_hook (int, int (*)(vrf_id_t, void **)); + +/* + * VRF iteration + */ + +typedef void * vrf_iter_t; +#define VRF_ITER_INVALID NULL /* invalid value of the iterator */ + +/* + * VRF iteration utilities. Example for the usage: + * + * vrf_iter_t iter = vrf_first(); + * for (; iter != VRF_ITER_INVALID; iter = vrf_next (iter)) + * + * or + * + * vrf_iter_t iter = vrf_iterator (); + * for (; iter != VRF_ITER_INVALID; iter = vrf_next (iter)) + */ + +/* Return the iterator of the first VRF. */ +extern vrf_iter_t vrf_first (void); +/* Return the next VRF iterator to the given iterator. */ +extern vrf_iter_t vrf_next (vrf_iter_t); +/* Return the VRF iterator of the given VRF ID. If it does not exist, + * the iterator of the next existing VRF is returned. */ +extern vrf_iter_t vrf_iterator (vrf_id_t); + +/* + * VRF iterator to properties + */ +extern vrf_id_t vrf_iter2id (vrf_iter_t); +extern void *vrf_iter2info (vrf_iter_t); +extern struct list *vrf_iter2iflist (vrf_iter_t); + +/* + * Utilities to obtain the user data + */ + +/* Get the data pointer of the specified VRF. If not found, create one. */ +extern void *vrf_info_get (vrf_id_t); +/* Look up the data pointer of the specified VRF. */ +extern void *vrf_info_lookup (vrf_id_t); + +/* + * Utilities to obtain the interface list + */ + +/* Look up the interface list of the specified VRF. */ +extern struct list *vrf_iflist (vrf_id_t); +/* Get the interface list of the specified VRF. Create one if not find. */ +extern struct list *vrf_iflist_get (vrf_id_t); + +/* + * VRF bit-map: maintaining flags, one bit per VRF ID + */ + +typedef void * vrf_bitmap_t; +#define VRF_BITMAP_NULL NULL + +extern vrf_bitmap_t vrf_bitmap_init (void); +extern void vrf_bitmap_free (vrf_bitmap_t); +extern void vrf_bitmap_set (vrf_bitmap_t, vrf_id_t); +extern void vrf_bitmap_unset (vrf_bitmap_t, vrf_id_t); +extern int vrf_bitmap_check (vrf_bitmap_t, vrf_id_t); + +/* + * VRF initializer/destructor + */ +/* Please add hooks before calling vrf_init(). */ +extern void vrf_init (void); +extern void vrf_terminate (void); + +/* + * VRF utilities + */ + +/* Create a socket serving for the given VRF */ +extern int vrf_socket (int, int, int, vrf_id_t); + +#endif /*_ZEBRA_VRF_H*/ + diff --git a/lib/vty.c b/lib/vty.c index 9a4efe641..7ca835436 100644 --- a/lib/vty.c +++ b/lib/vty.c @@ -38,6 +38,9 @@ #include "network.h" #include +#include + +#define VTY_BUFSIZ 4096 /* Vty events */ enum event @@ -57,7 +60,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 +74,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 +92,25 @@ static u_char restricted_mode = 0; /* Integrated configuration file path */ char integrate_default[] = SYSCONFDIR INTEGRATE_DEFAULT_CONFIG; - +static int do_log_commands = 0; + +static void +vty_buf_assert (struct vty *vty) +{ + assert (vty->cp <= vty->length); + assert (vty->length < vty->max); + assert (vty->buf[vty->length] == '\0'); +} + +/* Sanity/safety wrappers around access to vty->buf */ +static void +vty_buf_put (struct vty *vty, char c) +{ + vty_buf_assert (vty); + vty->buf[vty->cp] = c; + vty->buf[vty->max - 1] = '\0'; +} + /* VTY standard output function. */ int vty_out (struct vty *vty, const char *format, ...) @@ -110,7 +131,7 @@ vty_out (struct vty *vty, const char *format, ...) { /* Try to write to initial buffer. */ va_start (args, format); - len = vsnprintf (buf, sizeof buf, format, args); + len = vsnprintf (buf, sizeof(buf), format, args); va_end (args); /* Initial buffer is not enough. */ @@ -184,7 +205,7 @@ vty_log_out (struct vty *vty, const char *level, const char *proto_str, buf[len++] = '\r'; buf[len++] = '\n'; - if (write(vty->fd, buf, len) < 0) + if (write(vty->wfd, buf, len) < 0) { if (ERRNO_IO_RETRY(errno)) /* Kernel buffer is full, probably too much debugging output, so just @@ -208,7 +229,7 @@ vty_log_out (struct vty *vty, const char *level, const char *proto_str, void vty_time_print (struct vty *vty, int cr) { - char buf [25]; + char buf[QUAGGA_TIMESTAMP_LEN]; if (quagga_timestamp(0, buf, sizeof(buf)) == 0) { @@ -400,7 +421,42 @@ vty_command (struct vty *vty, char *buf) int ret; vector vline; const char *protocolname; + char *cp = NULL; + + /* + * Log non empty command lines + */ + if (do_log_commands) + cp = buf; + if (cp != NULL) + { + /* Skip white spaces. */ + while (isspace ((int) *cp) && *cp != '\0') + cp++; + } + if (cp != NULL && *cp != '\0') + { + unsigned i; + char vty_str[VTY_BUFSIZ]; + char prompt_str[VTY_BUFSIZ]; + + /* format the base vty info */ + snprintf(vty_str, sizeof(vty_str), "vty[??]@%s", vty->address); + if (vty) + for (i = 0; i < vector_active (vtyvec); i++) + if (vty == vector_slot (vtyvec, i)) + { + snprintf(vty_str, sizeof(vty_str), "vty[%d]@%s", + i, vty->address); + break; + } + /* format the prompt */ + snprintf(prompt_str, sizeof(prompt_str), cmd_prompt (vty->node), vty_str); + + /* now log the command */ + zlog(NULL, LOG_ERR, "%s%s", prompt_str, buf); + } /* Split readline string up into the vector */ vline = cmd_make_strvec (buf); @@ -455,7 +511,7 @@ vty_command (struct vty *vty, char *buf) return ret; } - + static const char telnet_backward_char = 0x08; static const char telnet_space_char = ' '; @@ -470,84 +526,104 @@ vty_write (struct vty *vty, const char *buf, size_t nbytes) buffer_put (vty->obuf, buf, nbytes); } -/* Ensure length of input buffer. Is buffer is short, double it. */ -static void -vty_ensure (struct vty *vty, int length) -{ - if (vty->max <= length) - { - vty->max *= 2; - vty->buf = XREALLOC (MTYPE_VTY, vty->buf, vty->max); - } -} - /* Basic function to insert character into vty. */ static void vty_self_insert (struct vty *vty, char c) { int i; int length; + + vty_buf_assert (vty); + + /* length is sans nul, max is with */ + if (vty->length + 1 >= vty->max) + return; - vty_ensure (vty, vty->length + 1); length = vty->length - vty->cp; memmove (&vty->buf[vty->cp + 1], &vty->buf[vty->cp], length); - vty->buf[vty->cp] = c; + vty->length++; + vty->buf[vty->length] = '\0'; + + vty_buf_put (vty, c); vty_write (vty, &vty->buf[vty->cp], length + 1); for (i = 0; i < length; i++) vty_write (vty, &telnet_backward_char, 1); vty->cp++; - vty->length++; + + vty_buf_assert (vty); } /* Self insert character 'c' in overwrite mode. */ static void vty_self_insert_overwrite (struct vty *vty, char c) { - vty_ensure (vty, vty->length + 1); - vty->buf[vty->cp++] = c; - - if (vty->cp > vty->length) - vty->length++; - - if ((vty->node == AUTH_NODE) || (vty->node == AUTH_ENABLE_NODE)) - return; + vty_buf_assert (vty); + + if (vty->cp == vty->length) + { + vty_self_insert (vty, c); + return; + } + vty_buf_put (vty, c); + vty->cp++; + + vty_buf_assert (vty); + vty_write (vty, &c, 1); } -/* Insert a word into vty interface with overwrite mode. */ +/** + * Insert a string into vty->buf at the current cursor position. + * + * If the resultant string would be larger than VTY_BUFSIZ it is + * truncated to fit. + */ static void vty_insert_word_overwrite (struct vty *vty, char *str) { - int len = strlen (str); - vty_write (vty, str, len); - strcpy (&vty->buf[vty->cp], str); - vty->cp += len; + vty_buf_assert (vty); + + size_t nwrite = MIN ((int) strlen (str), vty->max - vty->cp - 1); + memcpy (&vty->buf[vty->cp], str, nwrite); + vty->cp += nwrite; vty->length = vty->cp; + vty->buf[vty->length] = '\0'; + vty_buf_assert (vty); + + vty_write (vty, str, nwrite); } /* Forward character. */ static void vty_forward_char (struct vty *vty) { + vty_buf_assert (vty); + if (vty->cp < vty->length) { vty_write (vty, &vty->buf[vty->cp], 1); vty->cp++; } + + vty_buf_assert (vty); } /* Backward character. */ static void vty_backward_char (struct vty *vty) { + vty_buf_assert (vty); + if (vty->cp > 0) { vty->cp--; vty_write (vty, &telnet_backward_char, 1); } + + vty_buf_assert (vty); } /* Move to the beginning of the line. */ @@ -582,7 +658,9 @@ vty_history_print (struct vty *vty) length = strlen (vty->hist[vty->hp]); memcpy (vty->buf, vty->hist[vty->hp], length); vty->cp = vty->length = length; - + vty->buf[vty->length] = '\0'; + vty_buf_assert (vty); + /* Redraw current line */ vty_redraw_line (vty); } @@ -638,6 +716,8 @@ vty_redraw_line (struct vty *vty) { vty_write (vty, vty->buf, vty->length); vty->cp = vty->length; + + vty_buf_assert (vty); } /* Forward word. */ @@ -702,6 +782,9 @@ vty_end_config (struct vty *vty) case BABEL_NODE: case BGP_NODE: case BGP_VPNV4_NODE: + case BGP_VPNV6_NODE: + case BGP_ENCAP_NODE: + case BGP_ENCAPV6_NODE: case BGP_IPV4_NODE: case BGP_IPV4M_NODE: case BGP_IPV6_NODE: @@ -713,6 +796,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; @@ -738,10 +822,12 @@ vty_delete_char (struct vty *vty) vty_down_level (vty); return; } - + if (vty->cp == vty->length) return; /* completion need here? */ + vty_buf_assert (vty); + size = vty->length - vty->cp; vty->length--; @@ -788,6 +874,7 @@ vty_kill_line (struct vty *vty) memset (&vty->buf[vty->cp], 0, size); vty->length = vty->cp; + vty_buf_assert (vty); } /* Kill line from the beginning. */ @@ -869,9 +956,9 @@ vty_complete_command (struct vty *vty) /* In case of 'help \t'. */ if (isspace ((int) vty->buf[vty->length - 1])) - vector_set (vline, '\0'); + vector_set (vline, NULL); - matched = cmd_complete_command (vline, vty, &ret); + matched = cmd_complete_command_lib (vline, vty, &ret, 1); cmd_free_strvec (vline); @@ -931,23 +1018,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 +1063,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); @@ -984,11 +1071,11 @@ vty_describe_command (struct vty *vty) if (vline == NULL) { vline = vector_init (1); - vector_set (vline, '\0'); + vector_set (vline, NULL); } else if (isspace ((int) vty->buf[vty->length - 1])) - vector_set (vline, '\0'); + vector_set (vline, NULL); describe = cmd_describe_command (vline, vty, &ret); @@ -1010,15 +1097,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 +1117,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 +1146,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 +1204,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; @@ -1298,8 +1386,9 @@ vty_execute (struct vty *vty) #define CONTROL(X) ((X) - '@') #define VTY_NORMAL 0 -#define VTY_PRE_ESCAPE 1 -#define VTY_ESCAPE 2 +#define VTY_PRE_ESCAPE 1 /* Esc seen */ +#define VTY_ESCAPE 2 /* ANSI terminal escape (Esc-[) seen */ +#define VTY_LITERAL 3 /* Next char taken as literal */ /* Escape character command map. */ static void @@ -1361,8 +1450,8 @@ vty_read (struct thread *thread) vty->monitor = 0; /* disable monitoring to avoid infinite recursion */ zlog_warn("%s: read error on vty client fd %d, closing: %s", __func__, vty->fd, safe_strerror(errno)); + buffer_reset(vty->obuf); } - buffer_reset(vty->obuf); vty->status = VTY_CLOSE; } @@ -1427,7 +1516,14 @@ vty_read (struct thread *thread) vty_escape_map (buf[i], vty); continue; } - + + if (vty->escape == VTY_LITERAL) + { + vty_self_insert (vty, buf[i]); + vty->escape = VTY_NORMAL; + continue; + } + /* Pre-escape status. */ if (vty->escape == VTY_PRE_ESCAPE) { @@ -1499,6 +1595,9 @@ vty_read (struct thread *thread) case CONTROL('U'): vty_kill_line_from_beginning (vty); break; + case CONTROL('V'): + vty->escape = VTY_LITERAL; + break; case CONTROL('W'): vty_backward_kill_word (vty); break; @@ -1540,7 +1639,7 @@ vty_read (struct thread *thread) vty_close (vty); else { - vty_event (VTY_WRITE, vty_sock, vty); + vty_event (VTY_WRITE, vty->wfd, vty); vty_event (VTY_READ, vty_sock, vty); } return 0; @@ -1568,13 +1667,13 @@ vty_flush (struct thread *thread) erase = ((vty->status == VTY_MORE || vty->status == VTY_MORELINE)); /* N.B. if width is 0, that means we don't know the window size. */ - if ((vty->lines == 0) || (vty->width == 0)) - flushrc = buffer_flush_available(vty->obuf, vty->fd); + if ((vty->lines == 0) || (vty->width == 0) || (vty->height == 0)) + flushrc = buffer_flush_available(vty->obuf, vty_sock); else if (vty->status == VTY_MORELINE) - flushrc = buffer_flush_window(vty->obuf, vty->fd, vty->width, + flushrc = buffer_flush_window(vty->obuf, vty_sock, vty->width, 1, erase, 0); else - flushrc = buffer_flush_window(vty->obuf, vty->fd, vty->width, + flushrc = buffer_flush_window(vty->obuf, vty_sock, vty->width, vty->lines >= 0 ? vty->lines : vty->height, erase, 0); @@ -1608,28 +1707,17 @@ vty_flush (struct thread *thread) return 0; } -/* Create new vty structure. */ +/* allocate and initialise vty */ static struct vty * -vty_create (int vty_sock, union sockunion *su) +vty_new_init (int vty_sock) { struct vty *vty; - /* Allocate new vty structure and set up default values. */ vty = vty_new (); vty->fd = vty_sock; + vty->wfd = vty_sock; vty->type = VTY_TERM; - vty->address = sockunion_su2str (su); - if (no_password_check) - { - if (restricted_mode) - vty->node = RESTRICTED_NODE; - else if (host.advanced) - vty->node = ENABLE_NODE; - else - vty->node = VIEW_NODE; - } - else - vty->node = AUTH_NODE; + vty->node = AUTH_NODE; vty->fail = 0; vty->cp = 0; vty_clear_buf (vty); @@ -1639,15 +1727,41 @@ vty_create (int vty_sock, union sockunion *su) vty->hindex = 0; vector_set_index (vtyvec, vty_sock, vty); vty->status = VTY_NORMAL; - vty->v_timeout = vty_timeout_val; - if (host.lines >= 0) - vty->lines = host.lines; - else - vty->lines = -1; + vty->lines = -1; vty->iac = 0; vty->iac_sb_in_progress = 0; vty->sb_len = 0; + return vty; +} + +/* Create new vty structure. */ +static struct vty * +vty_create (int vty_sock, union sockunion *su) +{ + char buf[SU_ADDRSTRLEN]; + struct vty *vty; + + sockunion2str(su, buf, SU_ADDRSTRLEN); + + /* Allocate new vty structure and set up default values. */ + vty = vty_new_init (vty_sock); + + /* configurable parameters not part of basic init */ + vty->v_timeout = vty_timeout_val; + strcpy (vty->address, buf); + if (no_password_check) + { + if (restricted_mode) + vty->node = RESTRICTED_NODE; + else if (host.advanced) + vty->node = ENABLE_NODE; + else + vty->node = VIEW_NODE; + } + if (host.lines >= 0) + vty->lines = host.lines; + if (! no_password_check) { /* Vty is not available if password isn't set. */ @@ -1682,6 +1796,66 @@ vty_create (int vty_sock, union sockunion *su) return vty; } +/* create vty for stdio */ +static struct termios stdio_orig_termios; +static struct vty *stdio_vty = NULL; +static void (*stdio_vty_atclose)(void); + +static void +vty_stdio_reset (void) +{ + if (stdio_vty) + { + tcsetattr (0, TCSANOW, &stdio_orig_termios); + stdio_vty = NULL; + + if (stdio_vty_atclose) + stdio_vty_atclose (); + stdio_vty_atclose = NULL; + } +} + +struct vty * +vty_stdio (void (*atclose)()) +{ + struct vty *vty; + struct termios termios; + + /* refuse creating two vtys on stdio */ + if (stdio_vty) + return NULL; + + vty = stdio_vty = vty_new_init (0); + stdio_vty_atclose = atclose; + vty->wfd = 1; + + /* always have stdio vty in a known _unchangeable_ state, don't want config + * to have any effect here to make sure scripting this works as intended */ + vty->node = ENABLE_NODE; + vty->v_timeout = 0; + strcpy (vty->address, "console"); + + if (!tcgetattr (0, &stdio_orig_termios)) + { + termios = stdio_orig_termios; + termios.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP + | INLCR | IGNCR | ICRNL | IXON); + termios.c_oflag &= ~OPOST; + termios.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); + termios.c_cflag &= ~(CSIZE | PARENB); + termios.c_cflag |= CS8; + tcsetattr (0, TCSANOW, &termios); + } + + vty_prompt (vty); + + /* Add read/write thread. */ + vty_event (VTY_WRITE, 1, vty); + vty_event (VTY_READ, 0, vty); + + return vty; +} + /* Accept connection from the network. */ static int vty_accept (struct thread *thread) @@ -1691,9 +1865,9 @@ vty_accept (struct thread *thread) int ret; unsigned int on; int accept_sock; - struct prefix *p = NULL; + struct prefix p; struct access_list *acl = NULL; - char *bufp; + char buf[SU_ADDRSTRLEN]; accept_sock = THREAD_FD (thread); @@ -1711,54 +1885,44 @@ vty_accept (struct thread *thread) } set_nonblocking(vty_sock); - p = sockunion2hostprefix (&su); + sockunion2hostprefix (&su, &p); /* VTY's accesslist apply. */ - if (p->family == AF_INET && vty_accesslist_name) + if (p.family == AF_INET && vty_accesslist_name) { if ((acl = access_list_lookup (AFI_IP, vty_accesslist_name)) && - (access_list_apply (acl, p) == FILTER_DENY)) + (access_list_apply (acl, &p) == FILTER_DENY)) { - char *buf; zlog (NULL, LOG_INFO, "Vty connection refused from %s", - (buf = sockunion_su2str (&su))); - free (buf); + sockunion2str (&su, buf, SU_ADDRSTRLEN)); close (vty_sock); /* continue accepting connections */ vty_event (VTY_SERV, accept_sock, NULL); - prefix_free (p); - return 0; } } #ifdef HAVE_IPV6 /* VTY's ipv6 accesslist apply. */ - if (p->family == AF_INET6 && vty_ipv6_accesslist_name) + if (p.family == AF_INET6 && vty_ipv6_accesslist_name) { if ((acl = access_list_lookup (AFI_IP6, vty_ipv6_accesslist_name)) && - (access_list_apply (acl, p) == FILTER_DENY)) + (access_list_apply (acl, &p) == FILTER_DENY)) { - char *buf; zlog (NULL, LOG_INFO, "Vty connection refused from %s", - (buf = sockunion_su2str (&su))); - free (buf); + sockunion2str (&su, buf, SU_ADDRSTRLEN)); close (vty_sock); /* continue accepting connections */ vty_event (VTY_SERV, accept_sock, NULL); - prefix_free (p); - return 0; } } #endif /* HAVE_IPV6 */ - prefix_free (p); - on = 1; ret = setsockopt (vty_sock, IPPROTO_TCP, TCP_NODELAY, (char *) &on, sizeof (on)); @@ -1767,16 +1931,14 @@ vty_accept (struct thread *thread) safe_strerror (errno)); zlog (NULL, LOG_INFO, "Vty connection from %s", - (bufp = sockunion_su2str (&su))); - if (bufp) - XFREE (MTYPE_TMP, bufp); + sockunion2str (&su, buf, SU_ADDRSTRLEN)); vty_create (vty_sock, &su); return 0; } -#if defined(HAVE_IPV6) && !defined(NRL) +#ifdef HAVE_IPV6 static void vty_serv_sock_addrinfo (const char *hostname, unsigned short port) { @@ -1841,7 +2003,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 @@ -1859,9 +2021,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 } @@ -1907,7 +2071,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. */ @@ -2021,6 +2185,7 @@ vtysh_accept (struct thread *thread) vty = vty_new (); vty->fd = sock; + vty->wfd = sock; vty->type = VTY_SHELL_SERV; vty->node = VIEW_NODE; @@ -2032,10 +2197,10 @@ vtysh_accept (struct thread *thread) static int vtysh_flush(struct vty *vty) { - switch (buffer_flush_available(vty->obuf, vty->fd)) + switch (buffer_flush_available(vty->obuf, vty->wfd)) { case BUFFER_PENDING: - vty_event(VTYSH_WRITE, vty->fd, vty); + vty_event(VTYSH_WRITE, vty->wfd, vty); break; case BUFFER_ERROR: vty->monitor = 0; /* disable monitoring to avoid infinite recursion */ @@ -2090,12 +2255,21 @@ vtysh_read (struct thread *thread) printf ("line: %.*s\n", nbytes, buf); #endif /* VTYSH_DEBUG */ + if (vty->length + nbytes >= vty->max) + { + /* Clear command line buffer. */ + vty->cp = vty->length = 0; + vty_clear_buf (vty); + vty_out (vty, "%% Command is too long.%s", VTY_NEWLINE); + goto out; + } + for (p = buf; p < buf+nbytes; p++) { - vty_ensure(vty, vty->length+1); vty->buf[vty->length++] = *p; if (*p == '\0') { + /* Pass this line to parser. */ ret = vty_execute (vty); /* Note that vty_execute clears the command buffer and resets @@ -2116,6 +2290,7 @@ vtysh_read (struct thread *thread) } } +out: vty_event (VTYSH_READ, sock, vty); return 0; @@ -2142,12 +2317,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 */ @@ -2176,7 +2346,7 @@ vty_close (struct vty *vty) thread_cancel (vty->t_timeout); /* Flush buffer. */ - buffer_flush_all (vty->obuf, vty->fd); + buffer_flush_all (vty->obuf, vty->wfd); /* Free input buffer. */ buffer_free (vty->obuf); @@ -2192,9 +2362,9 @@ vty_close (struct vty *vty) /* Close socket. */ if (vty->fd > 0) close (vty->fd); + else + vty_stdio_reset (); - if (vty->address) - XFREE (MTYPE_TMP, vty->address); if (vty->buf) XFREE (MTYPE_VTY, vty->buf); @@ -2232,28 +2402,38 @@ 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->wfd = dup(STDERR_FILENO); /* vty_close() will close this */ + if (vty->wfd < 0) + { + /* Fine, we couldn't make a new fd. vty_close doesn't close stdout. */ + vty->wfd = STDOUT_FILENO; + } + vty->fd = STDIN_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); } @@ -2452,7 +2632,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]; @@ -2461,7 +2641,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; @@ -2472,7 +2652,7 @@ vty_log_fixed (const char *buf, size_t len) if (((vty = vector_slot (vtyvec, i)) != NULL) && vty->monitor) /* N.B. We don't care about the return code, since process is most likely just about to die anyway. */ - writev(vty->fd, iov, 2); + writev(vty->wfd, iov, 2); } } @@ -2497,9 +2677,9 @@ vty_config_unlock (struct vty *vty) } return vty->config; } - + /* Master of the threads. */ -static struct thread_master *master; +static struct thread_master *vty_master; static void vty_event (enum event event, int sock, struct vty *vty) @@ -2509,22 +2689,23 @@ vty_event (enum event event, int sock, struct vty *vty) switch (event) { case VTY_SERV: - vty_serv_thread = thread_add_read (master, vty_accept, vty, sock); + vty_serv_thread = thread_add_read (vty_master, vty_accept, vty, sock); vector_set_index (Vvty_serv_thread, sock, vty_serv_thread); break; #ifdef VTYSH case VTYSH_SERV: - thread_add_read (master, vtysh_accept, vty, sock); + vty_serv_thread = thread_add_read (vty_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); + vty->t_read = thread_add_read (vty_master, vtysh_read, vty, sock); break; case VTYSH_WRITE: - vty->t_write = thread_add_write (master, vtysh_write, vty, sock); + vty->t_write = thread_add_write (vty_master, vtysh_write, vty, sock); break; #endif /* VTYSH */ case VTY_READ: - vty->t_read = thread_add_read (master, vty_read, vty, sock); + vty->t_read = thread_add_read (vty_master, vty_read, vty, sock); /* Time out treatment. */ if (vty->v_timeout) @@ -2532,12 +2713,12 @@ vty_event (enum event event, int sock, struct vty *vty) if (vty->t_timeout) thread_cancel (vty->t_timeout); vty->t_timeout = - thread_add_timer (master, vty_timeout, vty, vty->v_timeout); + thread_add_timer (vty_master, vty_timeout, vty, vty->v_timeout); } break; case VTY_WRITE: if (! vty->t_write) - vty->t_write = thread_add_write (master, vty_flush, vty, sock); + vty->t_write = thread_add_write (vty_master, vty_flush, vty, sock); break; case VTY_TIMEOUT_RESET: if (vty->t_timeout) @@ -2548,14 +2729,14 @@ vty_event (enum event event, int sock, struct vty *vty) if (vty->v_timeout) { vty->t_timeout = - thread_add_timer (master, vty_timeout, vty, vty->v_timeout); + thread_add_timer (vty_master, vty_timeout, vty, vty->v_timeout); } break; } } - -DEFUN (config_who, - config_who_cmd, + +DEFUN (who, + who_cmd, "who", "Display who is on vty\n") { @@ -2826,6 +3007,17 @@ DEFUN (show_history, return CMD_SUCCESS; } +/* vty login. */ +DEFUN (log_commands, + log_commands_cmd, + "log commands", + "Logging control\n" + "Log all commands (can't be unset without restart)\n") +{ + do_log_commands = 1; + return CMD_SUCCESS; +} + /* Display current configuration. */ static int vty_config_write (struct vty *vty) @@ -2858,6 +3050,9 @@ vty_config_write (struct vty *vty) vty_out (vty, " anonymous restricted%s", VTY_NEWLINE); } + if (do_log_commands) + vty_out (vty, "log commands%s", VTY_NEWLINE); + vty_out (vty, "!%s", VTY_NEWLINE); return CMD_SUCCESS; @@ -2960,7 +3155,9 @@ vty_init (struct thread_master *master_thread) vtyvec = vector_init (VECTOR_MIN_SIZE); - master = master_thread; + vty_master = master_thread; + + atexit (vty_stdio_reset); /* Initilize server thread vector. */ Vvty_serv_thread = vector_init (VECTOR_MIN_SIZE); @@ -2968,19 +3165,18 @@ vty_init (struct thread_master *master_thread) /* Install bgp top node. */ install_node (&vty_node, vty_config_write); - install_element (RESTRICTED_NODE, &config_who_cmd); + install_element (RESTRICTED_NODE, &who_cmd); install_element (RESTRICTED_NODE, &show_history_cmd); - install_element (VIEW_NODE, &config_who_cmd); + install_element (VIEW_NODE, &who_cmd); install_element (VIEW_NODE, &show_history_cmd); - install_element (ENABLE_NODE, &config_who_cmd); install_element (CONFIG_NODE, &line_vty_cmd); install_element (CONFIG_NODE, &service_advanced_vty_cmd); install_element (CONFIG_NODE, &no_service_advanced_vty_cmd); install_element (CONFIG_NODE, &show_history_cmd); + install_element (CONFIG_NODE, &log_commands_cmd); install_element (ENABLE_NODE, &terminal_monitor_cmd); install_element (ENABLE_NODE, &terminal_no_monitor_cmd); install_element (ENABLE_NODE, &no_terminal_monitor_cmd); - install_element (ENABLE_NODE, &show_history_cmd); install_default (VTY_NODE); install_element (VTY_NODE, &exec_timeout_min_cmd); diff --git a/lib/vty.h b/lib/vty.h index 639d74175..1e3b12439 100644 --- a/lib/vty.h +++ b/lib/vty.h @@ -23,8 +23,8 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #include "thread.h" #include "log.h" +#include "sockunion.h" -#define VTY_BUFSIZ 512 #define VTY_MAXHIST 20 /* VTY struct. */ @@ -33,15 +33,15 @@ struct vty /* File descripter of this vty. */ int fd; + /* output FD, to support stdin/stdout combination */ + int wfd; + /* Is this vty connect to file or not */ enum {VTY_TERM, VTY_FILE, VTY_SHELL, VTY_SHELL_SERV} type; /* Node status of this vty */ int node; - /* What address is this vty comming from. */ - char *address; - /* Failure count */ int fail; @@ -118,6 +118,9 @@ struct vty /* Timeout seconds and thread. */ unsigned long v_timeout; struct thread *t_timeout; + + /* What address is this vty comming from. */ + char address[SU_ADDRSTRLEN]; }; /* Integrated configuration file. */ @@ -148,8 +151,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; \ @@ -161,20 +164,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_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_GET_INTEGER(NAME,V,STR) \ - VTY_GET_INTEGER_RANGE(NAME,V,STR,0U,UINT32_MAX) +#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 { \ @@ -198,6 +219,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[]; @@ -207,6 +236,7 @@ extern void vty_init_vtysh (void); extern void vty_terminate (void); extern void vty_reset (void); extern struct vty *vty_new (void); +extern struct vty *vty_stdio (void (*atclose)(void)); extern int vty_out (struct vty *, const char *, ...) PRINTF_ATTRIBUTE(2, 3); extern void vty_read_config (char *, char *); extern void vty_time_print (struct vty *, int); @@ -223,6 +253,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 61643bf81..6453e7bb6 100644 --- a/lib/workqueue.c +++ b/lib/workqueue.c @@ -21,7 +21,7 @@ * 02111-1307, USA. */ -#include +#include #include "thread.h" #include "memory.h" #include "workqueue.h" @@ -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,10 +82,11 @@ 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; - + new->cycles.worst = UINT_MAX; + /* Default values, can be overriden by caller */ new->spec.hold = WORK_QUEUE_DEFAULT_HOLD; @@ -96,13 +101,19 @@ 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); return; } +bool +work_queue_is_scheduled (struct work_queue *wq) +{ + return (wq->thread != NULL); +} + static int work_queue_schedule (struct work_queue *wq, unsigned int delay) { @@ -174,29 +185,33 @@ DEFUN(show_work_queues, struct work_queue *wq; vty_out (vty, - "%c %8s %5s %8s %21s%s", + "%c %8s %5s %8s %21s %6s %5s%s", ' ', "List","(ms) ","Q. Runs","Cycle Counts ", + " ","Worst", VTY_NEWLINE); vty_out (vty, - "%c %8s %5s %8s %7s %6s %6s %s%s", + "%c %8s %5s %8s %7s %6s %6s %6s %5s %s%s", 'P', "Items", "Hold", "Total", - "Best","Gran.","Avg.", + "Best","Worst","Gran.","Avg.", "Lat.", "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", + vty_out (vty,"%c %8u %5u %8lu %7u %6u %6u %6u %5lu %s%s", (CHECK_FLAG (wq->flags, WQ_UNPLUGGED) ? ' ' : 'P'), listcount (wq->items), wq->spec.hold, wq->runs, - wq->cycles.best, wq->cycles.granularity, + wq->cycles.best, + MIN(wq->cycles.best, wq->cycles.worst), + wq->cycles.granularity, (wq->runs) ? (unsigned int) (wq->cycles.total / wq->runs) : 0, + wq->worst_usec, wq->name, VTY_NEWLINE); } @@ -239,6 +254,7 @@ work_queue_run (struct thread *thread) { struct work_queue *wq; struct work_queue_item *item; + unsigned long took; wq_item_status ret; unsigned int cycles = 0; struct listnode *node, *nnode; @@ -258,6 +274,8 @@ work_queue_run (struct thread *thread) * * Best: starts low, can only increase * + * Worst: starts at MAX, can only decrease. + * * Granularity: starts at WORK_QUEUE_MIN_GRANULARITY, can be decreased * if we run to end of time slot, can increase otherwise * by a small factor. @@ -332,7 +350,7 @@ work_queue_run (struct thread *thread) /* test if we should yield */ if ( !(cycles % wq->cycles.granularity) - && thread_should_yield (thread)) + && (took = thread_should_yield (thread))) { yielded = 1; goto stats; @@ -343,24 +361,32 @@ work_queue_run (struct thread *thread) #define WQ_HYSTERESIS_FACTOR 4 + if (cycles > wq->cycles.best) + wq->cycles.best = cycles; + + if (took > wq->worst_usec) + wq->worst_usec = took; + /* we yielded, check whether granularity should be reduced */ if (yielded && (cycles < wq->cycles.granularity)) { wq->cycles.granularity = ((cycles > 0) ? cycles : WORK_QUEUE_MIN_GRANULARITY); + if (cycles < wq->cycles.worst) + wq->cycles.worst = cycles; } /* otherwise, should granularity increase? */ else if (cycles >= (wq->cycles.granularity)) { - if (cycles > wq->cycles.best) - wq->cycles.best = cycles; - - /* along with yielded check, provides hysteresis for granularity */ + /* along with yielded check, provides hysteresis for granularity */ if (cycles > (wq->cycles.granularity * WQ_HYSTERESIS_FACTOR * WQ_HYSTERESIS_FACTOR)) wq->cycles.granularity *= WQ_HYSTERESIS_FACTOR; /* quick ramp-up */ else if (cycles > (wq->cycles.granularity * WQ_HYSTERESIS_FACTOR)) wq->cycles.granularity += WQ_HYSTERESIS_FACTOR; + + /* clamp granularity down to the worst yielded cycle count */ + wq->cycles.granularity = MIN(wq->cycles.granularity, wq->cycles.worst); } #undef WQ_HYSTERIS_FACTOR diff --git a/lib/workqueue.h b/lib/workqueue.h index f59499a0a..aac786058 100644 --- a/lib/workqueue.h +++ b/lib/workqueue.h @@ -89,9 +89,11 @@ struct work_queue /* remaining fields should be opaque to users */ struct list *items; /* queue item list */ unsigned long runs; /* runs count */ + unsigned long worst_usec; struct { unsigned int best; + unsigned int worst; unsigned int granularity; unsigned long total; } cycles; /* cycle counts */ @@ -119,6 +121,8 @@ extern void work_queue_plug (struct work_queue *wq); /* unplug the queue, allow it to be drained again */ extern void work_queue_unplug (struct work_queue *wq); +bool work_queue_is_scheduled (struct work_queue *); + /* Helpers, exported for thread.c and command.c */ extern int work_queue_run (struct thread *); extern struct cmd_element show_work_queues_cmd; diff --git a/lib/zassert.h b/lib/zassert.h index 79126760a..bf0a851ba 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 3521d99ee..a32faaabf 100644 --- a/lib/zclient.c +++ b/lib/zclient.c @@ -32,23 +32,21 @@ #include "zclient.h" #include "memory.h" #include "table.h" - + /* Zebra client events. */ enum event {ZCLIENT_SCHEDULE, ZCLIENT_READ, ZCLIENT_CONNECT}; /* Prototype for event manager. */ static void zclient_event (enum event, struct zclient *); -extern struct thread_master *master; - -char *zclient_serv_path = NULL; +const char *zclient_serv_path = NULL; /* This file local debug flag. */ int zclient_debug = 0; - + /* Allocate zclient structure. */ struct zclient * -zclient_new () +zclient_new (struct thread_master *master) { struct zclient *zclient; zclient = XCALLOC (MTYPE_ZCLIENT, sizeof (struct zclient)); @@ -56,6 +54,7 @@ zclient_new () zclient->ibuf = stream_new (ZEBRA_MAX_PACKET_SIZ); zclient->obuf = stream_new (ZEBRA_MAX_PACKET_SIZ); zclient->wb = buffer_new(0); + zclient->master = master; return zclient; } @@ -93,15 +92,14 @@ zclient_init (struct zclient *zclient, int redist_default) /* Clear redistribution flags. */ for (i = 0; i < ZEBRA_ROUTE_MAX; i++) - zclient->redist[i] = 0; + zclient->redist[i] = vrf_bitmap_init (); /* Set unwanted redistribute route. bgpd does not need BGP route redistribution. */ zclient->redist_default = redist_default; - zclient->redist[redist_default] = 1; /* Set default-information redistribute to zero. */ - zclient->default_information = 0; + zclient->default_information = vrf_bitmap_init (); /* Schedule first zclient connection. */ if (zclient_debug) @@ -114,6 +112,8 @@ zclient_init (struct zclient *zclient, int redist_default) void zclient_stop (struct zclient *zclient) { + int i; + if (zclient_debug) zlog_debug ("zclient stopped"); @@ -136,6 +136,14 @@ zclient_stop (struct zclient *zclient) zclient->sock = -1; } zclient->fail = 0; + + for (i = 0; i < ZEBRA_ROUTE_MAX; i++) + { + vrf_bitmap_free(zclient->redist[i]); + zclient->redist[i] = VRF_BITMAP_NULL; + } + vrf_bitmap_free(zclient->default_information); + zclient->default_information = VRF_BITMAP_NULL; } void @@ -173,6 +181,7 @@ zclient_socket(void) ret = connect (sock, (struct sockaddr *) &serv, sizeof (serv)); if (ret < 0) { + zlog_warn ("%s connect failure: %d", __PRETTY_FUNCTION__, errno); close (sock); return -1; } @@ -208,6 +217,7 @@ zclient_socket_un (const char *path) ret = connect (sock, (struct sockaddr *) &addr, len); if (ret < 0) { + zlog_warn ("%s connect failure: %d", __PRETTY_FUNCTION__, errno); close (sock); return -1; } @@ -229,7 +239,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; } @@ -259,8 +269,8 @@ zclient_flush_data(struct thread *thread) return zclient_failed(zclient); break; case BUFFER_PENDING: - zclient->t_write = thread_add_write(master, zclient_flush_data, - zclient, zclient->sock); + zclient->t_write = thread_add_write (zclient->master, zclient_flush_data, + zclient, zclient->sock); break; case BUFFER_EMPTY: break; @@ -285,26 +295,53 @@ zclient_send_message(struct zclient *zclient) THREAD_OFF(zclient->t_write); break; case BUFFER_PENDING: - THREAD_WRITE_ON(master, zclient->t_write, - zclient_flush_data, zclient, zclient->sock); + THREAD_WRITE_ON (zclient->master, zclient->t_write, + zclient_flush_data, zclient, zclient->sock); break; } return 0; } void -zclient_create_header (struct stream *s, uint16_t command) +zclient_create_header (struct stream *s, uint16_t command, vrf_id_t vrf_id) { /* length placeholder, caller can update */ stream_putw (s, ZEBRA_HEADER_SIZE); stream_putc (s, ZEBRA_HEADER_MARKER); stream_putc (s, ZSERV_VERSION); + stream_putw (s, vrf_id); stream_putw (s, command); } +int +zclient_read_header (struct stream *s, int sock, u_int16_t *size, u_char *marker, + u_char *version, u_int16_t *vrf_id, u_int16_t *cmd) +{ + if (stream_read (s, sock, ZEBRA_HEADER_SIZE) != ZEBRA_HEADER_SIZE) + return -1; + + *size = stream_getw (s) - ZEBRA_HEADER_SIZE; + *marker = stream_getc (s); + *version = stream_getc (s); + *vrf_id = stream_getw (s); + *cmd = stream_getw (s); + + if (*version != ZSERV_VERSION || *marker != ZEBRA_HEADER_MARKER) + { + zlog_err("%s: socket %d version mismatch, marker %d, version %d", + __func__, sock, *marker, *version); + return -1; + } + + if (*size && stream_read (s, sock, *size) != *size) + return -1; + + return 0; +} + /* Send simple Zebra message. */ static int -zebra_message_send (struct zclient *zclient, int command) +zebra_message_send (struct zclient *zclient, int command, vrf_id_t vrf_id) { struct stream *s; @@ -313,7 +350,7 @@ zebra_message_send (struct zclient *zclient, int command) stream_reset (s); /* Send very simple command only Zebra message. */ - zclient_create_header (s, command); + zclient_create_header (s, command, vrf_id); return zclient_send_message(zclient); } @@ -328,7 +365,8 @@ zebra_hello_send (struct zclient *zclient) s = zclient->obuf; stream_reset (s); - zclient_create_header (s, ZEBRA_HELLO); + /* The VRF ID in the HELLO message is always 0. */ + zclient_create_header (s, ZEBRA_HELLO, VRF_DEFAULT); stream_putc (s, zclient->redist_default); stream_putw_at (s, 0, stream_get_endp (s)); return zclient_send_message(zclient); @@ -337,12 +375,47 @@ zebra_hello_send (struct zclient *zclient) return 0; } +/* Send requests to zebra daemon for the information in a VRF. */ +void +zclient_send_requests (struct zclient *zclient, vrf_id_t vrf_id) +{ + int i; + + /* zclient is disabled. */ + if (! zclient->enable) + return; + + /* If not connected to the zebra yet. */ + if (zclient->sock < 0) + return; + + if (zclient_debug) + zlog_debug ("%s: send messages for VRF %u", __func__, vrf_id); + + /* We need router-id information. */ + zebra_message_send (zclient, ZEBRA_ROUTER_ID_ADD, vrf_id); + + /* We need interface information. */ + zebra_message_send (zclient, ZEBRA_INTERFACE_ADD, vrf_id); + + /* Set unwanted redistribute route. */ + vrf_bitmap_set (zclient->redist[zclient->redist_default], vrf_id); + + /* Flush all redistribute request. */ + for (i = 0; i < ZEBRA_ROUTE_MAX; i++) + if (i != zclient->redist_default && + vrf_bitmap_check (zclient->redist[i], vrf_id)) + zebra_redistribute_send (ZEBRA_REDISTRIBUTE_ADD, zclient, i, vrf_id); + + /* If default information is needed. */ + if (vrf_bitmap_check (zclient->default_information, VRF_DEFAULT)) + zebra_message_send (zclient, ZEBRA_REDISTRIBUTE_DEFAULT_ADD, vrf_id); +} + /* Make connection to zebra daemon. */ int zclient_start (struct zclient *zclient) { - int i; - if (zclient_debug) zlog_debug ("zclient_start is called"); @@ -358,11 +431,23 @@ zclient_start (struct zclient *zclient) if (zclient->t_connect) return 0; - if (zclient_socket_connect(zclient) < 0) + /* + * If we fail to connect to the socket on initialization, + * Let's wait a second and see if we can reconnect. + * Cause if we don't connect, we never attempt to + * reconnect. On startup if zebra is slow we + * can get into this situation. + */ + while (zclient_socket_connect(zclient) < 0 && zclient->fail < 5) { if (zclient_debug) zlog_debug ("zclient connection fail"); zclient->fail++; + sleep (1); + } + + if (zclient->sock < 0) + { zclient_event (ZCLIENT_CONNECT, zclient); return -1; } @@ -380,20 +465,9 @@ zclient_start (struct zclient *zclient) zebra_hello_send (zclient); - /* We need router-id information. */ - zebra_message_send (zclient, ZEBRA_ROUTER_ID_ADD); - - /* We need interface information. */ - zebra_message_send (zclient, ZEBRA_INTERFACE_ADD); - - /* Flush all redistribute request. */ - for (i = 0; i < ZEBRA_ROUTE_MAX; i++) - if (i != zclient->redist_default && zclient->redist[i]) - zebra_redistribute_send (ZEBRA_REDISTRIBUTE_ADD, zclient, i); - - /* If default information is needed. */ - if (zclient->default_information) - zebra_message_send (zclient, ZEBRA_REDISTRIBUTE_DEFAULT_ADD); + /* Inform the successful connection. */ + if (zclient->zebra_connected) + (*zclient->zebra_connected) (zclient); return 0; } @@ -413,7 +487,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 @@ -456,6 +530,8 @@ zclient_connect (struct thread *t) * If ZAPI_MESSAGE_METRIC is set, the metric value is written as an 8 * byte value. * + * If ZAPI_MESSAGE_TAG is set, the tag value is written as a 4 byte value + * * XXX: No attention paid to alignment. */ int @@ -469,8 +545,8 @@ zapi_ipv4_route (u_char cmd, struct zclient *zclient, struct prefix_ipv4 *p, /* Reset stream. */ s = zclient->obuf; stream_reset (s); - - zclient_create_header (s, cmd); + + zclient_create_header (s, cmd, api->vrf_id); /* Put type and nexthop. */ stream_putc (s, api->type); @@ -512,6 +588,10 @@ zapi_ipv4_route (u_char cmd, struct zclient *zclient, struct prefix_ipv4 *p, stream_putc (s, api->distance); if (CHECK_FLAG (api->message, ZAPI_MESSAGE_METRIC)) stream_putl (s, api->metric); + if (CHECK_FLAG (api->message, ZAPI_MESSAGE_MTU)) + stream_putl (s, api->mtu); + if (CHECK_FLAG (api->message, ZAPI_MESSAGE_TAG)) + stream_putl (s, api->tag); /* Put length at the first point of the stream. */ stream_putw_at (s, 0, stream_get_endp (s)); @@ -532,7 +612,7 @@ zapi_ipv6_route (u_char cmd, struct zclient *zclient, struct prefix_ipv6 *p, s = zclient->obuf; stream_reset (s); - zclient_create_header (s, cmd); + zclient_create_header (s, cmd, api->vrf_id); /* Put type and nexthop. */ stream_putc (s, api->type); @@ -566,6 +646,10 @@ zapi_ipv6_route (u_char cmd, struct zclient *zclient, struct prefix_ipv6 *p, stream_putc (s, api->distance); if (CHECK_FLAG (api->message, ZAPI_MESSAGE_METRIC)) stream_putl (s, api->metric); + if (CHECK_FLAG (api->message, ZAPI_MESSAGE_MTU)) + stream_putl (s, api->mtu); + if (CHECK_FLAG (api->message, ZAPI_MESSAGE_TAG)) + stream_putl (s, api->tag); /* Put length at the first point of the stream. */ stream_putw_at (s, 0, stream_get_endp (s)); @@ -581,14 +665,15 @@ zapi_ipv6_route (u_char cmd, struct zclient *zclient, struct prefix_ipv6 *p, * sending client */ int -zebra_redistribute_send (int command, struct zclient *zclient, int type) +zebra_redistribute_send (int command, struct zclient *zclient, int type, + vrf_id_t vrf_id) { struct stream *s; s = zclient->obuf; stream_reset(s); - zclient_create_header (s, command); + zclient_create_header (s, command, vrf_id); stream_putc (s, type); stream_putw_at (s, 0, stream_get_endp (s)); @@ -596,18 +681,30 @@ zebra_redistribute_send (int command, struct zclient *zclient, int type) return zclient_send_message(zclient); } +/* Get prefix in ZServ format; family should be filled in on prefix */ +static void +zclient_stream_get_prefix (struct stream *s, struct prefix *p) +{ + size_t plen = prefix_blen (p); + u_char c; + p->prefixlen = 0; + + if (plen == 0) + return; + + stream_get (&p->u.prefix, s, plen); + c = stream_getc(s); + p->prefixlen = MIN(plen * 8, c); +} + /* Router-id update from zebra daemon. */ void zebra_router_id_update_read (struct stream *s, struct prefix *rid) { - int plen; - /* Fetch interface address. */ rid->family = stream_getc (s); - - plen = prefix_blen (rid); - stream_get (&rid->u.prefix, s, plen); - rid->prefixlen = stream_getc (s); + + zclient_stream_get_prefix (s, rid); } /* Interface addition from zebra daemon. */ @@ -616,8 +713,6 @@ zebra_router_id_update_read (struct stream *s, struct prefix *rid) * ZEBRA_INTERFACE_DELETE from zebra to the client is: * 0 1 2 3 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - * +-+-+-+-+-+-+-+-+ - * | type | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | ifname | * | | @@ -625,25 +720,37 @@ zebra_router_id_update_read (struct stream *s, struct prefix *rid) * | | * | | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | ifindex | + * | ifindex | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | status | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | if_flags | + * | if_flags | * | | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | metric | + * | metric | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | ifmtu | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | ifmtu6 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | bandwidth | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | ifmtu | + * | Link Layer Type | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | ifmtu6 | + * | Harware Address Length | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | bandwidth | + * | Hardware Address if HW lenght different from 0 | + * | ... max INTERFACE_HWADDR_MAX | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - * | sockaddr_dl | + * | Link_params? | Whether a link-params follows: 1 or 0. + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Link_params 0 or 1 INTERFACE_LINK_PARAMS_SIZE sized | + * | .... (struct if_link_params). | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ struct interface * -zebra_interface_add_read (struct stream *s) +zebra_interface_add_read (struct stream *s, vrf_id_t vrf_id) { struct interface *ifp; char ifname_tmp[INTERFACE_NAMSIZ]; @@ -652,26 +759,12 @@ zebra_interface_add_read (struct stream *s) stream_get (ifname_tmp, s, INTERFACE_NAMSIZ); /* Lookup/create interface by name. */ - ifp = if_get_by_name_len (ifname_tmp, strnlen(ifname_tmp, INTERFACE_NAMSIZ)); + ifp = if_get_by_name_len_vrf (ifname_tmp, + strnlen (ifname_tmp, INTERFACE_NAMSIZ), + vrf_id); - /* Read interface's index. */ - ifp->ifindex = stream_getl (s); + zebra_interface_if_set_value (s, ifp); - /* Read interface's value. */ - ifp->status = stream_getc (s); - ifp->flags = stream_getq (s); - ifp->metric = stream_getl (s); - ifp->mtu = stream_getl (s); - ifp->mtu6 = stream_getl (s); - ifp->bandwidth = stream_getl (s); -#ifdef HAVE_STRUCT_SOCKADDR_DL - stream_get (&ifp->sdl, s, sizeof (ifp->sdl)); -#else - ifp->hw_addr_len = stream_getl (s); - if (ifp->hw_addr_len) - stream_get (ifp->hw_addr, s, ifp->hw_addr_len); -#endif /* HAVE_STRUCT_SOCKADDR_DL */ - return ifp; } @@ -683,7 +776,7 @@ zebra_interface_add_read (struct stream *s) * is sent at the tail of the message. */ struct interface * -zebra_interface_state_read (struct stream *s) +zebra_interface_state_read (struct stream *s, vrf_id_t vrf_id) { struct interface *ifp; char ifname_tmp[INTERFACE_NAMSIZ]; @@ -692,28 +785,149 @@ zebra_interface_state_read (struct stream *s) stream_get (ifname_tmp, s, INTERFACE_NAMSIZ); /* Lookup this by interface index. */ - ifp = if_lookup_by_name_len (ifname_tmp, - strnlen(ifname_tmp, INTERFACE_NAMSIZ)); + ifp = if_lookup_by_name_len_vrf (ifname_tmp, + strnlen (ifname_tmp, INTERFACE_NAMSIZ), + vrf_id); /* If such interface does not exist, indicate an error */ if (! ifp) return NULL; + zebra_interface_if_set_value (s, ifp); + + return ifp; +} + +static void +link_params_set_value(struct stream *s, struct if_link_params *iflp) +{ + + if (iflp == NULL) + return; + + iflp->lp_status = stream_getl (s); + iflp->te_metric = stream_getl (s); + iflp->max_bw = stream_getf (s); + iflp->max_rsv_bw = stream_getf (s); + uint32_t bwclassnum = stream_getl (s); + { + unsigned int i; + for (i = 0; i < bwclassnum && i < MAX_CLASS_TYPE; i++) + iflp->unrsv_bw[i] = stream_getf (s); + if (i < bwclassnum) + zlog_err ("%s: received %d > %d (MAX_CLASS_TYPE) bw entries" + " - outdated library?", + __func__, bwclassnum, MAX_CLASS_TYPE); + } + iflp->admin_grp = stream_getl (s); + iflp->rmt_as = stream_getl (s); + iflp->rmt_ip.s_addr = stream_get_ipv4 (s); + + iflp->av_delay = stream_getl (s); + iflp->min_delay = stream_getl (s); + iflp->max_delay = stream_getl (s); + iflp->delay_var = stream_getl (s); + + iflp->pkt_loss = stream_getf (s); + iflp->res_bw = stream_getf (s); + iflp->ava_bw = stream_getf (s); + iflp->use_bw = stream_getf (s); +} + +struct interface * +zebra_interface_link_params_read (struct stream *s) +{ + struct if_link_params *iflp; + uint32_t ifindex = stream_getl (s); + + struct interface *ifp = if_lookup_by_index (ifindex); + + if (ifp == NULL || s == NULL) + { + zlog_err ("%s: unknown ifindex %u, shouldn't happen", + __func__, ifindex); + return NULL; + } + + if ((iflp = if_link_params_get (ifp)) == NULL) + return NULL; + + link_params_set_value(s, iflp); + + return ifp; +} + +void +zebra_interface_if_set_value (struct stream *s, struct interface *ifp) +{ + u_char link_params_status = 0; + /* Read interface's index. */ ifp->ifindex = stream_getl (s); + ifp->status = stream_getc (s); /* Read interface's value. */ - ifp->status = stream_getc (s); ifp->flags = stream_getq (s); ifp->metric = stream_getl (s); ifp->mtu = stream_getl (s); ifp->mtu6 = stream_getl (s); ifp->bandwidth = stream_getl (s); + ifp->ll_type = stream_getl (s); + ifp->hw_addr_len = stream_getl (s); + if (ifp->hw_addr_len) + stream_get (ifp->hw_addr, s, MIN(ifp->hw_addr_len, INTERFACE_HWADDR_MAX)); - return ifp; + /* Read Traffic Engineering status */ + link_params_status = stream_getc (s); + /* Then, Traffic Engineering parameters if any */ + if (link_params_status) + { + struct if_link_params *iflp = if_link_params_get (ifp); + link_params_set_value(s, iflp); + } } -/* +size_t +zebra_interface_link_params_write (struct stream *s, struct interface *ifp) +{ + size_t w; + struct if_link_params *iflp; + int i; + + if (s == NULL || ifp == NULL || ifp->link_params == NULL) + return 0; + + iflp = ifp->link_params; + w = 0; + + w += stream_putl (s, iflp->lp_status); + + w += stream_putl (s, iflp->te_metric); + w += stream_putf (s, iflp->max_bw); + w += stream_putf (s, iflp->max_rsv_bw); + + w += stream_putl (s, MAX_CLASS_TYPE); + for (i = 0; i < MAX_CLASS_TYPE; i++) + w += stream_putf (s, iflp->unrsv_bw[i]); + + w += stream_putl (s, iflp->admin_grp); + w += stream_putl (s, iflp->rmt_as); + w += stream_put_in_addr (s, &iflp->rmt_ip); + + w += stream_putl (s, iflp->av_delay); + w += stream_putl (s, iflp->min_delay); + w += stream_putl (s, iflp->max_delay); + w += stream_putl (s, iflp->delay_var); + + w += stream_putf (s, iflp->pkt_loss); + w += stream_putf (s, iflp->res_bw); + w += stream_putf (s, iflp->ava_bw); + w += stream_putf (s, iflp->use_bw); + + return w; +} + +/* * format of message for address additon is: * 0 * 0 1 2 3 4 5 6 7 @@ -742,24 +956,8 @@ zebra_interface_state_read (struct stream *s) * : : * | | * +-+-+-+-+-+-+-+-+ - * */ -void -zebra_interface_if_set_value (struct stream *s, struct interface *ifp) -{ - /* Read interface's index. */ - ifp->ifindex = stream_getl (s); - ifp->status = stream_getc (s); - - /* Read interface's value. */ - ifp->flags = stream_getq (s); - ifp->metric = stream_getl (s); - ifp->mtu = stream_getl (s); - ifp->mtu6 = stream_getl (s); - ifp->bandwidth = stream_getl (s); -} - static int memconstant(const void *s, int c, size_t n) { @@ -772,13 +970,12 @@ memconstant(const void *s, int c, size_t n) } struct connected * -zebra_interface_address_read (int type, struct stream *s) +zebra_interface_address_read (int type, struct stream *s, vrf_id_t vrf_id) { - unsigned int ifindex; + ifindex_t ifindex; struct interface *ifp; struct connected *ifc; - struct prefix p, d; - int family; + struct prefix p, d, *dp; int plen; u_char ifc_flags; @@ -789,7 +986,7 @@ zebra_interface_address_read (int type, struct stream *s) ifindex = stream_getl (s); /* Lookup index. */ - ifp = if_lookup_by_index (ifindex); + ifp = if_lookup_by_index_vrf (ifindex, vrf_id); if (ifp == NULL) { zlog_warn ("zebra_interface_address_read(%s): " @@ -803,26 +1000,36 @@ zebra_interface_address_read (int type, struct stream *s) ifc_flags = stream_getc (s); /* Fetch interface address. */ - family = p.family = stream_getc (s); - - plen = prefix_blen (&p); - stream_get (&p.u.prefix, s, plen); - p.prefixlen = stream_getc (s); + d.family = p.family = stream_getc (s); + plen = prefix_blen (&d); + + zclient_stream_get_prefix (s, &p); /* Fetch destination address. */ stream_get (&d.u.prefix, s, plen); - d.family = family; - + + /* N.B. NULL destination pointers are encoded as all zeroes */ + dp = memconstant(&d.u.prefix,0,plen) ? NULL : &d; + if (type == ZEBRA_INTERFACE_ADDRESS_ADD) { /* N.B. NULL destination pointers are encoded as all zeroes */ - ifc = connected_add_by_prefix(ifp, &p,(memconstant(&d.u.prefix,0,plen) ? - NULL : &d)); + ifc = connected_add_by_prefix(ifp, &p, dp); if (ifc != NULL) { 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[PREFIX_STRLEN]; + zlog_warn("warning: interface %s address %s " + "with peer flag set, but no peer address!", + ifp->name, + prefix2str (ifc->address, buf, sizeof buf)); + UNSET_FLAG(ifc->flags, ZEBRA_IFA_PEER); + } } } else @@ -834,7 +1041,7 @@ zebra_interface_address_read (int type, struct stream *s) return ifc; } - + /* Zebra client message read function. */ static int zclient_read (struct thread *thread) @@ -842,6 +1049,7 @@ zclient_read (struct thread *thread) size_t already; uint16_t length, command; uint8_t marker, version; + vrf_id_t vrf_id; struct zclient *zclient; /* Get socket to zebra. */ @@ -876,6 +1084,7 @@ zclient_read (struct thread *thread) length = stream_getw (zclient->ibuf); marker = stream_getc (zclient->ibuf); version = stream_getc (zclient->ibuf); + vrf_id = stream_getw (zclient->ibuf); command = stream_getw (zclient->ibuf); if (marker != ZEBRA_HEADER_MARKER || version != ZSERV_VERSION) @@ -927,53 +1136,60 @@ zclient_read (struct thread *thread) length -= ZEBRA_HEADER_SIZE; if (zclient_debug) - zlog_debug("zclient 0x%p command 0x%x \n", zclient, command); + zlog_debug("zclient 0x%p command 0x%x VRF %u\n", (void *)zclient, command, vrf_id); switch (command) { case ZEBRA_ROUTER_ID_UPDATE: if (zclient->router_id_update) - (*zclient->router_id_update) (command, zclient, length); + (*zclient->router_id_update) (command, zclient, length, vrf_id); break; case ZEBRA_INTERFACE_ADD: if (zclient->interface_add) - (*zclient->interface_add) (command, zclient, length); + (*zclient->interface_add) (command, zclient, length, vrf_id); break; case ZEBRA_INTERFACE_DELETE: if (zclient->interface_delete) - (*zclient->interface_delete) (command, zclient, length); + (*zclient->interface_delete) (command, zclient, length, vrf_id); break; case ZEBRA_INTERFACE_ADDRESS_ADD: if (zclient->interface_address_add) - (*zclient->interface_address_add) (command, zclient, length); + (*zclient->interface_address_add) (command, zclient, length, vrf_id); break; case ZEBRA_INTERFACE_ADDRESS_DELETE: if (zclient->interface_address_delete) - (*zclient->interface_address_delete) (command, zclient, length); + (*zclient->interface_address_delete) (command, zclient, length, vrf_id); break; case ZEBRA_INTERFACE_UP: if (zclient->interface_up) - (*zclient->interface_up) (command, zclient, length); + (*zclient->interface_up) (command, zclient, length, vrf_id); break; case ZEBRA_INTERFACE_DOWN: if (zclient->interface_down) - (*zclient->interface_down) (command, zclient, length); + (*zclient->interface_down) (command, zclient, length, vrf_id); break; case ZEBRA_IPV4_ROUTE_ADD: if (zclient->ipv4_route_add) - (*zclient->ipv4_route_add) (command, zclient, length); + (*zclient->ipv4_route_add) (command, zclient, length, vrf_id); break; case ZEBRA_IPV4_ROUTE_DELETE: if (zclient->ipv4_route_delete) - (*zclient->ipv4_route_delete) (command, zclient, length); + (*zclient->ipv4_route_delete) (command, zclient, length, vrf_id); break; case ZEBRA_IPV6_ROUTE_ADD: if (zclient->ipv6_route_add) - (*zclient->ipv6_route_add) (command, zclient, length); + (*zclient->ipv6_route_add) (command, zclient, length, vrf_id); break; case ZEBRA_IPV6_ROUTE_DELETE: if (zclient->ipv6_route_delete) - (*zclient->ipv6_route_delete) (command, zclient, length); + (*zclient->ipv6_route_delete) (command, zclient, length, vrf_id); + break; + case ZEBRA_INTERFACE_LINK_PARAMS: + if (zclient->interface_link_params) + (*zclient->interface_link_params) (command, zclient, length); + case ZEBRA_NEXTHOP_UPDATE: + if (zclient->nexthop_update) + (*zclient->nexthop_update) (command, zclient, length, vrf_id); break; default: break; @@ -991,46 +1207,48 @@ zclient_read (struct thread *thread) } void -zclient_redistribute (int command, struct zclient *zclient, int type) +zclient_redistribute (int command, struct zclient *zclient, int type, + vrf_id_t vrf_id) { if (command == ZEBRA_REDISTRIBUTE_ADD) { - if (zclient->redist[type]) + if (vrf_bitmap_check (zclient->redist[type], vrf_id)) return; - zclient->redist[type] = 1; + vrf_bitmap_set (zclient->redist[type], vrf_id); } else { - if (!zclient->redist[type]) + if (!vrf_bitmap_check (zclient->redist[type], vrf_id)) return; - zclient->redist[type] = 0; + vrf_bitmap_unset (zclient->redist[type], vrf_id); } if (zclient->sock > 0) - zebra_redistribute_send (command, zclient, type); + zebra_redistribute_send (command, zclient, type, vrf_id); } void -zclient_redistribute_default (int command, struct zclient *zclient) +zclient_redistribute_default (int command, struct zclient *zclient, + vrf_id_t vrf_id) { if (command == ZEBRA_REDISTRIBUTE_DEFAULT_ADD) { - if (zclient->default_information) + if (vrf_bitmap_check (zclient->default_information, vrf_id)) return; - zclient->default_information = 1; + vrf_bitmap_set (zclient->default_information, vrf_id); } else { - if (!zclient->default_information) + if (!vrf_bitmap_check (zclient->default_information, vrf_id)) return; - zclient->default_information = 0; + vrf_bitmap_unset (zclient->default_information, vrf_id); } if (zclient->sock > 0) - zebra_message_send (zclient, command); + zebra_message_send (zclient, command, vrf_id); } static void @@ -1041,7 +1259,7 @@ zclient_event (enum event event, struct zclient *zclient) case ZCLIENT_SCHEDULE: if (! zclient->t_connect) zclient->t_connect = - thread_add_event (master, zclient_connect, zclient, 0); + thread_add_event (zclient->master, zclient_connect, zclient, 0); break; case ZCLIENT_CONNECT: if (zclient->fail >= 10) @@ -1051,16 +1269,21 @@ zclient_event (enum event event, struct zclient *zclient) zclient->fail < 3 ? 10 : 60); if (! zclient->t_connect) zclient->t_connect = - thread_add_timer (master, zclient_connect, zclient, + thread_add_timer (zclient->master, zclient_connect, zclient, zclient->fail < 3 ? 10 : 60); break; case ZCLIENT_READ: zclient->t_read = - thread_add_read (master, zclient_read, zclient, zclient->sock); + thread_add_read (zclient->master, zclient_read, zclient, zclient->sock); break; } } +const char *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 a7d7b5487..d46728dac 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -28,15 +28,21 @@ /* For struct interface and struct connected. */ #include "if.h" +/* For vrf_bitmap_t. */ +#include "vrf.h" + /* For input/output buffer to zebra. */ #define ZEBRA_MAX_PACKET_SIZ 4096 /* Zebra header size. */ -#define ZEBRA_HEADER_SIZE 6 +#define ZEBRA_HEADER_SIZE 8 /* Structure for the zebra client. */ struct zclient { + /* The thread master we schedule ourselves on */ + struct thread_master *master; + /* Socket to zebra daemon. */ int sock; @@ -65,23 +71,26 @@ struct zclient /* Redistribute information. */ u_char redist_default; - u_char redist[ZEBRA_ROUTE_MAX]; + vrf_bitmap_t redist[ZEBRA_ROUTE_MAX]; /* Redistribute defauilt. */ - u_char default_information; + vrf_bitmap_t default_information; /* Pointer to the callback functions. */ - int (*router_id_update) (int, struct zclient *, uint16_t); - int (*interface_add) (int, struct zclient *, uint16_t); - int (*interface_delete) (int, struct zclient *, uint16_t); - int (*interface_up) (int, struct zclient *, uint16_t); - int (*interface_down) (int, struct zclient *, uint16_t); - int (*interface_address_add) (int, struct zclient *, uint16_t); - int (*interface_address_delete) (int, struct zclient *, uint16_t); - int (*ipv4_route_add) (int, struct zclient *, uint16_t); - int (*ipv4_route_delete) (int, struct zclient *, uint16_t); - int (*ipv6_route_add) (int, struct zclient *, uint16_t); - int (*ipv6_route_delete) (int, struct zclient *, uint16_t); + void (*zebra_connected) (struct zclient *); + int (*router_id_update) (int, struct zclient *, uint16_t, vrf_id_t); + int (*interface_add) (int, struct zclient *, uint16_t, vrf_id_t); + int (*interface_delete) (int, struct zclient *, uint16_t, vrf_id_t); + int (*interface_up) (int, struct zclient *, uint16_t, vrf_id_t); + int (*interface_down) (int, struct zclient *, uint16_t, vrf_id_t); + int (*interface_address_add) (int, struct zclient *, uint16_t, vrf_id_t); + int (*interface_address_delete) (int, struct zclient *, uint16_t, vrf_id_t); + int (*interface_link_params) (int, struct zclient *, uint16_t); + int (*ipv4_route_add) (int, struct zclient *, uint16_t, vrf_id_t); + int (*ipv4_route_delete) (int, struct zclient *, uint16_t, vrf_id_t); + int (*ipv6_route_add) (int, struct zclient *, uint16_t, vrf_id_t); + int (*ipv6_route_delete) (int, struct zclient *, uint16_t, vrf_id_t); + int (*nexthop_update) (int, struct zclient *, uint16_t, vrf_id_t); }; /* Zebra API message flag. */ @@ -89,6 +98,8 @@ struct zclient #define ZAPI_MESSAGE_IFINDEX 0x02 #define ZAPI_MESSAGE_DISTANCE 0x04 #define ZAPI_MESSAGE_METRIC 0x08 +#define ZAPI_MESSAGE_MTU 0x10 +#define ZAPI_MESSAGE_TAG 0x20 /* Zserv protocol message header */ struct zserv_header @@ -98,7 +109,8 @@ struct zserv_header * always set to 255 in new zserv. */ uint8_t version; -#define ZSERV_VERSION 1 +#define ZSERV_VERSION 3 + vrf_id_t vrf_id; uint16_t command; }; @@ -117,15 +129,21 @@ struct zapi_ipv4 struct in_addr **nexthop; u_char ifindex_num; - unsigned int *ifindex; + ifindex_t *ifindex; u_char distance; + route_tag_t tag; + u_int32_t metric; + + u_int32_t mtu; + + vrf_id_t vrf_id; }; /* Prototypes of zebra client service functions. */ -extern struct zclient *zclient_new (void); +extern struct zclient *zclient_new (struct thread_master *); extern void zclient_init (struct zclient *, int); extern int zclient_start (struct zclient *); extern void zclient_stop (struct zclient *); @@ -134,31 +152,46 @@ extern void zclient_free (struct zclient *); extern int zclient_socket_connect (struct zclient *); extern void zclient_serv_path_set (char *path); +extern const char *zclient_serv_path_get (void); + +extern void zclient_send_requests (struct zclient *, vrf_id_t); /* Send redistribute command to zebra daemon. Do not update zclient state. */ -extern int zebra_redistribute_send (int command, struct zclient *, int type); +extern int zebra_redistribute_send (int command, struct zclient *, int type, + vrf_id_t vrf_id); /* If state has changed, update state and call zebra_redistribute_send. */ -extern void zclient_redistribute (int command, struct zclient *, int type); +extern void zclient_redistribute (int command, struct zclient *, int type, + vrf_id_t vrf_id); /* If state has changed, update state and send the command to zebra. */ -extern void zclient_redistribute_default (int command, struct zclient *); +extern void zclient_redistribute_default (int command, struct zclient *, + vrf_id_t vrf_id); /* Send the message in zclient->obuf to the zebra daemon (or enqueue it). Returns 0 for success or -1 on an I/O error. */ extern int zclient_send_message(struct zclient *); /* create header for command, length to be filled in by user later */ -extern void zclient_create_header (struct stream *, uint16_t); - -extern struct interface *zebra_interface_add_read (struct stream *); -extern struct interface *zebra_interface_state_read (struct stream *s); -extern struct connected *zebra_interface_address_read (int, struct stream *); +extern void zclient_create_header (struct stream *, uint16_t, vrf_id_t); +extern int zclient_read_header (struct stream *s, int sock, u_int16_t *size, + u_char *marker, u_char *version, + u_int16_t *vrf_id, u_int16_t *cmd); + +extern struct interface *zebra_interface_add_read (struct stream *, + vrf_id_t); +extern struct interface *zebra_interface_state_read (struct stream *, + vrf_id_t); +extern struct connected *zebra_interface_address_read (int, struct stream *, + vrf_id_t); extern void zebra_interface_if_set_value (struct stream *, struct interface *); extern void zebra_router_id_update_read (struct stream *s, struct prefix *rid); extern int zapi_ipv4_route (u_char, struct zclient *, struct prefix_ipv4 *, struct zapi_ipv4 *); +extern struct interface *zebra_interface_link_params_read (struct stream *); +extern size_t zebra_interface_link_params_write (struct stream *, + struct interface *); #ifdef HAVE_IPV6 /* IPv6 prefix add and delete function prototype. */ @@ -176,11 +209,17 @@ struct zapi_ipv6 struct in6_addr **nexthop; u_char ifindex_num; - unsigned int *ifindex; + ifindex_t *ifindex; u_char distance; + route_tag_t tag; + u_int32_t metric; + + u_int32_t mtu; + + vrf_id_t vrf_id; }; extern int zapi_ipv6_route (u_char cmd, struct zclient *zclient, diff --git a/lib/zebra.h b/lib/zebra.h index ab072d6e5..a405d46eb 100644 --- a/lib/zebra.h +++ b/lib/zebra.h @@ -27,7 +27,6 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA #ifdef SUNOS_5 #define _XPG4_2 -#define __EXTENSIONS__ typedef unsigned int u_int32_t; typedef unsigned short u_int16_t; typedef unsigned char u_int8_t; @@ -40,6 +39,7 @@ typedef int socklen_t; #include #include #include +#include #include #include #include @@ -50,7 +50,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 */ @@ -92,6 +91,13 @@ typedef int socklen_t; #ifdef HAVE_INTTYPES_H #include #endif /* HAVE_INTTYPES_H */ +#ifdef HAVE_STDBOOL_H +#include +#endif +/* primarily for __STDC_IEC_559__ with clang */ +#ifdef HAVE_FEATURES_H +#include +#endif /* machine dependent includes */ #ifdef SUNOS_5 @@ -142,6 +148,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 +181,6 @@ typedef int socklen_t; #include #include #include -#include #else #define RT_TABLE_MAIN 0 #endif /* HAVE_NETLINK */ @@ -241,20 +250,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,8 +350,10 @@ 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)) #define HAVE_IP_HDRINCL_BSD_ORDER #endif @@ -380,12 +377,20 @@ struct in_pktinfo /* MAX / MIN are not commonly defined, but useful */ #ifndef MAX -#define MAX(a, b) ((a) > (b) ? (a) : (b)) -#endif +#define MAX(a, b) \ + ({ typeof (a) _a = (a); \ + typeof (b) _b = (b); \ + _a > _b ? _a : _b; }) +#endif #ifndef MIN -#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#define MIN(a, b) \ + ({ typeof (a) _a = (a); \ + typeof (b) _b = (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 @@ -418,7 +423,13 @@ 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_VRF_UNREGISTER 25 +#define ZEBRA_INTERFACE_LINK_PARAMS 26 +#define ZEBRA_NEXTHOP_REGISTER 27 +#define ZEBRA_NEXTHOP_UNREGISTER 28 +#define ZEBRA_NEXTHOP_UPDATE 29 +#define ZEBRA_MESSAGE_MAX 30 /* Marker value used in new Zserv, in the byte location corresponding * the command value in the old zserv header. To allow old and new @@ -448,11 +459,6 @@ extern int proto_redistnum(int afi, const char *s); extern const char *zserv_command_string (unsigned int command); -/* Zebra's family types. */ -#define ZEBRA_FAMILY_IPV4 1 -#define ZEBRA_FAMILY_IPV6 2 -#define ZEBRA_FAMILY_MAX 3 - /* Error codes of zebra. */ #define ZEBRA_ERR_NOERROR 0 #define ZEBRA_ERR_RTEXIST -1 @@ -467,7 +473,7 @@ extern const char *zserv_command_string (unsigned int command); #define ZEBRA_FLAG_BLACKHOLE 0x04 #define ZEBRA_FLAG_IBGP 0x08 #define ZEBRA_FLAG_SELECTED 0x10 -#define ZEBRA_FLAG_CHANGED 0x20 +#define ZEBRA_FLAG_FIB_OVERRIDE 0x20 #define ZEBRA_FLAG_STATIC 0x40 #define ZEBRA_FLAG_REJECT 0x80 @@ -487,21 +493,20 @@ extern const char *zserv_command_string (unsigned int command); #endif /* Address family numbers from RFC1700. */ -#define AFI_IP 1 -#define AFI_IP6 2 -#define AFI_MAX 3 +typedef enum { + AFI_IP = 1, + AFI_IP6 = 2, + AFI_ETHER = 3, /* RFC 1700 has "6" for 802.* */ +#define AFI_MAX 4 +} afi_t; /* Subsequent Address Family Identifier. */ #define SAFI_UNICAST 1 #define SAFI_MULTICAST 2 #define SAFI_RESERVED_3 3 #define SAFI_MPLS_VPN 4 -#define SAFI_MAX 5 - -/* Filter direction. */ -#define FILTER_IN 0 -#define FILTER_OUT 1 -#define FILTER_MAX 2 +#define SAFI_ENCAP 7 /* per IANA */ +#define SAFI_MAX 8 /* Default Administrative Distance of each protocol. */ #define ZEBRA_KERNEL_DISTANCE_DEFAULT 0 @@ -519,52 +524,18 @@ extern const char *zserv_command_string (unsigned int command); #define CHECK_FLAG(V,F) ((V) & (F)) #define SET_FLAG(V,F) (V) |= (F) #define UNSET_FLAG(V,F) (V) &= ~(F) +#define RESET_FLAG(V) (V) = 0 -/* AFI and SAFI type. */ -typedef u_int16_t afi_t; typedef u_int8_t safi_t; /* Zebra types. Used in Zserv message header. */ typedef u_int16_t zebra_size_t; typedef u_int16_t zebra_command_t; -/* FIFO -- first in first out structure and macros. */ -struct fifo -{ - struct fifo *next; - struct fifo *prev; -}; +/* VRF ID type. */ +typedef u_int16_t vrf_id_t; -#define FIFO_INIT(F) \ - do { \ - struct fifo *Xfifo = (struct fifo *)(F); \ - Xfifo->next = Xfifo->prev = Xfifo; \ - } while (0) - -#define FIFO_ADD(F,N) \ - do { \ - struct fifo *Xfifo = (struct fifo *)(F); \ - struct fifo *Xnode = (struct fifo *)(N); \ - Xnode->next = Xfifo; \ - Xnode->prev = Xfifo->prev; \ - Xfifo->prev = Xfifo->prev->next = Xnode; \ - } while (0) - -#define FIFO_DEL(N) \ - do { \ - struct fifo *Xnode = (struct fifo *)(N); \ - Xnode->prev->next = Xnode->next; \ - Xnode->next->prev = Xnode->prev; \ - } while (0) - -#define FIFO_HEAD(F) \ - ((((struct fifo *)(F))->next == (struct fifo *)(F)) \ - ? NULL : (F)->next) - -#define FIFO_EMPTY(F) \ - (((struct fifo *)(F))->next == (struct fifo *)(F)) - -#define FIFO_TOP(F) \ - (FIFO_EMPTY(F) ? NULL : ((struct fifo *)(F))->next) +typedef uint32_t route_tag_t; +#define ROUTE_TAG_MAX UINT32_MAX #endif /* _ZEBRA_H */ diff --git a/m4/ax_sys_weak_alias.m4 b/m4/ax_sys_weak_alias.m4 new file mode 100644 index 000000000..27b0f0c4f --- /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/nhrpd/Makefile.am b/nhrpd/Makefile.am new file mode 100644 index 000000000..a9f70e2a3 --- /dev/null +++ b/nhrpd/Makefile.am @@ -0,0 +1,38 @@ +## Process this file with automake to produce Makefile.in. + +AM_CPPFLAGS = -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib -DQUAGGA_NO_DEPRECATED_INTERFACES +DEFS = @DEFS@ @CARES_CFLAGS@ -DSYSCONFDIR=\"$(sysconfdir)/\" +INSTALL_SDATA=@INSTALL@ -m 600 + +AM_CFLAGS = $(PICFLAGS) #$(WERROR) +AM_LDFLAGS = $(PICLDFLAGS) + +sbin_PROGRAMS = nhrpd + +nhrpd_SOURCES = \ + zbuf.c \ + znl.c \ + resolver.c \ + linux.c \ + netlink_arp.c \ + netlink_gre.c \ + vici.c \ + reqid.c \ + nhrp_event.c \ + nhrp_packet.c \ + nhrp_interface.c \ + nhrp_vc.c \ + nhrp_peer.c \ + nhrp_cache.c \ + nhrp_nhs.c \ + nhrp_route.c \ + nhrp_shortcut.c \ + nhrp_vty.c \ + nhrp_main.c + +nhrpd_LDADD = ../lib/libzebra.la @LIBCAP@ @CARES_LIBS@ + +noinst_HEADERS = debug.h netlink.h nhrpd.h vici.h znl.h list.h \ + nhrp_protocol.h os.h zbuf.h + +#dist_examples_DATA = nhrpd.conf.sample diff --git a/nhrpd/README.kernel b/nhrpd/README.kernel new file mode 100644 index 000000000..3fe1f65f9 --- /dev/null +++ b/nhrpd/README.kernel @@ -0,0 +1,147 @@ +LINUX KERNEL REQUIREMENTS +========================= + +The linux kernel has had various major regressions, performance +issues and subtle bugs (especially in pmtu). Here is a short list +of some -stable kernels that have been tested (at least briefly) +and seem to be working well with Quagga/NHRP: + 3.12.8 or later + 3.14.54 or later + 3.18.22 or later[1] + 4.4.52 or later + 4.9.30 or later + +[1] But you need to apply the following two backported commits: + 3cdaa5be9e ipv4: Don't increase PMTU with Datagram Too Big message + cb6ccf09d6 route: Use ipv4_mtu instead of raw rt_pmtu + +See below for list of known issues in various kernel versions. + +Kernels earlier than 3.12 need CONFIG_ARPD enabled in the configuration. +Many distributions do not enable it by default, and you may need to +compile your own kernel. + +KERNEL BUGS +=========== + +DMVPN and mGRE support in the kernel has been brittle. There are various +regressions in multiple kernel versions. + +This list tries to collect them to one source of information: + +- forward pmtu is disabled intentionally (but tunnel devices rely on it) + Broken since 3.14-rc1: + commit "ipv4: introduce ip_dst_mtu_maybe_forward and protect forwarding path against pmtu spoofing" + Workaround: + Set sysctl net.ipv4.ip_forward_use_pmtu=1 + (Should fix kernel to have this by default on for tunnel devices) + +- subtle path mtu mishandling issues + Broken since (uncertain) + Fixed in 4.1-rc2: + commit "ipv4: Don't increase PMTU with Datagram Too Big message." + commit "route: Use ipv4_mtu instead of raw rt_pmtu" + +- fragmentation of large packets inside tunnel not working + Broken since 3.11-rc1 + commit "ip_tunnels: Use skb-len to PMTU check." + Fixed in 3.14.54, 3.18.22, 4.1.9, 4.2-rc3 + commit "ip_tunnel: fix ipv4 pmtu check to honor inner ip header df" + +- ipsec will crash during xfrm gc + Broke since 3.15-rc1 + commit "flowcache: Make flow cache name space aware" + Fixed in 3.18.10, 4.0 + commit "flowcache: Fix kernel panic in flow_cache_flush_task" + +- TSO on GRE tunnels failed, and resulted in very slow performance + Broke since 3.14.24, 3.18-rc3 + commit "gre: Use inner mac length when computing tunnel length" + Fixed in 3.14.30, 3.18.4 + commit "gre: fix the inner mac header in nbma tunnel xmit path" + commit "gre: Set inner mac header in gro complete" + +- NAPI GRO handling was broken; causing immediate crash (32-bit only?) + Broken since 3.13-rc1 + commit "net: gro: allow to build full sized skb" + Fixed 3.14.5, 3.15-rc7 + commit "net: gro: make sure skb->cb[] initial content has not to be zero" + +- ip_gre dst caching broke NBMA GRE tunnels + Broken since 3.14-rc1 + Fixed in 3.14.5, 3.15-rc6 + commit "ipv4: ip_tunnels: disable cache for nbma gre tunnels" + +- Few packets can be lost when neighbor entry is in NUD_PROBE state, + and there is continuous traffic to it. + Broken since dawn of time + Fixed in 3.15-rc1 + commit "neigh: probe application via netlink in NUD_PROBE" + +- GRO was implemented for GRE, but the hw capabilities were not updated + correctly. In practice forwarding from non-GRE (physical) interface + to GRE interface with gro/gso/tx offloads enabled (also on the target + interface) does not work properly. + Broken around 3.9 to 3.11, need to check details. + +- recvfrom() returned incorrect NBMA address, breaking NAT detection + Broken since 3.10-rc1 + commit "GRE: Refactor GRE tunneling code." + Fixed in 3.10.27, 3.12.8, 3.13-rc7 + commit "ip_gre: fix msg_name parsing for recvfrom/recvmsg" + +- sendto() was broken causing opennhrp not work at all + Broken since 3.10-rc1 + commit "GRE: Refactor GRE tunneling code." + Fixed in 3.10.12, 3.11-rc6 + commit "ip_gre: fix ipgre_header to return correct offset" + +- PMTU was broken due to GRE driver rewrite + Broken since 3.10-rc1 + commit "GRE: Refactor GRE tunneling code." + Fixed in 3.11-rc1 + commit "ip_tunnels: Use skb-len to PMTU check." + +- PMTU was broken due to routing cache removal + Broken since 3.6-rc1 + commit "ipv4: Cache input routes in fib_info nexthops" + Fixed in 3.11-rc1 + commit "ipv4: use next hop exceptions also for input routes" + + 3 other commits + Patches exist for 3.10, but they were not approved to 3.10-stable. + +- Race condition during bootup: changing ARP flag did not flush + existing neighbor entries, causing problems if traffic was routed + to gre interface before opennhrp was running. + Broken since dawn of time + Fixed in 3.11-rc1 + commit "arp: flush arp cache on IFF_NOARP change" + +- Crash in IPsec + Broken since 3.9-rc1 + commit "xfrm: removes a superfluous check and add a statistic" + Fixed in 3.10-rc3 + commit "xfrm: properly handle invalid states as an error" + +- An incorrect ip_gre change broke NHRP traffic over GRE + Broken since 3.8-rc2 + commit "ip_gre: make ipgre_tunnel_xmit() not parse network header as IP unconditionally" + Fixed in 3.8.5, 3.9-rc4 + commit "Revert "ip_gre: make ipgre_tunnel_xmit() not parse network header as IP unconditionally"" + +- Multicast traffic over mGRE was broken. + Broken since 2.6.34-rc2 + commit "gre: fix hard header destination address checking" + Fixed in 2.6.39-rc2 + commit "net: gre: provide multicast mappings for ipv4 and ipv6" + +- Serious performance issues causing small throughput on medium to large DMVPN networks + Broken since dawn of time + Fixed in 2.6.35 + multiple commits rewriting ipsec caching + +- Even though around 2.6.24 is the first version where opennhrp started + to work, there has been various PMTU, performance, and functionality + bugs before 2.6.34. That's one of the first version I consider stable + wrt. to opennhrp functionality. + diff --git a/nhrpd/README.nhrpd b/nhrpd/README.nhrpd new file mode 100644 index 000000000..af358fa92 --- /dev/null +++ b/nhrpd/README.nhrpd @@ -0,0 +1,140 @@ +Quagga / NHRP Design and Configuration Notes +============================================ + +Quagga/NHRP is an NHRP (RFC2332) implementation for Linux. The primary +use case is to implement DMVPN. The aim is thus to be compatible with +Cisco DMVPN (and potentially with FlexVPN in the future). + + +Current Status +-------------- + +Implemented: +- IPsec integration with strongSwan (requires patched strongSwan) +- IPv4 over IPv4 NBMA GRE +- IPv6 over IPv4 NBMA GRE -- majority of code exist; but is not tested +- Spoke (NHC) functionality +- Hub (NHS) functionality + +Not yet implemented: +- NHRP Authentication +- NHRP Groups +- Multicast support (OSPF will not work) +- Full Cisco FlexVPN compatibility (IKEv2 routing) + + +Routing Design +-------------- + +In contrast to opennhrp routing design, Quagga/NHRP routes each NHRP +domain address individually (similar to Cisco FlexVPN). + +To create NBMA GRE tunnel you might use following: + ip tunnel add gre1 mode gre key 42 ttl 64 dev eth0 + ip addr add 10.255.255.2/32 dev gre1 + ip link set gre1 up + sysctl net.ipv4.ip_forward_use_pmtu=1 #for kernels>=3.14 + +This has two important differences compared to opennhrp setup: + 1. The 'tunnel add' now specifies physical device binding. Quagga/NHRP + wants to know stable protocol address to NBMA address mapping. Thus, + add 'dev ' binding, or specify 'local '. If + neither of this is specified, NHRP will not be enabled on the interface. + Alternatively you can skip 'dev' binding on tunnel if you allow + nhrpd to manage it using 'tunnel source' command (see below). + + 2. The 'addr add' now has host prefix. In opennhrp you would have used + the GRE subnet prefix length here instead, e.g. /24. + +Quagga/NHRP will automatically create additional host routes pointing to +gre1 when a connection with these hosts is established. The gre1 subnet +should be announced by routing protocol. This allows routing protocol +to decide which is the closest hub and get the gre addresses' traffic. + +The second benefit is that hubs can then easily exchange host prefixes +of directly connected gre addresses. And thus routing of gre addresses +inside hubs is based on routing protocol's shortest path choice -- not +on random choice from next hop server list. + + +Configuring nhrpd +----------------- + +The configuration is done using vtysh, and most commands do what they +do in Cisco. As minimal configuration example one can do: + configure terminal + interface gre1 + tunnel protection vici profile dmvpn + tunnel source eth0 + ip nhrp network-id 1 + ip nhrp shortcut + ip nhrp registration no-unique + ip nhrp nhs dynamic nbma hubs.example.com + +There's important notes about the "ip nhrp nhs" command: + + 1. The 'dynamic' works only against Cisco (or nhrpd), but is not + compatible with opennhrp. To use dynamic detection of opennhrp hub's + protocol address use the GRE broadcast address there. For the above + example of 10.255.255.0/24 the configuration should read instead: + ip nhrp nhs 10.255.255.255 nbma hubs.example.com + + 2. nbma works like opennhrp dynamic-map. That is, all of the + A-records are configured as NBMA addresses of different hubs, and + each hub protocol address will be dynamically detected. + + +Hub functionality +----------------- + +Sending Traffic Indication (redirect) notifications is now accomplished +using NFLOG. + +Use: +iptables -A FORWARD -i gre1 -o gre1 \ + -m hashlimit --hashlimit-upto 4/minute --hashlimit-burst 1 \ + --hashlimit-mode srcip,dstip --hashlimit-srcmask 16 --hashlimit-dstmask 16 \ + --hashlimit-name loglimit-0 -j NFLOG --nflog-group 1 --nflog-range 128 + +or similar to get rate-limited samples of the packets that match traffic +flow needing redirection. This kernel NFLOG target's nflog-group is configured +in global nhrp config with: + nhrp nflog-group 1 + +To start sending these traffic notices out from hubs, use the nhrp per-interface +directive: + ip nhrp redirect + +opennhrp used PF_PACKET and tried to create packet filter to get only +the packets of interest. Though, this was bad if shortcut fails to +establish (remote policy, or both are behind NAT or restrictive +firewalls), all of the relayaed traffic would match always. + + +Getting information via vtysh +----------------------------- + +Some commands of interest: + - show dmvpn + - show ip nhrp nhs + - show ip nhrp cache + - show ip nhrp shortcut + - show ip route nhrp + - clear ip nhrp cache + - clear ip nhrp shortcut + + +Integration with strongSwan +--------------------------- + +Contrary to opennhrp, Quagga/NHRP has tight integration with IKE daemon. +Currently strongSwan is supported using the VICI protocol. strongSwan +is connected using UNIX socket (hardcoded now as /var/run/charon.vici). +Thus nhrpd needs to be run as user that can open that file. + +Currently, you will need patched strongSwan. The working tree is at: + http://git.alpinelinux.org/cgit/user/tteras/strongswan/log/?h=tteras + +And the branch with patches against latest release are: + http://git.alpinelinux.org/cgit/user/tteras/strongswan/log/?h=tteras-release + diff --git a/nhrpd/debug.h b/nhrpd/debug.h new file mode 100644 index 000000000..b1f49aa8b --- /dev/null +++ b/nhrpd/debug.h @@ -0,0 +1,42 @@ +#include "log.h" + +#if defined(__GNUC__) && (__GNUC__ >= 3) +#define likely(_x) __builtin_expect(!!(_x), 1) +#define unlikely(_x) __builtin_expect(!!(_x), 0) +#else +#define likely(_x) !!(_x) +#define unlikely(_x) !!(_x) +#endif + +#define NHRP_DEBUG_COMMON (1 << 0) +#define NHRP_DEBUG_KERNEL (1 << 1) +#define NHRP_DEBUG_IF (1 << 2) +#define NHRP_DEBUG_ROUTE (1 << 3) +#define NHRP_DEBUG_VICI (1 << 4) +#define NHRP_DEBUG_EVENT (1 << 5) +#define NHRP_DEBUG_ALL (0xFFFF) + +extern unsigned int debug_flags; + +#if defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L + +#define debugf(level, ...) \ + do { \ + if (unlikely(debug_flags & level)) \ + zlog_debug(__VA_ARGS__); \ + } while(0) + +#elif defined __GNUC__ + +#define debugf(level, _args...) \ + do { \ + if (unlikely(debug_flags & level)) \ + zlog_debug(_args); \ + } while(0) + +#else + +static inline void debugf(int level, const char *format, ...) { } + +#endif + diff --git a/nhrpd/linux.c b/nhrpd/linux.c new file mode 100644 index 000000000..75a16eab3 --- /dev/null +++ b/nhrpd/linux.c @@ -0,0 +1,153 @@ +/* NHRP daemon Linux specific glue + * Copyright (c) 2014-2015 Timo Teräs + * + * This file is free software: you may copy, redistribute 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "nhrp_protocol.h" +#include "os.h" +#include "netlink.h" + +static int nhrp_socket_fd = -1; + +int os_socket(void) +{ + if (nhrp_socket_fd < 0) + nhrp_socket_fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_NHRP)); + return nhrp_socket_fd; +} + +int os_sendmsg(const uint8_t *buf, size_t len, int ifindex, const uint8_t *addr, size_t addrlen) +{ + struct sockaddr_ll lladdr; + struct iovec iov = { + .iov_base = (void*) buf, + .iov_len = len, + }; + struct msghdr msg = { + .msg_name = &lladdr, + .msg_namelen = sizeof(lladdr), + .msg_iov = &iov, + .msg_iovlen = 1, + }; + int status; + + if (addrlen > sizeof(lladdr.sll_addr)) + return -1; + + memset(&lladdr, 0, sizeof(lladdr)); + lladdr.sll_family = AF_PACKET; + lladdr.sll_protocol = htons(ETH_P_NHRP); + lladdr.sll_ifindex = ifindex; + lladdr.sll_halen = addrlen; + memcpy(lladdr.sll_addr, addr, addrlen); + + status = sendmsg(nhrp_socket_fd, &msg, 0); + if (status < 0) + return -1; + + return 0; +} + +int os_recvmsg(uint8_t *buf, size_t *len, int *ifindex, uint8_t *addr, size_t *addrlen) +{ + struct sockaddr_ll lladdr; + struct iovec iov = { + .iov_base = buf, + .iov_len = *len, + }; + struct msghdr msg = { + .msg_name = &lladdr, + .msg_namelen = sizeof(lladdr), + .msg_iov = &iov, + .msg_iovlen = 1, + }; + int r; + + r = recvmsg(nhrp_socket_fd, &msg, MSG_DONTWAIT); + if (r < 0) + return r; + + *len = r; + *ifindex = lladdr.sll_ifindex; + + if (*addrlen <= (size_t) lladdr.sll_addr) { + if (memcmp(lladdr.sll_addr, "\x00\x00\x00\x00", 4) != 0) { + memcpy(addr, lladdr.sll_addr, lladdr.sll_halen); + *addrlen = lladdr.sll_halen; + } else { + *addrlen = 0; + } + } + + return 0; +} + +static int linux_configure_arp(const char *iface, int on) +{ + struct ifreq ifr; + + strncpy(ifr.ifr_name, iface, IFNAMSIZ); + if (ioctl(nhrp_socket_fd, SIOCGIFFLAGS, &ifr)) + return -1; + + if (on) + ifr.ifr_flags &= ~IFF_NOARP; + else + ifr.ifr_flags |= IFF_NOARP; + + if (ioctl(nhrp_socket_fd, SIOCSIFFLAGS, &ifr)) + return -1; + + return 0; +} + +static int linux_icmp_redirect_off(const char *iface) +{ + char fname[256]; + int fd, ret = -1; + + sprintf(fname, "/proc/sys/net/ipv4/conf/%s/send_redirects", iface); + fd = open(fname, O_WRONLY); + if (fd < 0) + return -1; + if (write(fd, "0\n", 2) == 2) + ret = 0; + close(fd); + + return ret; +} + +int os_configure_dmvpn(unsigned int ifindex, const char *ifname, int af) +{ + int ret = 0; + + switch (af) { + case AF_INET: + ret |= linux_icmp_redirect_off("all"); + ret |= linux_icmp_redirect_off(ifname); + break; + } + ret |= linux_configure_arp(ifname, 1); + ret |= netlink_configure_arp(ifindex, af); + + return ret; +} diff --git a/nhrpd/list.h b/nhrpd/list.h new file mode 100644 index 000000000..32f21ed5e --- /dev/null +++ b/nhrpd/list.h @@ -0,0 +1,191 @@ +/* Linux kernel style list handling function + * + * Written from scratch by Timo Teräs , but modeled + * after the linux kernel code. + * + * This file is free software: you may copy, redistribute 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. + */ + +#ifndef LIST_H +#define LIST_H + +#ifndef NULL +#define NULL 0L +#endif + +#ifndef offsetof +#ifdef __compiler_offsetof +#define offsetof(TYPE,MEMBER) __compiler_offsetof(TYPE,MEMBER) +#else +#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) +#endif +#endif + +#ifndef container_of +#define container_of(ptr, type, member) ({ \ + const typeof( ((type *)0)->member ) *__mptr = (ptr); \ + (type *)( (char *)__mptr - offsetof(type,member) );}) +#endif + +struct hlist_head { + struct hlist_node *first; +}; + +struct hlist_node { + struct hlist_node *next; + struct hlist_node **pprev; +}; + +static inline int hlist_empty(const struct hlist_head *h) +{ + return !h->first; +} + +static inline int hlist_hashed(const struct hlist_node *n) +{ + return n->pprev != NULL; +} + +static inline void hlist_del(struct hlist_node *n) +{ + struct hlist_node *next = n->next; + struct hlist_node **pprev = n->pprev; + + *pprev = next; + if (next) + next->pprev = pprev; + + n->next = NULL; + n->pprev = NULL; +} + +static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h) +{ + struct hlist_node *first = h->first; + + n->next = first; + if (first) + first->pprev = &n->next; + n->pprev = &h->first; + h->first = n; +} + +static inline void hlist_add_after(struct hlist_node *n, struct hlist_node *prev) +{ + n->next = prev->next; + n->pprev = &prev->next; + prev->next = n; +} + +static inline struct hlist_node **hlist_tail_ptr(struct hlist_head *h) +{ + struct hlist_node *n = h->first; + if (n == NULL) + return &h->first; + while (n->next != NULL) + n = n->next; + return &n->next; +} + +#define hlist_entry(ptr, type, member) container_of(ptr,type,member) + +#define hlist_for_each(pos, head) \ + for (pos = (head)->first; pos; pos = pos->next) + +#define hlist_for_each_safe(pos, n, head) \ + for (pos = (head)->first; pos && ({ n = pos->next; 1; }); pos = n) + +#define hlist_for_each_entry(tpos, pos, head, member) \ + for (pos = (head)->first; pos && \ + ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \ + pos = pos->next) + +#define hlist_for_each_entry_safe(tpos, pos, n, head, member) \ + for (pos = (head)->first; \ + pos && ({ n = pos->next; 1; }) && \ + ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \ + pos = n) + + +struct list_head { + struct list_head *next, *prev; +}; + +#define LIST_INITIALIZER(l) { .next = &l, .prev = &l } + +static inline void list_init(struct list_head *list) +{ + list->next = list; + list->prev = list; +} + +static inline void __list_add(struct list_head *new, + struct list_head *prev, + struct list_head *next) +{ + next->prev = new; + new->next = next; + new->prev = prev; + prev->next = new; +} + +static inline void list_add(struct list_head *new, struct list_head *head) +{ + __list_add(new, head, head->next); +} + +static inline void list_add_tail(struct list_head *new, struct list_head *head) +{ + __list_add(new, head->prev, head); +} + +static inline void __list_del(struct list_head * prev, struct list_head * next) +{ + next->prev = prev; + prev->next = next; +} + +static inline void list_del(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); + entry->next = NULL; + entry->prev = NULL; +} + +static inline int list_hashed(const struct list_head *n) +{ + return n->next != n && n->next != NULL; +} + +static inline int list_empty(const struct list_head *n) +{ + return !list_hashed(n); +} + +#define list_next(ptr, type, member) \ + (list_hashed(ptr) ? container_of((ptr)->next,type,member) : NULL) + +#define list_entry(ptr, type, member) container_of(ptr,type,member) + +#define list_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); pos = pos->next) + +#define list_for_each_safe(pos, n, head) \ + for (pos = (head)->next, n = pos->next; pos != (head); \ + pos = n, n = pos->next) + +#define list_for_each_entry(pos, head, member) \ + for (pos = list_entry((head)->next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = list_entry(pos->member.next, typeof(*pos), member)) + +#define list_for_each_entry_safe(pos, n, head, member) \ + for (pos = list_entry((head)->next, typeof(*pos), member), \ + n = list_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.next, typeof(*n), member)) + +#endif diff --git a/nhrpd/netlink.h b/nhrpd/netlink.h new file mode 100644 index 000000000..f05596ba1 --- /dev/null +++ b/nhrpd/netlink.h @@ -0,0 +1,24 @@ +/* NHRP netlink/neighbor table API + * Copyright (c) 2014-2015 Timo Teräs + * + * This file is free software: you may copy, redistribute 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. + */ + +#include + +union sockunion; +struct interface; + +extern int netlink_nflog_group; +extern int netlink_req_fd; + +int netlink_init(void); +int netlink_configure_arp(unsigned int ifindex, int pf); +void netlink_update_binding(struct interface *ifp, union sockunion *proto, union sockunion *nbma); +void netlink_set_nflog_group(int nlgroup); + +void netlink_gre_get_info(unsigned int ifindex, uint32_t *gre_key, unsigned int *link_index, struct in_addr *saddr); +void netlink_gre_set_link(unsigned int ifindex, unsigned int link_index); diff --git a/nhrpd/netlink_arp.c b/nhrpd/netlink_arp.c new file mode 100644 index 000000000..a418ecabd --- /dev/null +++ b/nhrpd/netlink_arp.c @@ -0,0 +1,275 @@ +/* NHRP netlink/neighbor table arpd code + * Copyright (c) 2014-2016 Timo Teräs + * + * This file is free software: you may copy, redistribute 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. + */ + +#include +#include +#include +#include +#include +#include + +#include "thread.h" +#include "nhrpd.h" +#include "netlink.h" +#include "znl.h" + +int netlink_req_fd = -1; +int netlink_nflog_group; +static int netlink_log_fd = -1; +static struct thread *netlink_log_thread; +static int netlink_listen_fd = -1; + +typedef void (*netlink_dispatch_f)(struct nlmsghdr *msg, struct zbuf *zb); + +void netlink_update_binding(struct interface *ifp, union sockunion *proto, union sockunion *nbma) +{ + struct nlmsghdr *n; + struct ndmsg *ndm; + struct zbuf *zb = zbuf_alloc(512); + + n = znl_nlmsg_push(zb, nbma ? RTM_NEWNEIGH : RTM_DELNEIGH, NLM_F_REQUEST | NLM_F_REPLACE | NLM_F_CREATE); + ndm = znl_push(zb, sizeof(*ndm)); + *ndm = (struct ndmsg) { + .ndm_family = sockunion_family(proto), + .ndm_ifindex = ifp->ifindex, + .ndm_type = RTN_UNICAST, + .ndm_state = nbma ? NUD_REACHABLE : NUD_FAILED, + }; + znl_rta_push(zb, NDA_DST, sockunion_get_addr(proto), family2addrsize(sockunion_family(proto))); + if (nbma) + znl_rta_push(zb, NDA_LLADDR, sockunion_get_addr(nbma), family2addrsize(sockunion_family(nbma))); + znl_nlmsg_complete(zb, n); + zbuf_send(zb, netlink_req_fd); + zbuf_recv(zb, netlink_req_fd); + zbuf_free(zb); +} + +static void netlink_neigh_msg(struct nlmsghdr *msg, struct zbuf *zb) +{ + struct ndmsg *ndm; + struct rtattr *rta; + struct nhrp_cache *c; + struct interface *ifp; + struct zbuf payload; + union sockunion addr; + size_t len; + char buf[SU_ADDRSTRLEN]; + int state; + + ndm = znl_pull(zb, sizeof(*ndm)); + if (!ndm) return; + + sockunion_family(&addr) = AF_UNSPEC; + while ((rta = znl_rta_pull(zb, &payload)) != NULL) { + len = zbuf_used(&payload); + switch (rta->rta_type) { + case NDA_DST: + sockunion_set(&addr, ndm->ndm_family, zbuf_pulln(&payload, len), len); + break; + } + } + + ifp = if_lookup_by_index(ndm->ndm_ifindex); + if (!ifp || sockunion_family(&addr) == AF_UNSPEC) + return; + + c = nhrp_cache_get(ifp, &addr, 0); + if (!c) + return; + + if (msg->nlmsg_type == RTM_GETNEIGH) { + debugf(NHRP_DEBUG_KERNEL, "Netlink: who-has %s dev %s", + sockunion2str(&addr, buf, sizeof buf), + ifp->name); + + if (c->cur.type >= NHRP_CACHE_CACHED) { + nhrp_cache_set_used(c, 1); + netlink_update_binding(ifp, &addr, &c->cur.peer->vc->remote.nbma); + } + } else { + debugf(NHRP_DEBUG_KERNEL, "Netlink: update %s dev %s nud %x", + sockunion2str(&addr, buf, sizeof buf), + ifp->name, ndm->ndm_state); + + state = (msg->nlmsg_type == RTM_NEWNEIGH) ? ndm->ndm_state : NUD_FAILED; + nhrp_cache_set_used(c, state == NUD_REACHABLE); + } +} + +static int netlink_route_recv(struct thread *t) +{ + uint8_t buf[ZNL_BUFFER_SIZE]; + int fd = THREAD_FD(t); + struct zbuf payload, zb; + struct nlmsghdr *n; + + zbuf_init(&zb, buf, sizeof(buf), 0); + while (zbuf_recv(&zb, fd) > 0) { + while ((n = znl_nlmsg_pull(&zb, &payload)) != 0) { + debugf(NHRP_DEBUG_KERNEL, "Netlink: Received msg_type %u, msg_flags %u", + n->nlmsg_type, n->nlmsg_flags); + switch (n->nlmsg_type) { + case RTM_GETNEIGH: + case RTM_NEWNEIGH: + case RTM_DELNEIGH: + netlink_neigh_msg(n, &payload); + break; + } + } + } + + thread_add_read(master, netlink_route_recv, 0, fd); + + return 0; +} + +static void netlink_log_register(int fd, int group) +{ + struct nlmsghdr *n; + struct nfgenmsg *nf; + struct nfulnl_msg_config_cmd cmd; + struct zbuf *zb = zbuf_alloc(512); + + n = znl_nlmsg_push(zb, (NFNL_SUBSYS_ULOG<<8) | NFULNL_MSG_CONFIG, NLM_F_REQUEST | NLM_F_ACK); + nf = znl_push(zb, sizeof(*nf)); + *nf = (struct nfgenmsg) { + .nfgen_family = AF_UNSPEC, + .version = NFNETLINK_V0, + .res_id = htons(group), + }; + cmd.command = NFULNL_CFG_CMD_BIND; + znl_rta_push(zb, NFULA_CFG_CMD, &cmd, sizeof(cmd)); + znl_nlmsg_complete(zb, n); + + zbuf_send(zb, fd); + zbuf_free(zb); +} + +static void netlink_log_indication(struct nlmsghdr *msg, struct zbuf *zb) +{ + struct nfgenmsg *nf; + struct rtattr *rta; + struct zbuf rtapl, pktpl; + struct interface *ifp; + struct nfulnl_msg_packet_hdr *pkthdr = NULL; + uint32_t *in_ndx = NULL; + + nf = znl_pull(zb, sizeof(*nf)); + if (!nf) return; + + memset(&pktpl, 0, sizeof(pktpl)); + while ((rta = znl_rta_pull(zb, &rtapl)) != NULL) { + switch (rta->rta_type) { + case NFULA_PACKET_HDR: + pkthdr = znl_pull(&rtapl, sizeof(*pkthdr)); + break; + case NFULA_IFINDEX_INDEV: + in_ndx = znl_pull(&rtapl, sizeof(*in_ndx)); + break; + case NFULA_PAYLOAD: + pktpl = rtapl; + break; + /* NFULA_HWHDR exists and is supposed to contain source + * hardware address. However, for ip_gre it seems to be + * the nexthop destination address if the packet matches + * route. */ + } + } + + if (!pkthdr || !in_ndx || !zbuf_used(&pktpl)) + return; + + ifp = if_lookup_by_index(htonl(*in_ndx)); + if (!ifp) + return; + + nhrp_peer_send_indication(ifp, htons(pkthdr->hw_protocol), &pktpl); +} + +static int netlink_log_recv(struct thread *t) +{ + uint8_t buf[ZNL_BUFFER_SIZE]; + int fd = THREAD_FD(t); + struct zbuf payload, zb; + struct nlmsghdr *n; + + netlink_log_thread = NULL; + + zbuf_init(&zb, buf, sizeof(buf), 0); + while (zbuf_recv(&zb, fd) > 0) { + while ((n = znl_nlmsg_pull(&zb, &payload)) != 0) { + debugf(NHRP_DEBUG_KERNEL, "Netlink-log: Received msg_type %u, msg_flags %u", + n->nlmsg_type, n->nlmsg_flags); + switch (n->nlmsg_type) { + case (NFNL_SUBSYS_ULOG<<8) | NFULNL_MSG_PACKET: + netlink_log_indication(n, &payload); + break; + } + } + } + + THREAD_READ_ON(master, netlink_log_thread, netlink_log_recv, 0, netlink_log_fd); + + return 0; +} + +void netlink_set_nflog_group(int nlgroup) +{ + if (netlink_log_fd >= 0) { + THREAD_OFF(netlink_log_thread); + close(netlink_log_fd); + netlink_log_fd = -1; + } + netlink_nflog_group = nlgroup; + if (nlgroup) { + netlink_log_fd = znl_open(NETLINK_NETFILTER, 0); + netlink_log_register(netlink_log_fd, nlgroup); + THREAD_READ_ON(master, netlink_log_thread, netlink_log_recv, 0, netlink_log_fd); + } +} + +int netlink_init(void) +{ + netlink_req_fd = znl_open(NETLINK_ROUTE, 0); + netlink_listen_fd = znl_open(NETLINK_ROUTE, RTMGRP_NEIGH); + thread_add_read(master, netlink_route_recv, 0, netlink_listen_fd); + + return 0; +} + +int netlink_configure_arp(unsigned int ifindex, int pf) +{ + struct nlmsghdr *n; + struct ndtmsg *ndtm; + struct rtattr *rta; + struct zbuf *zb = zbuf_alloc(512); + int r; + + n = znl_nlmsg_push(zb, RTM_SETNEIGHTBL, NLM_F_REQUEST | NLM_F_REPLACE); + ndtm = znl_push(zb, sizeof(*ndtm)); + *ndtm = (struct ndtmsg) { + .ndtm_family = pf, + }; + + znl_rta_push(zb, NDTA_NAME, pf == AF_INET ? "arp_cache" : "ndisc_cache", 10); + + rta = znl_rta_nested_push(zb, NDTA_PARMS); + znl_rta_push_u32(zb, NDTPA_IFINDEX, ifindex); + znl_rta_push_u32(zb, NDTPA_APP_PROBES, 1); + znl_rta_push_u32(zb, NDTPA_MCAST_PROBES, 0); + znl_rta_push_u32(zb, NDTPA_UCAST_PROBES, 0); + znl_rta_nested_complete(zb, rta); + + znl_nlmsg_complete(zb, n); + r = zbuf_send(zb, netlink_req_fd); + zbuf_recv(zb, netlink_req_fd); + zbuf_free(zb); + + return r; +} diff --git a/nhrpd/netlink_gre.c b/nhrpd/netlink_gre.c new file mode 100644 index 000000000..93998dc5f --- /dev/null +++ b/nhrpd/netlink_gre.c @@ -0,0 +1,142 @@ +/* NHRP netlink/GRE tunnel configuration code + * Copyright (c) 2014-2016 Timo Teräs + * + * This file is free software: you may copy, redistribute 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "debug.h" +#include "netlink.h" +#include "znl.h" + +static int __netlink_gre_get_data(struct zbuf *zb, struct zbuf *data, int ifindex) +{ + struct nlmsghdr *n; + struct ifinfomsg *ifi; + struct zbuf payload, rtapayload; + struct rtattr *rta; + + debugf(NHRP_DEBUG_KERNEL, "netlink-link-gre: get-info %u", ifindex); + + n = znl_nlmsg_push(zb, RTM_GETLINK, NLM_F_REQUEST); + ifi = znl_push(zb, sizeof(*ifi)); + *ifi = (struct ifinfomsg) { + .ifi_index = ifindex, + }; + znl_nlmsg_complete(zb, n); + + if (zbuf_send(zb, netlink_req_fd) < 0 || + zbuf_recv(zb, netlink_req_fd) < 0) + return -1; + + n = znl_nlmsg_pull(zb, &payload); + if (!n) return -1; + + if (n->nlmsg_type != RTM_NEWLINK) + return -1; + + ifi = znl_pull(&payload, sizeof(struct ifinfomsg)); + if (!ifi) + return -1; + + debugf(NHRP_DEBUG_KERNEL, "netlink-link-gre: ifindex %u, receive msg_type %u, msg_flags %u", + ifi->ifi_index, n->nlmsg_type, n->nlmsg_flags); + + if (ifi->ifi_index != ifindex) + return -1; + + while ((rta = znl_rta_pull(&payload, &rtapayload)) != NULL) + if (rta->rta_type == IFLA_LINKINFO) + break; + if (!rta) return -1; + + payload = rtapayload; + while ((rta = znl_rta_pull(&payload, &rtapayload)) != NULL) + if (rta->rta_type == IFLA_INFO_DATA) + break; + if (!rta) return -1; + + *data = rtapayload; + return 0; +} + +void netlink_gre_get_info(unsigned int ifindex, uint32_t *gre_key, unsigned int *link_index, struct in_addr *saddr) +{ + struct zbuf *zb = zbuf_alloc(8192), data, rtapl; + struct rtattr *rta; + + *link_index = 0; + *gre_key = 0; + saddr->s_addr = 0; + + if (__netlink_gre_get_data(zb, &data, ifindex) < 0) + goto err; + + while ((rta = znl_rta_pull(&data, &rtapl)) != NULL) { + switch (rta->rta_type) { + case IFLA_GRE_LINK: + *link_index = zbuf_get32(&rtapl); + break; + case IFLA_GRE_IKEY: + case IFLA_GRE_OKEY: + *gre_key = zbuf_get32(&rtapl); + break; + case IFLA_GRE_LOCAL: + saddr->s_addr = zbuf_get32(&rtapl); + break; + } + } +err: + zbuf_free(zb); +} + +void netlink_gre_set_link(unsigned int ifindex, unsigned int link_index) +{ + struct nlmsghdr *n; + struct ifinfomsg *ifi; + struct rtattr *rta_info, *rta_data, *rta; + struct zbuf *zr = zbuf_alloc(8192), data, rtapl; + struct zbuf *zb = zbuf_alloc(8192); + size_t len; + + if (__netlink_gre_get_data(zr, &data, ifindex) < 0) + goto err; + + n = znl_nlmsg_push(zb, RTM_NEWLINK, NLM_F_REQUEST); + ifi = znl_push(zb, sizeof(*ifi)); + *ifi = (struct ifinfomsg) { + .ifi_index = ifindex, + }; + rta_info = znl_rta_nested_push(zb, IFLA_LINKINFO); + znl_rta_push(zb, IFLA_INFO_KIND, "gre", 3); + rta_data = znl_rta_nested_push(zb, IFLA_INFO_DATA); + + znl_rta_push_u32(zb, IFLA_GRE_LINK, link_index); + while ((rta = znl_rta_pull(&data, &rtapl)) != NULL) { + if (rta->rta_type == IFLA_GRE_LINK) + continue; + len = zbuf_used(&rtapl); + znl_rta_push(zb, rta->rta_type, zbuf_pulln(&rtapl, len), len); + } + + znl_rta_nested_complete(zb, rta_data); + znl_rta_nested_complete(zb, rta_info); + + znl_nlmsg_complete(zb, n); + zbuf_send(zb, netlink_req_fd); + zbuf_recv(zb, netlink_req_fd); +err: + zbuf_free(zb); + zbuf_free(zr); +} diff --git a/nhrpd/nhrp-events.lua b/nhrpd/nhrp-events.lua new file mode 100755 index 000000000..e5e3bbf70 --- /dev/null +++ b/nhrpd/nhrp-events.lua @@ -0,0 +1,272 @@ +#!/usr/bin/lua5.2 + +-- Example NHRP events processing script which validates +-- NHRP registration GRE address against certificate subjectAltName IP +-- and auto-creates BGP pairings and filters based on sbgp extensions. + +-- Depends on lua5.2 lua5.2-posix lua5.2-cqueues lua5.2-ossl lua-asn1 + +local posix = require 'posix' +local struct = require 'struct' +local cq = require 'cqueues' +local cqs = require 'cqueues.socket' +local x509 = require 'openssl.x509' +local x509an = require 'openssl.x509.altname' +local rfc3779 = require 'asn1.rfc3779' + +local SOCK = "/var/run/nhrp-events.sock" +posix.unlink(SOCK) + +local loop = cq.new() +local nulfd = posix.open("/dev/null", posix.O_RDWR) +local listener = cqs.listen{path=SOCK} + +posix.chown(SOCK, "quagga", "quagga") +posix.setpid("u", "quagga") +posix.setpid("g", "quagga") +posix.openlog("nhrp-events", "np") + +function string.hex2bin(str) + return str:gsub('..', function(cc) return string.char(tonumber(cc, 16)) end) +end + +local function decode_ext(cert, name, tpe) + local ext = cert:getExtension(name) + if not ext then return end + return tpe.decode(ext:getData()) +end + +local function do_parse_cert(cert, out) + for type, value in pairs(cert:getSubjectAlt()) do + if type == 'IP' then + table.insert(out.GRE, value) + end + end + if #out.GRE == 0 then return end + + local asn = decode_ext(cert, 'sbgp-autonomousSysNum', rfc3779.ASIdentifiers) + if asn and asn.asnum and asn.asnum.asIdsOrRanges then + for _, as in ipairs(asn.asnum.asIdsOrRanges) do + if as.id then + out.AS = tonumber(as.id) + break + end + end + end + + local addrBlocks = decode_ext(cert, 'sbgp-ipAddrBlock', rfc3779.IPAddrBlocks) + for _, ab in ipairs(addrBlocks or {}) do + if ab.ipAddressChoice and ab.ipAddressChoice.addressesOrRanges then + for _, a in ipairs(ab.ipAddressChoice.addressesOrRanges) do + if a.addressPrefix then + table.insert(out.NET, a.addressPrefix) + end + end + end + end + + return true +end + +local function parse_cert(certhex) + local out = { + cn = "(no CN)", + AS = 0, + GRE = {}, + NET = {}, + } + local cert = x509.new(certhex:hex2bin(), 'der') + out.cn = tostring(cert:getSubject()) + -- Recognize hubs by certificate's CN to have OU=Hubs + out.hub = out.cn:match("/OU=Hubs/") and true or nil + do_parse_cert(cert, out) + return out +end + +local function execute(desc, cmd, ...) + local piper, pipew = posix.pipe() + if piper == nil then + return error("Pipe failed") + end + + local pid = posix.fork() + if pid == -1 then + return error("Fork failed") + end + if pid == 0 then + posix.close(piper) + posix.dup2(nulfd, 0) + posix.dup2(pipew, 1) + posix.dup2(nulfd, 2) + posix.execp(cmd, ...) + os.exit(1) + end + posix.close(pipew) + + -- This blocks -- perhaps should handle command executions in separate queue. + local output = {} + while true do + local d = posix.read(piper, 8192) + if d == nil or d == "" then break end + table.insert(output, d) + end + posix.close(piper) + + local _, reason, status = posix.wait(pid) + if status == 0 then + posix.syslog(6, ("Executed '%s' successfully"):format(desc)) + else + posix.syslog(3, ("Failed to execute '%s': %s %d"):format(desc, reason, status)) + end + return status, table.concat(output) +end + +local function configure_bgp(desc, ...) + local args = { + "-d", "bgpd", + "-c", "configure terminal", + } + for _, val in ipairs({...}) do + table.insert(args, "-c") + table.insert(args, val) + end + return execute(desc, "vtysh", table.unpack(args)) +end + +local last_bgp_reset = 0 + +local function bgp_reset(msg, local_cert) + local now = os.time() + if last_bgp_reset + 60 > now then return end + last_bgp_reset = now + + configure_bgp("spoke reset", + "route-map RTT-SET permit 10", "set metric rtt", "exit", + "route-map RTT-ADD permit 10", "set metric +rtt", "exit", + ("router bgp %d"):format(local_cert.AS), + "no neighbor hubs", + "neighbor hubs peer-group", + "neighbor hubs remote-as 65000", + "neighbor hubs ebgp-multihop 1", + "neighbor hubs disable-connected-check", + "neighbor hubs timers 10 30", + "neighbor hubs timers connect 10", + "neighbor hubs next-hop-self all", + "neighbor hubs soft-reconfiguration inbound", + "neighbor hubs route-map RTT-ADD in") +end + +local function bgp_nhs_up(msg, remote_cert, local_cert) + configure_bgp(("nhs-up %s"):format(msg.remote_addr), + ("router bgp %s"):format(local_cert.AS), + ("neighbor %s peer-group hubs"):format(msg.remote_addr)) +end + +local function bgp_nhs_down(msg, remote_cert, local_cert) + configure_bgp(("nhs-down %s"):format(msg.remote_addr), + ("router bgp %s"):format(local_cert.AS), + ("no neighbor %s"):format(msg.remote_addr)) +end + +local function bgp_create_spoke_rules(msg, remote_cert, local_cert) + if not local_cert.hub then return end + + local bgpcfg = {} + for seq, net in ipairs(remote_cert.NET) do + table.insert(bgpcfg, + ("ip prefix-list net-%s-in seq %d permit %s le %d"):format( + msg.remote_addr, seq * 5, net, + remote_cert.hub and 32 or 26)) + end + table.insert(bgpcfg, ("router bgp %s"):format(local_cert.AS)) + if remote_cert.hub then + table.insert(bgpcfg, ("neighbor %s peer-group hubs"):format(msg.remote_addr)) + elseif local_cert.AS == remote_cert.AS then + table.insert(bgpcfg, ("neighbor %s peer-group spoke-ibgp"):format(msg.remote_addr)) + else + table.insert(bgpcfg, ("neighbor %s remote-as %s"):format(msg.remote_addr, remote_cert.AS)) + table.insert(bgpcfg, ("neighbor %s peer-group spoke-ebgp"):format(msg.remote_addr)) + end + table.insert(bgpcfg, ("neighbor %s prefix-list net-%s-in in"):format(msg.remote_addr, msg.remote_addr)) + + local status, output = configure_bgp(("nhc-register %s"):format(msg.remote_addr), table.unpack(bgpcfg)) + if output:find("Cannot") then + posix.syslog(6, "BGP: "..output) + configure_bgp( + ("nhc-recreate %s"):format(msg.remote_addr), + ("router bgp %s"):format(local_cert.AS), + ("no neighbor %s"):format(msg.remote_addr), + table.unpack(bgpcfg)) + end +end + +local function handle_message(msg) + if msg.event ~= "authorize-binding" then return end + + -- Verify protocol address against certificate + local auth = false + local local_cert = parse_cert(msg.local_cert) + local remote_cert = parse_cert(msg.remote_cert) + for _, gre in pairs(remote_cert.GRE) do + if gre == msg.remote_addr then auth = true end + end + if not auth then + posix.syslog(3, ("GRE %s to NBMA %s DENIED (cert '%s', allows: %s)"):format( + msg.remote_addr, msg.remote_nbma, + remote_cert.cn, table.concat(remote_cert.GRE, " "))) + return "deny" + end + posix.syslog(6, ("GRE %s to NBMA %s authenticated for %s"):format( + msg.remote_addr, msg.remote_nbma, remote_cert.cn)) + + -- Automatic BGP binding for hub-spoke connections + if msg.type == "nhs" and msg.old_type ~= "nhs" then + if not local_cert.hub then + if tonumber(msg.num_nhs) == 0 and msg.vc_initiated == "yes" then + bgp_reset(msg, local_cert) + end + bgp_nhs_up(msg, remote_cert, local_cert) + else + bgp_create_spoke_rules(msg, remote_cert, local_cert) + end + elseif msg.type ~= "nhs" and msg.old_type == "nhs" then + bgp_nhs_down(msg, remote_cert, local_cert) + elseif msg.type == "dynamic" and msg.old_type ~= "dynamic" then + bgp_create_spoke_rules(msg, remote_cert, local_cert) + end + + return "accept" +end + +local function handle_connection(conn) + local msg = {} + for l in conn:lines() do + if l == "" then + res = handle_message(msg) + if msg.eventid then + conn:write(("eventid=%s\nresult=%s\n\n"):format(msg.eventid, res or "default")) + end + msg = {} + else + local key, value = l:match('([^=]*)=(.*)') + if key and value then + msg[key] = value + end + end + end + conn:close() +end + +loop:wrap(function() + while true do + local conn = listener:accept() + conn:setmode("b", "bl") + loop:wrap(function() + local ok, msg = pcall(handle_connection, conn) + if not ok then posix.syslog(3, msg) end + conn:close() + end) + end +end) + +print(loop:loop()) diff --git a/nhrpd/nhrp_cache.c b/nhrpd/nhrp_cache.c new file mode 100644 index 000000000..447a814c0 --- /dev/null +++ b/nhrpd/nhrp_cache.c @@ -0,0 +1,341 @@ +/* NHRP cache + * Copyright (c) 2014-2015 Timo Teräs + * + * This file is free software: you may copy, redistribute 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. + */ + +#include "zebra.h" +#include "memory.h" +#include "thread.h" +#include "hash.h" +#include "nhrpd.h" + +#include "netlink.h" + +unsigned long nhrp_cache_counts[NHRP_CACHE_NUM_TYPES]; + +const char * const nhrp_cache_type_str[] = { + [NHRP_CACHE_INVALID] = "invalid", + [NHRP_CACHE_INCOMPLETE] = "incomplete", + [NHRP_CACHE_NEGATIVE] = "negative", + [NHRP_CACHE_CACHED] = "cached", + [NHRP_CACHE_DYNAMIC] = "dynamic", + [NHRP_CACHE_NHS] = "nhs", + [NHRP_CACHE_STATIC] = "static", + [NHRP_CACHE_LOCAL] = "local", +}; + +static unsigned int nhrp_cache_protocol_key(void *peer_data) +{ + struct nhrp_cache *p = peer_data; + return sockunion_hash(&p->remote_addr); +} + +static int nhrp_cache_protocol_cmp(const void *cache_data, const void *key_data) +{ + const struct nhrp_cache *a = cache_data; + const struct nhrp_cache *b = key_data; + return sockunion_same(&a->remote_addr, &b->remote_addr); +} + +static void *nhrp_cache_alloc(void *data) +{ + struct nhrp_cache *p, *key = data; + + p = XMALLOC(MTYPE_NHRP_CACHE, sizeof(struct nhrp_cache)); + if (p) { + *p = (struct nhrp_cache) { + .cur.type = NHRP_CACHE_INVALID, + .new.type = NHRP_CACHE_INVALID, + .remote_addr = key->remote_addr, + .ifp = key->ifp, + .notifier_list = NOTIFIER_LIST_INITIALIZER(&p->notifier_list), + }; + nhrp_cache_counts[p->cur.type]++; + } + + return p; +} + +static void nhrp_cache_free(struct nhrp_cache *c) +{ + struct nhrp_interface *nifp = c->ifp->info; + + zassert(c->cur.type == NHRP_CACHE_INVALID && c->cur.peer == NULL); + zassert(c->new.type == NHRP_CACHE_INVALID && c->new.peer == NULL); + nhrp_cache_counts[c->cur.type]--; + notifier_call(&c->notifier_list, NOTIFY_CACHE_DELETE); + zassert(!notifier_active(&c->notifier_list)); + hash_release(nifp->cache_hash, c); + XFREE(MTYPE_NHRP_CACHE, c); +} + +struct nhrp_cache *nhrp_cache_get(struct interface *ifp, union sockunion *remote_addr, int create) +{ + struct nhrp_interface *nifp = ifp->info; + struct nhrp_cache key; + + if (!nifp->cache_hash) { + nifp->cache_hash = hash_create(nhrp_cache_protocol_key, nhrp_cache_protocol_cmp); + if (!nifp->cache_hash) + return NULL; + } + + key.remote_addr = *remote_addr; + key.ifp = ifp; + + return hash_get(nifp->cache_hash, &key, create ? nhrp_cache_alloc : NULL); +} + +static int nhrp_cache_do_free(struct thread *t) +{ + struct nhrp_cache *c = THREAD_ARG(t); + c->t_timeout = NULL; + nhrp_cache_free(c); + return 0; +} + +static int nhrp_cache_do_timeout(struct thread *t) +{ + struct nhrp_cache *c = THREAD_ARG(t); + c->t_timeout = NULL; + if (c->cur.type != NHRP_CACHE_INVALID) + nhrp_cache_update_binding(c, c->cur.type, -1, NULL, 0, NULL); + return 0; +} + +static void nhrp_cache_update_route(struct nhrp_cache *c) +{ + struct prefix pfx; + struct nhrp_peer *p = c->cur.peer; + + sockunion2hostprefix(&c->remote_addr, &pfx); + + if (p && nhrp_peer_check(p, 1)) { + netlink_update_binding(p->ifp, &c->remote_addr, &p->vc->remote.nbma); + nhrp_route_announce(1, c->cur.type, &pfx, c->ifp, NULL, c->cur.mtu); + if (c->cur.type >= NHRP_CACHE_DYNAMIC) { + nhrp_route_update_nhrp(&pfx, c->ifp); + c->nhrp_route_installed = 1; + } else if (c->nhrp_route_installed) { + nhrp_route_update_nhrp(&pfx, NULL); + c->nhrp_route_installed = 0; + } + if (!c->route_installed) { + notifier_call(&c->notifier_list, NOTIFY_CACHE_UP); + c->route_installed = 1; + } + } else { + if (c->nhrp_route_installed) { + nhrp_route_update_nhrp(&pfx, NULL); + c->nhrp_route_installed = 0; + } + if (c->route_installed) { + sockunion2hostprefix(&c->remote_addr, &pfx); + notifier_call(&c->notifier_list, NOTIFY_CACHE_DOWN); + nhrp_route_announce(0, c->cur.type, &pfx, NULL, NULL, 0); + c->route_installed = 0; + } + } +} + +static void nhrp_cache_peer_notifier(struct notifier_block *n, unsigned long cmd) +{ + struct nhrp_cache *c = container_of(n, struct nhrp_cache, peer_notifier); + + switch (cmd) { + case NOTIFY_PEER_UP: + nhrp_cache_update_route(c); + break; + case NOTIFY_PEER_DOWN: + case NOTIFY_PEER_IFCONFIG_CHANGED: + notifier_call(&c->notifier_list, NOTIFY_CACHE_DOWN); + nhrp_cache_update_binding(c, c->cur.type, -1, NULL, 0, NULL); + break; + case NOTIFY_PEER_NBMA_CHANGING: + if (c->cur.type == NHRP_CACHE_DYNAMIC) + c->cur.peer->vc->abort_migration = 1; + break; + } +} + +static void nhrp_cache_reset_new(struct nhrp_cache *c) +{ + THREAD_OFF(c->t_auth); + if (list_hashed(&c->newpeer_notifier.notifier_entry)) + nhrp_peer_notify_del(c->new.peer, &c->newpeer_notifier); + nhrp_peer_unref(c->new.peer); + memset(&c->new, 0, sizeof(c->new)); + c->new.type = NHRP_CACHE_INVALID; +} + +static void nhrp_cache_update_timers(struct nhrp_cache *c) +{ + THREAD_OFF(c->t_timeout); + + switch (c->cur.type) { + case NHRP_CACHE_INVALID: + if (!c->t_auth) + THREAD_TIMER_MSEC_ON(master, c->t_timeout, nhrp_cache_do_free, c, 10); + break; + default: + if (c->cur.expires) + THREAD_TIMER_ON(master, c->t_timeout, nhrp_cache_do_timeout, c, c->cur.expires - recent_relative_time().tv_sec); + break; + } +} + +static void nhrp_cache_authorize_binding(struct nhrp_reqid *r, void *arg) +{ + struct nhrp_cache *c = container_of(r, struct nhrp_cache, eventid); + char buf[SU_ADDRSTRLEN]; + + debugf(NHRP_DEBUG_COMMON, "cache: %s %s: %s", + c->ifp->name, sockunion2str(&c->remote_addr, buf, sizeof buf), + (const char *) arg); + + nhrp_reqid_free(&nhrp_event_reqid, r); + + if (arg && strcmp(arg, "accept") == 0) { + if (c->cur.peer) { + netlink_update_binding(c->cur.peer->ifp, &c->remote_addr, NULL); + nhrp_peer_notify_del(c->cur.peer, &c->peer_notifier); + nhrp_peer_unref(c->cur.peer); + } + nhrp_cache_counts[c->cur.type]--; + nhrp_cache_counts[c->new.type]++; + c->cur = c->new; + c->cur.peer = nhrp_peer_ref(c->cur.peer); + nhrp_cache_reset_new(c); + if (c->cur.peer) + nhrp_peer_notify_add(c->cur.peer, &c->peer_notifier, nhrp_cache_peer_notifier); + nhrp_cache_update_route(c); + notifier_call(&c->notifier_list, NOTIFY_CACHE_BINDING_CHANGE); + } else { + nhrp_cache_reset_new(c); + } + + nhrp_cache_update_timers(c); +} + +static int nhrp_cache_do_auth_timeout(struct thread *t) +{ + struct nhrp_cache *c = THREAD_ARG(t); + c->t_auth = NULL; + nhrp_cache_authorize_binding(&c->eventid, (void *) "timeout"); + return 0; +} + +static void nhrp_cache_newpeer_notifier(struct notifier_block *n, unsigned long cmd) +{ + struct nhrp_cache *c = container_of(n, struct nhrp_cache, newpeer_notifier); + + switch (cmd) { + case NOTIFY_PEER_UP: + if (nhrp_peer_check(c->new.peer, 1)) { + evmgr_notify("authorize-binding", c, nhrp_cache_authorize_binding); + THREAD_TIMER_ON(master, c->t_auth, nhrp_cache_do_auth_timeout, c, 10); + } + break; + case NOTIFY_PEER_DOWN: + case NOTIFY_PEER_IFCONFIG_CHANGED: + nhrp_cache_reset_new(c); + break; + } +} + +int nhrp_cache_update_binding(struct nhrp_cache *c, enum nhrp_cache_type type, int holding_time, struct nhrp_peer *p, uint32_t mtu, union sockunion *nbma_oa) +{ + if (c->cur.type > type || c->new.type > type) { + nhrp_peer_unref(p); + return 0; + } + + /* Sanitize MTU */ + switch (sockunion_family(&c->remote_addr)) { + case AF_INET: + if (mtu < 576 || mtu >= 1500) + mtu = 0; + /* Opennhrp announces nbma mtu, but we use protocol mtu. + * This heuristic tries to fix up it. */ + if (mtu > 1420) mtu = (mtu & -16) - 80; + break; + default: + mtu = 0; + break; + } + + nhrp_cache_reset_new(c); + if (c->cur.type == type && c->cur.peer == p && c->cur.mtu == mtu) { + if (holding_time > 0) c->cur.expires = recent_relative_time().tv_sec + holding_time; + if (nbma_oa) c->cur.remote_nbma_natoa = *nbma_oa; + else memset(&c->cur.remote_nbma_natoa, 0, sizeof c->cur.remote_nbma_natoa); + nhrp_peer_unref(p); + } else { + c->new.type = type; + c->new.peer = p; + c->new.mtu = mtu; + if (nbma_oa) c->new.remote_nbma_natoa = *nbma_oa; + + if (holding_time > 0) + c->new.expires = recent_relative_time().tv_sec + holding_time; + else if (holding_time < 0) + c->new.type = NHRP_CACHE_INVALID; + + if (c->new.type == NHRP_CACHE_INVALID || + c->new.type >= NHRP_CACHE_STATIC || + c->map) { + nhrp_cache_authorize_binding(&c->eventid, (void *) "accept"); + } else { + nhrp_peer_notify_add(c->new.peer, &c->newpeer_notifier, nhrp_cache_newpeer_notifier); + nhrp_cache_newpeer_notifier(&c->newpeer_notifier, NOTIFY_PEER_UP); + THREAD_TIMER_ON(master, c->t_auth, nhrp_cache_do_auth_timeout, c, 60); + } + } + nhrp_cache_update_timers(c); + + return 1; +} + +void nhrp_cache_set_used(struct nhrp_cache *c, int used) +{ + c->used = used; + if (c->used) + notifier_call(&c->notifier_list, NOTIFY_CACHE_USED); +} + +struct nhrp_cache_iterator_ctx { + void (*cb)(struct nhrp_cache *, void *); + void *ctx; +}; + +static void nhrp_cache_iterator(struct hash_backet *b, void *ctx) +{ + struct nhrp_cache_iterator_ctx *ic = ctx; + ic->cb(b->data, ic->ctx); +} + +void nhrp_cache_foreach(struct interface *ifp, void (*cb)(struct nhrp_cache *, void *), void *ctx) +{ + struct nhrp_interface *nifp = ifp->info; + struct nhrp_cache_iterator_ctx ic = { + .cb = cb, + .ctx = ctx, + }; + + if (nifp->cache_hash) + hash_iterate(nifp->cache_hash, nhrp_cache_iterator, &ic); +} + +void nhrp_cache_notify_add(struct nhrp_cache *c, struct notifier_block *n, notifier_fn_t fn) +{ + notifier_add(n, &c->notifier_list, fn); +} + +void nhrp_cache_notify_del(struct nhrp_cache *c, struct notifier_block *n) +{ + notifier_del(n); +} diff --git a/nhrpd/nhrp_event.c b/nhrpd/nhrp_event.c new file mode 100644 index 000000000..34cb0655f --- /dev/null +++ b/nhrpd/nhrp_event.c @@ -0,0 +1,283 @@ +/* NHRP event manager + * Copyright (c) 2014-2015 Timo Teräs + * + * This file is free software: you may copy, redistribute 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. + */ + +#include +#include +#include + +#include "thread.h" +#include "zbuf.h" +#include "log.h" +#include "nhrpd.h" + +const char *nhrp_event_socket_path; +struct nhrp_reqid_pool nhrp_event_reqid; + +struct event_manager { + struct thread *t_reconnect, *t_read, *t_write; + struct zbuf ibuf; + struct zbuf_queue obuf; + int fd; + uint8_t ibuf_data[4*1024]; +}; + +static int evmgr_reconnect(struct thread *t); + +static void evmgr_connection_error(struct event_manager *evmgr) +{ + THREAD_OFF(evmgr->t_read); + THREAD_OFF(evmgr->t_write); + zbuf_reset(&evmgr->ibuf); + zbufq_reset(&evmgr->obuf); + + if (evmgr->fd >= 0) + close(evmgr->fd); + evmgr->fd = -1; + if (nhrp_event_socket_path) + THREAD_TIMER_MSEC_ON(master, evmgr->t_reconnect, evmgr_reconnect, + evmgr, 10); +} + +static void evmgr_recv_message(struct event_manager *evmgr, struct zbuf *zb) +{ + struct zbuf zl; + uint32_t eventid = 0; + size_t len; + char buf[256], result[64] = ""; + + while (zbuf_may_pull_until(zb, "\n", &zl)) { + len = zbuf_used(&zl) - 1; + if (len >= sizeof(buf)-1) + continue; + memcpy(buf, zbuf_pulln(&zl, len), len); + buf[len] = 0; + + debugf(NHRP_DEBUG_EVENT, "evmgr: msg: %s", buf); + sscanf(buf, "eventid=%d", &eventid); + sscanf(buf, "result=%63s", result); + } + debugf(NHRP_DEBUG_EVENT, "evmgr: received: eventid=%d result=%s", eventid, result); + if (eventid && result[0]) { + struct nhrp_reqid *r = nhrp_reqid_lookup(&nhrp_event_reqid, eventid); + if (r) r->cb(r, result); + } +} + +static int evmgr_read(struct thread *t) +{ + struct event_manager *evmgr = THREAD_ARG(t); + struct zbuf *ibuf = &evmgr->ibuf; + struct zbuf msg; + + evmgr->t_read = NULL; + if (zbuf_read(ibuf, evmgr->fd, (size_t) -1) < 0) { + evmgr_connection_error(evmgr); + return 0; + } + + /* Process all messages in buffer */ + while (zbuf_may_pull_until(ibuf, "\n\n", &msg)) + evmgr_recv_message(evmgr, &msg); + + THREAD_READ_ON(master, evmgr->t_read, evmgr_read, evmgr, evmgr->fd); + return 0; +} + +static int evmgr_write(struct thread *t) +{ + struct event_manager *evmgr = THREAD_ARG(t); + int r; + + evmgr->t_write = NULL; + r = zbufq_write(&evmgr->obuf, evmgr->fd); + if (r > 0) { + THREAD_WRITE_ON(master, evmgr->t_write, evmgr_write, evmgr, evmgr->fd); + } else if (r < 0) { + evmgr_connection_error(evmgr); + } + + return 0; +} + +static void evmgr_hexdump(struct zbuf *zb, const uint8_t *val, size_t vallen) +{ + static const char xd[] = "0123456789abcdef"; + size_t i; + char *ptr; + + ptr = zbuf_pushn(zb, 2*vallen); + if (!ptr) return; + + for (i = 0; i < vallen; i++) { + uint8_t b = val[i]; + *(ptr++) = xd[b >> 4]; + *(ptr++) = xd[b & 0xf]; + } +} + +static void evmgr_put(struct zbuf *zb, const char *fmt, ...) +{ + const char *pos, *nxt, *str; + const uint8_t *bin; + const union sockunion *su; + int len; + va_list va; + + va_start(va, fmt); + for (pos = fmt; (nxt = strchr(pos, '%')) != NULL; pos = nxt + 2) { + zbuf_put(zb, pos, nxt-pos); + switch (nxt[1]) { + case '%': + zbuf_put8(zb, '%'); + break; + case 'u': + zb->tail += snprintf((char *) zb->tail, zbuf_tailroom(zb), "%u", va_arg(va, uint32_t)); + break; + case 's': + str = va_arg(va, const char *); + zbuf_put(zb, str, strlen(str)); + break; + case 'U': + su = va_arg(va, const union sockunion *); + if (sockunion2str(su, (char *) zb->tail, zbuf_tailroom(zb))) + zb->tail += strlen((char *) zb->tail); + else + zbuf_set_werror(zb); + break; + case 'H': + bin = va_arg(va, const uint8_t *); + len = va_arg(va, int); + evmgr_hexdump(zb, bin, len); + break; + } + } + va_end(va); + zbuf_put(zb, pos, strlen(pos)); +} + +static void evmgr_submit(struct event_manager *evmgr, struct zbuf *obuf) +{ + if (obuf->error) { + zbuf_free(obuf); + return; + } + zbuf_put(obuf, "\n", 1); + zbufq_queue(&evmgr->obuf, obuf); + if (evmgr->fd >= 0) + THREAD_WRITE_ON(master, evmgr->t_write, evmgr_write, evmgr, evmgr->fd); +} + +static int evmgr_reconnect(struct thread *t) +{ + struct event_manager *evmgr = THREAD_ARG(t); + int fd; + + evmgr->t_reconnect = NULL; + if (evmgr->fd >= 0 || !nhrp_event_socket_path) return 0; + + fd = sock_open_unix(nhrp_event_socket_path); + if (fd < 0) { + zlog_warn("%s: failure connecting nhrp-event socket: %s", + __PRETTY_FUNCTION__, strerror(errno)); + zbufq_reset(&evmgr->obuf); + THREAD_TIMER_ON(master, evmgr->t_reconnect, evmgr_reconnect, evmgr, 10); + return 0; + } + + zlog_info("Connected to Event Manager"); + evmgr->fd = fd; + THREAD_READ_ON(master, evmgr->t_read, evmgr_read, evmgr, evmgr->fd); + + return 0; +} + +static struct event_manager evmgr_connection; + +void evmgr_init(void) +{ + struct event_manager *evmgr = &evmgr_connection; + + evmgr->fd = -1; + zbuf_init(&evmgr->ibuf, evmgr->ibuf_data, sizeof(evmgr->ibuf_data), 0); + zbufq_init(&evmgr->obuf); + THREAD_TIMER_MSEC_ON(master, evmgr->t_reconnect, evmgr_reconnect, evmgr, 10); +} + +void evmgr_set_socket(const char *socket) +{ + if (nhrp_event_socket_path) { + free((char *) nhrp_event_socket_path); + nhrp_event_socket_path = NULL; + } + if (socket) + nhrp_event_socket_path = strdup(socket); + evmgr_connection_error(&evmgr_connection); +} + +void evmgr_terminate(void) +{ +} + +void evmgr_notify(const char *name, struct nhrp_cache *c, void (*cb)(struct nhrp_reqid *, void *)) +{ + struct event_manager *evmgr = &evmgr_connection; + struct nhrp_vc *vc; + struct nhrp_interface *nifp = c->ifp->info; + struct zbuf *zb; + afi_t afi = family2afi(sockunion_family(&c->remote_addr)); + + if (!nhrp_event_socket_path) { + cb(&c->eventid, (void*) "accept"); + return; + } + + debugf(NHRP_DEBUG_EVENT, "evmgr: sending event %s", name); + + vc = c->new.peer ? c->new.peer->vc : NULL; + zb = zbuf_alloc(1024 + (vc ? (vc->local.certlen + vc->remote.certlen) * 2 : 0)); + + if (cb) { + nhrp_reqid_free(&nhrp_event_reqid, &c->eventid); + evmgr_put(zb, + "eventid=%u\n", + nhrp_reqid_alloc(&nhrp_event_reqid, &c->eventid, cb)); + } + + evmgr_put(zb, + "event=%s\n" + "type=%s\n" + "old_type=%s\n" + "num_nhs=%u\n" + "interface=%s\n" + "local_addr=%U\n", + name, + nhrp_cache_type_str[c->new.type], + nhrp_cache_type_str[c->cur.type], + (unsigned int) nhrp_cache_counts[NHRP_CACHE_NHS], + c->ifp->name, + &nifp->afi[afi].addr); + + if (vc) { + evmgr_put(zb, + "vc_initiated=%s\n" + "local_nbma=%U\n" + "local_cert=%H\n" + "remote_addr=%U\n" + "remote_nbma=%U\n" + "remote_cert=%H\n", + c->new.peer->requested ? "yes" : "no", + &vc->local.nbma, + vc->local.cert, vc->local.certlen, + &c->remote_addr, &vc->remote.nbma, + vc->remote.cert, vc->remote.certlen); + } + + evmgr_submit(evmgr, zb); +} + diff --git a/nhrpd/nhrp_interface.c b/nhrpd/nhrp_interface.c new file mode 100644 index 000000000..4ea807646 --- /dev/null +++ b/nhrpd/nhrp_interface.c @@ -0,0 +1,406 @@ +/* NHRP interface + * Copyright (c) 2014-2015 Timo Teräs + * + * This file is free software: you may copy, redistribute 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. + */ + +#include +#include "zebra.h" +#include "linklist.h" +#include "memory.h" +#include "thread.h" + +#include "nhrpd.h" +#include "os.h" +#include "netlink.h" + +static int nhrp_if_new_hook(struct interface *ifp) +{ + struct nhrp_interface *nifp; + afi_t afi; + + nifp = XCALLOC(MTYPE_NHRP_IF, sizeof(struct nhrp_interface)); + if (!nifp) return 0; + + ifp->info = nifp; + nifp->ifp = ifp; + + notifier_init(&nifp->notifier_list); + for (afi = 0; afi < AFI_MAX; afi++) { + struct nhrp_afi_data *ad = &nifp->afi[afi]; + ad->holdtime = NHRPD_DEFAULT_HOLDTIME; + list_init(&ad->nhslist_head); + } + + return 0; +} + +static int nhrp_if_delete_hook(struct interface *ifp) +{ + XFREE(MTYPE_NHRP_IF, ifp->info); + return 0; +} + +void nhrp_interface_init(void) +{ + if_add_hook(IF_NEW_HOOK, nhrp_if_new_hook); + if_add_hook(IF_DELETE_HOOK, nhrp_if_delete_hook); +} + +void nhrp_interface_update_mtu(struct interface *ifp, afi_t afi) +{ + struct nhrp_interface *nifp = ifp->info; + struct nhrp_afi_data *if_ad = &nifp->afi[afi]; + unsigned short new_mtu; + + if (if_ad->configured_mtu < 0) + new_mtu = nifp->nbmaifp ? nifp->nbmaifp->mtu : 0; + else + new_mtu = if_ad->configured_mtu; + if (new_mtu >= 1500) + new_mtu = 0; + + if (new_mtu != if_ad->mtu) { + debugf(NHRP_DEBUG_IF, "%s: MTU changed to %d", ifp->name, new_mtu); + if_ad->mtu = new_mtu; + notifier_call(&nifp->notifier_list, NOTIFY_INTERFACE_MTU_CHANGED); + } +} + +static void nhrp_interface_update_source(struct interface *ifp) +{ + struct nhrp_interface *nifp = ifp->info; + + if (!nifp->source || !nifp->nbmaifp || + nifp->linkidx == nifp->nbmaifp->ifindex) + return; + + nifp->linkidx = nifp->nbmaifp->ifindex; + debugf(NHRP_DEBUG_IF, "%s: bound device index changed to %d", ifp->name, nifp->linkidx); + netlink_gre_set_link(ifp->ifindex, nifp->linkidx); +} + +static void nhrp_interface_interface_notifier(struct notifier_block *n, unsigned long cmd) +{ + struct nhrp_interface *nifp = container_of(n, struct nhrp_interface, nbmanifp_notifier); + struct interface *nbmaifp = nifp->nbmaifp; + struct nhrp_interface *nbmanifp = nbmaifp->info; + char buf[SU_ADDRSTRLEN]; + + switch (cmd) { + case NOTIFY_INTERFACE_CHANGED: + nhrp_interface_update_mtu(nifp->ifp, AFI_IP); + nhrp_interface_update_source(nifp->ifp); + break; + case NOTIFY_INTERFACE_ADDRESS_CHANGED: + nifp->nbma = nbmanifp->afi[AFI_IP].addr; + nhrp_interface_update(nifp->ifp); + notifier_call(&nifp->notifier_list, NOTIFY_INTERFACE_NBMA_CHANGED); + debugf(NHRP_DEBUG_IF, "%s: NBMA change: address %s", + nifp->ifp->name, + sockunion2str(&nifp->nbma, buf, sizeof buf)); + break; + } +} + +static void nhrp_interface_update_nbma(struct interface *ifp) +{ + struct nhrp_interface *nifp = ifp->info, *nbmanifp = NULL; + struct interface *nbmaifp = NULL; + union sockunion nbma; + + sockunion_family(&nbma) = AF_UNSPEC; + + if (nifp->source) + nbmaifp = if_lookup_by_name(nifp->source); + + switch (ifp->ll_type) { + case ZEBRA_LLT_IPGRE: { + struct in_addr saddr = {0}; + netlink_gre_get_info(ifp->ifindex, &nifp->grekey, &nifp->linkidx, &saddr); + debugf(NHRP_DEBUG_IF, "%s: GRE: %x %x %x", ifp->name, nifp->grekey, nifp->linkidx, saddr.s_addr); + if (saddr.s_addr) + sockunion_set(&nbma, AF_INET, (u_char *) &saddr.s_addr, sizeof(saddr.s_addr)); + else if (!nbmaifp && nifp->linkidx != IFINDEX_INTERNAL) + nbmaifp = if_lookup_by_index(nifp->linkidx); + } + break; + default: + break; + } + + if (nbmaifp) + nbmanifp = nbmaifp->info; + + if (nbmaifp != nifp->nbmaifp) { + if (nifp->nbmaifp) + notifier_del(&nifp->nbmanifp_notifier); + nifp->nbmaifp = nbmaifp; + if (nbmaifp) { + notifier_add(&nifp->nbmanifp_notifier, &nbmanifp->notifier_list, nhrp_interface_interface_notifier); + debugf(NHRP_DEBUG_IF, "%s: bound to %s", ifp->name, nbmaifp->name); + } + } + + if (nbmaifp) { + if (sockunion_family(&nbma) == AF_UNSPEC) + nbma = nbmanifp->afi[AFI_IP].addr; + nhrp_interface_update_mtu(ifp, AFI_IP); + nhrp_interface_update_source(ifp); + } + + if (!sockunion_same(&nbma, &nifp->nbma)) { + nifp->nbma = nbma; + nhrp_interface_update(nifp->ifp); + debugf(NHRP_DEBUG_IF, "%s: NBMA address changed", ifp->name); + notifier_call(&nifp->notifier_list, NOTIFY_INTERFACE_NBMA_CHANGED); + } + + nhrp_interface_update(ifp); +} + +static void nhrp_interface_update_address(struct interface *ifp, afi_t afi, int force) +{ + const int family = afi2family(afi); + struct nhrp_interface *nifp = ifp->info; + struct nhrp_afi_data *if_ad = &nifp->afi[afi]; + struct nhrp_cache *nc; + struct connected *c, *best; + struct listnode *cnode; + union sockunion addr; + char buf[PREFIX_STRLEN]; + + /* Select new best match preferring primary address */ + best = NULL; + for (ALL_LIST_ELEMENTS_RO(ifp->connected, cnode, c)) { + if (PREFIX_FAMILY(c->address) != family) + continue; + if (best == NULL) { + best = c; + continue; + } + if ((best->flags & ZEBRA_IFA_SECONDARY) && !(c->flags & ZEBRA_IFA_SECONDARY)) { + best = c; + continue; + } + if (!(best->flags & ZEBRA_IFA_SECONDARY) && (c->flags & ZEBRA_IFA_SECONDARY)) + continue; + if (best->address->prefixlen > c->address->prefixlen) { + best = c; + continue; + } + if (best->address->prefixlen < c->address->prefixlen) + continue; + } + + /* On NHRP interfaces a host prefix is required */ + if (best && if_ad->configured && best->address->prefixlen != 8 * prefix_blen(best->address)) { + zlog_notice("%s: %s is not a host prefix", ifp->name, + prefix2str(best->address, buf, sizeof buf)); + best = NULL; + } + + /* Update address if it changed */ + if (best) + prefix2sockunion(best->address, &addr); + else + memset(&addr, 0, sizeof(addr)); + + if (!force && sockunion_same(&if_ad->addr, &addr)) + return; + + if (sockunion_family(&if_ad->addr) != AF_UNSPEC) { + nc = nhrp_cache_get(ifp, &if_ad->addr, 0); + if (nc) nhrp_cache_update_binding(nc, NHRP_CACHE_LOCAL, -1, NULL, 0, NULL); + } + + debugf(NHRP_DEBUG_KERNEL, "%s: IPv%d address changed to %s", + ifp->name, afi == AFI_IP ? 4 : 6, + best ? prefix2str(best->address, buf, sizeof buf) : "(none)"); + if_ad->addr = addr; + + if (if_ad->configured && sockunion_family(&if_ad->addr) != AF_UNSPEC) { + nc = nhrp_cache_get(ifp, &addr, 1); + if (nc) nhrp_cache_update_binding(nc, NHRP_CACHE_LOCAL, 0, NULL, 0, NULL); + } + + notifier_call(&nifp->notifier_list, NOTIFY_INTERFACE_ADDRESS_CHANGED); +} + +void nhrp_interface_update(struct interface *ifp) +{ + struct nhrp_interface *nifp = ifp->info; + struct nhrp_afi_data *if_ad; + afi_t afi; + int enabled = 0; + + notifier_call(&nifp->notifier_list, NOTIFY_INTERFACE_CHANGED); + + for (afi = 0; afi < AFI_MAX; afi++) { + if_ad = &nifp->afi[afi]; + + if (sockunion_family(&nifp->nbma) == AF_UNSPEC || + ifp->ifindex == IFINDEX_INTERNAL || !if_is_up(ifp) || + !if_ad->network_id) { + if (if_ad->configured) { + if_ad->configured = 0; + nhrp_interface_update_address(ifp, afi, 1); + } + continue; + } + + if (!if_ad->configured) { + os_configure_dmvpn(ifp->ifindex, ifp->name, afi2family(afi)); + if_ad->configured = 1; + nhrp_interface_update_address(ifp, afi, 1); + } + + enabled = 1; + } + + if (enabled != nifp->enabled) { + nifp->enabled = enabled; + notifier_call(&nifp->notifier_list, enabled ? NOTIFY_INTERFACE_UP : NOTIFY_INTERFACE_DOWN); + } +} + +int nhrp_interface_add(int cmd, struct zclient *client, zebra_size_t length, vrf_id_t vrf_id) +{ + struct interface *ifp; + + /* read and add the interface in the iflist. */ + ifp = zebra_interface_add_read(client->ibuf, vrf_id); + if (ifp == NULL) + return 0; + + debugf(NHRP_DEBUG_IF, "if-add: %s, ifindex: %u, hw_type: %d %s", + ifp->name, ifp->ifindex, + ifp->ll_type, if_link_type_str(ifp->ll_type)); + + nhrp_interface_update_nbma(ifp); + + return 0; +} + +int nhrp_interface_delete(int cmd, struct zclient *client, + zebra_size_t length, vrf_id_t vrf_id) +{ + struct interface *ifp; + struct stream *s; + + s = client->ibuf; + ifp = zebra_interface_state_read(s, vrf_id); + if (ifp == NULL) + return 0; + + debugf(NHRP_DEBUG_IF, "if-delete: %s", ifp->name); + ifp->ifindex = IFINDEX_INTERNAL; + nhrp_interface_update(ifp); + /* if_delete(ifp); */ + return 0; +} + +int nhrp_interface_up(int cmd, struct zclient *client, + zebra_size_t length, vrf_id_t vrf_id) +{ + struct interface *ifp; + + ifp = zebra_interface_state_read(client->ibuf, vrf_id); + if (ifp == NULL) + return 0; + + debugf(NHRP_DEBUG_IF, "if-up: %s", ifp->name); + nhrp_interface_update_nbma(ifp); + + return 0; +} + +int nhrp_interface_down(int cmd, struct zclient *client, + zebra_size_t length, vrf_id_t vrf_id) +{ + struct interface *ifp; + + ifp = zebra_interface_state_read(client->ibuf, vrf_id); + if (ifp == NULL) + return 0; + + debugf(NHRP_DEBUG_IF, "if-down: %s", ifp->name); + nhrp_interface_update(ifp); + return 0; +} + +int nhrp_interface_address_add(int cmd, struct zclient *client, + zebra_size_t length, vrf_id_t vrf_id) +{ + struct connected *ifc; + char buf[PREFIX_STRLEN]; + + ifc = zebra_interface_address_read(cmd, client->ibuf, vrf_id); + if (ifc == NULL) + return 0; + + debugf(NHRP_DEBUG_IF, "if-addr-add: %s: %s", + ifc->ifp->name, + prefix2str(ifc->address, buf, sizeof buf)); + + nhrp_interface_update_address(ifc->ifp, family2afi(PREFIX_FAMILY(ifc->address)), 0); + + return 0; +} + +int nhrp_interface_address_delete(int cmd, struct zclient *client, + zebra_size_t length, vrf_id_t vrf_id) +{ + struct connected *ifc; + char buf[PREFIX_STRLEN]; + + ifc = zebra_interface_address_read(cmd, client->ibuf, vrf_id); + if (ifc == NULL) + return 0; + + debugf(NHRP_DEBUG_IF, "if-addr-del: %s: %s", + ifc->ifp->name, + prefix2str(ifc->address, buf, sizeof buf)); + + nhrp_interface_update_address(ifc->ifp, family2afi(PREFIX_FAMILY(ifc->address)), 0); + connected_free(ifc); + + return 0; +} + +void nhrp_interface_notify_add(struct interface *ifp, struct notifier_block *n, notifier_fn_t fn) +{ + struct nhrp_interface *nifp = ifp->info; + notifier_add(n, &nifp->notifier_list, fn); +} + +void nhrp_interface_notify_del(struct interface *ifp, struct notifier_block *n) +{ + notifier_del(n); +} + +void nhrp_interface_set_protection(struct interface *ifp, const char *profile, const char *fallback_profile) +{ + struct nhrp_interface *nifp = ifp->info; + + if (nifp->ipsec_profile) free(nifp->ipsec_profile); + nifp->ipsec_profile = profile ? strdup(profile) : NULL; + + if (nifp->ipsec_fallback_profile) free(nifp->ipsec_fallback_profile); + nifp->ipsec_fallback_profile = fallback_profile ? strdup(fallback_profile) : NULL; + + notifier_call(&nifp->notifier_list, NOTIFY_INTERFACE_ADDRESS_CHANGED); +} + +void nhrp_interface_set_source(struct interface *ifp, const char *ifname) +{ + struct nhrp_interface *nifp = ifp->info; + + if (nifp->source) free(nifp->source); + nifp->source = ifname ? strdup(ifname) : NULL; + + nhrp_interface_update_nbma(ifp); +} diff --git a/nhrpd/nhrp_main.c b/nhrpd/nhrp_main.c new file mode 100644 index 000000000..56ea2713a --- /dev/null +++ b/nhrpd/nhrp_main.c @@ -0,0 +1,247 @@ +/* NHRP daemon main functions + * Copyright (c) 2014-2015 Timo Teräs + * + * This file is free software: you may copy, redistribute 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. + */ + +#include +#include + +#include "zebra.h" +#include "privs.h" +#include "getopt.h" +#include "thread.h" +#include "sigevent.h" +#include "version.h" +#include "log.h" +#include "memory.h" +#include "command.h" + +#include "nhrpd.h" +#include "netlink.h" + +unsigned int debug_flags = 0; + +struct thread_master *master; +struct timeval current_time; +static const char *pid_file = PATH_NHRPD_PID; +static char config_default[] = SYSCONFDIR NHRP_DEFAULT_CONFIG; +static char *config_file = NULL; +static char *vty_addr = NULL; +static int vty_port = NHRP_VTY_PORT; +static int do_daemonise = 0; + +/* nhrpd options. */ +struct option longopts[] = { + { "daemon", no_argument, NULL, 'd'}, + { "config_file", required_argument, NULL, 'f'}, + { "pid_file", required_argument, NULL, 'i'}, + { "socket", required_argument, NULL, 'z'}, + { "help", no_argument, NULL, 'h'}, + { "vty_addr", required_argument, NULL, 'A'}, + { "vty_port", required_argument, NULL, 'P'}, + { "user", required_argument, NULL, 'u'}, + { "group", required_argument, NULL, 'g'}, + { "version", no_argument, NULL, 'v'}, + { 0 } +}; + +/* nhrpd privileges */ +static zebra_capabilities_t _caps_p [] = { + ZCAP_NET_RAW, + ZCAP_NET_ADMIN, + ZCAP_DAC_OVERRIDE, /* for now needed to write to /proc/sys/net/ipv4//send_redirect */ +}; + +static struct zebra_privs_t nhrpd_privs = { +#ifdef QUAGGA_USER + .user = QUAGGA_USER, +#endif +#ifdef QUAGGA_GROUP + .group = QUAGGA_GROUP, +#endif +#ifdef VTY_GROUP + .vty_group = VTY_GROUP, +#endif + .caps_p = _caps_p, + .cap_num_p = ZEBRA_NUM_OF(_caps_p), +}; + +static void usage(const char *progname, int status) +{ + if (status != 0) + fprintf(stderr, "Try `%s --help' for more information.\n", progname); + else + printf( +"Usage : %s [OPTION...]\n\ +Daemon which manages NHRP protocol.\n\n\ +-d, --daemon Runs 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\ +-u, --user User to run as\n\ +-g, --group Group to run as\n\ +-v, --version Print program version\n\ +-h, --help Display this help and exit\n\ +\n\ +Report bugs to %s\n", progname, ZEBRA_BUG_ADDRESS); + + exit(status); +} + +static void parse_arguments(const char *progname, int argc, char **argv) +{ + int opt; + + while (1) { + opt = getopt_long(argc, argv, "df:i:z:hA:P:u:g:v", longopts, 0); + if(opt < 0) break; + + switch (opt) { + case 0: + break; + case 'd': + do_daemonise = -1; + break; + case 'f': + config_file = optarg; + break; + case 'i': + pid_file = optarg; + break; + case 'z': + zclient_serv_path_set(optarg); + break; + case 'A': + vty_addr = optarg; + break; + case 'P': + vty_port = atoi (optarg); + if (vty_port <= 0 || vty_port > 0xffff) + vty_port = NHRP_VTY_PORT; + break; + case 'u': + nhrpd_privs.user = optarg; + break; + case 'g': + nhrpd_privs.group = optarg; + break; + case 'v': + print_version(progname); + exit(0); + break; + case 'h': + usage(progname, 0); + break; + default: + usage(progname, 1); + break; + } + } +} + +static void nhrp_sigusr1(void) +{ + zlog_rotate(NULL); +} + +static void nhrp_request_stop(void) +{ + debugf(NHRP_DEBUG_COMMON, "Exiting..."); + + nhrp_shortcut_terminate(); + nhrp_nhs_terminate(); + nhrp_zebra_terminate(); + vici_terminate(); + evmgr_terminate(); + nhrp_vc_terminate(); + vrf_terminate(); + /* memory_terminate(); */ + /* vty_terminate(); */ + cmd_terminate(); + /* signal_terminate(); */ + zprivs_terminate(&nhrpd_privs); + + debugf(NHRP_DEBUG_COMMON, "Remove pid file."); + if (pid_file) unlink(pid_file); + debugf(NHRP_DEBUG_COMMON, "Done."); + + closezlog(zlog_default); + + exit(0); +} + +static struct quagga_signal_t sighandlers[] = { + { .signal = SIGUSR1, .handler = &nhrp_sigusr1, }, + { .signal = SIGINT, .handler = &nhrp_request_stop, }, + { .signal = SIGTERM, .handler = &nhrp_request_stop, }, +}; + +int main(int argc, char **argv) +{ + struct thread thread; + const char *progname; + + /* Set umask before anything for security */ + umask(0027); + progname = basename(argv[0]); + zlog_default = openzlog(progname, ZLOG_NHRP, LOG_CONS|LOG_NDELAY|LOG_PID, LOG_DAEMON); + zlog_set_level(NULL, ZLOG_DEST_STDOUT, LOG_WARNING); + + parse_arguments(progname, argc, argv); + + /* Library inits. */ + master = thread_master_create(); + zprivs_init(&nhrpd_privs); + signal_init(master, array_size(sighandlers), sighandlers); + cmd_init(1); + vty_init(master); + memory_init(); + nhrp_interface_init(); + vrf_init(); + resolver_init(); + + /* Run with elevated capabilities, as for all netlink activity + * we need privileges anyway. */ + nhrpd_privs.change(ZPRIVS_RAISE); + + netlink_init(); + evmgr_init(); + nhrp_vc_init(); + nhrp_packet_init(); + vici_init(); + nhrp_zebra_init(); + nhrp_shortcut_init(); + + nhrp_config_init(); + + /* Get zebra configuration file. */ + zlog_set_level(NULL, ZLOG_DEST_STDOUT, do_daemonise ? ZLOG_DISABLED : LOG_DEBUG); + vty_read_config(config_file, config_default); + + if (do_daemonise && daemon(0, 0) < 0) { + zlog_err("daemonise: %s", safe_strerror(errno)); + exit (1); + } + + /* write pid file */ + if (pid_output(pid_file) < 0) { + zlog_err("error while writing pidfile"); + exit (1); + } + + /* Create VTY socket */ + vty_serv_sock(vty_addr, vty_port, NHRP_VTYSH_PATH); + zlog_notice("nhrpd starting: vty@%d", vty_port); + + /* Main loop */ + while (thread_fetch(master, &thread)) + thread_call(&thread); + + return 0; +} diff --git a/nhrpd/nhrp_nhs.c b/nhrpd/nhrp_nhs.c new file mode 100644 index 000000000..d77ec0b80 --- /dev/null +++ b/nhrpd/nhrp_nhs.c @@ -0,0 +1,371 @@ +/* NHRP NHC nexthop server functions (registration) + * Copyright (c) 2014-2015 Timo Teräs + * + * This file is free software: you may copy, redistribute 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. + */ + +#include "zebra.h" +#include "zbuf.h" +#include "memory.h" +#include "thread.h" +#include "nhrpd.h" +#include "nhrp_protocol.h" + +static int nhrp_nhs_resolve(struct thread *t); +static int nhrp_reg_send_req(struct thread *t); + +static void nhrp_reg_reply(struct nhrp_reqid *reqid, void *arg) +{ + struct nhrp_packet_parser *p = arg; + struct nhrp_registration *r = container_of(reqid, struct nhrp_registration, reqid); + struct nhrp_nhs *nhs = r->nhs; + struct interface *ifp = nhs->ifp; + struct nhrp_interface *nifp = ifp->info; + struct nhrp_extension_header *ext; + struct nhrp_cie_header *cie; + struct nhrp_cache *c; + struct zbuf extpl; + union sockunion cie_nbma, cie_proto, *proto; + char buf[64]; + int ok = 0, holdtime; + + nhrp_reqid_free(&nhrp_packet_reqid, &r->reqid); + + if (p->hdr->type != NHRP_PACKET_REGISTRATION_REPLY) { + debugf(NHRP_DEBUG_COMMON, "NHS: Registration failed"); + return; + } + + debugf(NHRP_DEBUG_COMMON, "NHS: Reg.reply received"); + + ok = 1; + while ((cie = nhrp_cie_pull(&p->payload, p->hdr, &cie_nbma, &cie_proto)) != NULL) { + proto = sockunion_family(&cie_proto) != AF_UNSPEC ? &cie_proto : &p->src_proto; + debugf(NHRP_DEBUG_COMMON, "NHS: CIE registration: %s: %d", + sockunion2str(proto, buf, sizeof(buf)), + cie->code); + if (!((cie->code == NHRP_CODE_SUCCESS) || + (cie->code == NHRP_CODE_ADMINISTRATIVELY_PROHIBITED && nhs->hub))) + ok = 0; + } + + if (!ok) + return; + + /* Parse extensions */ + sockunion_family(&nifp->nat_nbma) = AF_UNSPEC; + while ((ext = nhrp_ext_pull(&p->extensions, &extpl)) != NULL) { + switch (htons(ext->type) & ~NHRP_EXTENSION_FLAG_COMPULSORY) { + case NHRP_EXTENSION_NAT_ADDRESS: + /* NHS adds second CIE if NAT is detected */ + if (nhrp_cie_pull(&extpl, p->hdr, &cie_nbma, &cie_proto) && + nhrp_cie_pull(&extpl, p->hdr, &cie_nbma, &cie_proto)) { + nifp->nat_nbma = cie_nbma; + debugf(NHRP_DEBUG_IF, "%s: NAT detected, real NBMA address: %s", + ifp->name, sockunion2str(&nifp->nbma, buf, sizeof(buf))); + } + break; + } + } + + /* Success - schedule next registration, and route NHS */ + r->timeout = 2; + holdtime = nifp->afi[nhs->afi].holdtime; + THREAD_OFF(r->t_register); + + /* RFC 2332 5.2.3 - Registration is recommend to be renewed + * every one third of holdtime */ + THREAD_TIMER_ON(master, r->t_register, nhrp_reg_send_req, r, holdtime / 3); + + r->proto_addr = p->dst_proto; + c = nhrp_cache_get(ifp, &p->dst_proto, 1); + if (c) nhrp_cache_update_binding(c, NHRP_CACHE_NHS, holdtime, nhrp_peer_ref(r->peer), 0, NULL); +} + +static int nhrp_reg_timeout(struct thread *t) +{ + struct nhrp_registration *r = THREAD_ARG(t); + struct nhrp_cache *c; + + r->t_register = NULL; + + if (r->timeout >= 16 && sockunion_family(&r->proto_addr) != AF_UNSPEC) { + nhrp_reqid_free(&nhrp_packet_reqid, &r->reqid); + c = nhrp_cache_get(r->nhs->ifp, &r->proto_addr, 0); + if (c) nhrp_cache_update_binding(c, NHRP_CACHE_NHS, -1, NULL, 0, NULL); + sockunion_family(&r->proto_addr) = AF_UNSPEC; + } + + r->timeout <<= 1; + if (r->timeout > 64) r->timeout = 2; + THREAD_TIMER_MSEC_ON(master, r->t_register, nhrp_reg_send_req, r, 10); + + return 0; +} + +static void nhrp_reg_peer_notify(struct notifier_block *n, unsigned long cmd) +{ + struct nhrp_registration *r = container_of(n, struct nhrp_registration, peer_notifier); + char buf[SU_ADDRSTRLEN]; + + switch (cmd) { + case NOTIFY_PEER_UP: + case NOTIFY_PEER_DOWN: + case NOTIFY_PEER_IFCONFIG_CHANGED: + case NOTIFY_PEER_MTU_CHANGED: + debugf(NHRP_DEBUG_COMMON, "NHS: Flush timer for %s", + sockunion2str(&r->peer->vc->remote.nbma, buf, sizeof buf)); + THREAD_TIMER_OFF(r->t_register); + THREAD_TIMER_MSEC_ON(master, r->t_register, nhrp_reg_send_req, r, 10); + break; + } +} + +static int nhrp_reg_send_req(struct thread *t) +{ + struct nhrp_registration *r = THREAD_ARG(t); + struct nhrp_nhs *nhs = r->nhs; + char buf1[SU_ADDRSTRLEN], buf2[SU_ADDRSTRLEN]; + struct interface *ifp = nhs->ifp; + struct nhrp_interface *nifp = ifp->info; + struct nhrp_afi_data *if_ad = &nifp->afi[nhs->afi]; + union sockunion *dst_proto; + struct zbuf *zb; + struct nhrp_packet_header *hdr; + struct nhrp_extension_header *ext; + struct nhrp_cie_header *cie; + + r->t_register = NULL; + if (!nhrp_peer_check(r->peer, 2)) { + debugf(NHRP_DEBUG_COMMON, "NHS: Waiting link for %s", + sockunion2str(&r->peer->vc->remote.nbma, buf1, sizeof buf1)); + THREAD_TIMER_ON(master, r->t_register, nhrp_reg_send_req, r, 120); + return 0; + } + + THREAD_TIMER_ON(master, r->t_register, nhrp_reg_timeout, r, r->timeout); + + /* RFC2332 5.2.3 NHC uses it's own address as dst if NHS is unknown */ + dst_proto = &nhs->proto_addr; + if (sockunion_family(dst_proto) == AF_UNSPEC) + dst_proto = &if_ad->addr; + + sockunion2str(&if_ad->addr, buf1, sizeof(buf1)); + sockunion2str(dst_proto, buf2, sizeof(buf2)); + debugf(NHRP_DEBUG_COMMON, "NHS: Register %s -> %s (timeout %d)", buf1, buf2, r->timeout); + + /* No protocol address configured for tunnel interface */ + if (sockunion_family(&if_ad->addr) == AF_UNSPEC) + return 0; + + zb = zbuf_alloc(1400); + hdr = nhrp_packet_push(zb, NHRP_PACKET_REGISTRATION_REQUEST, &nifp->nbma, &if_ad->addr, dst_proto); + hdr->hop_count = 1; + if (!(if_ad->flags & NHRP_IFF_REG_NO_UNIQUE)) + hdr->flags |= htons(NHRP_FLAG_REGISTRATION_UNIQUE); + + hdr->u.request_id = htonl(nhrp_reqid_alloc(&nhrp_packet_reqid, &r->reqid, nhrp_reg_reply)); + + /* FIXME: push CIE for each local protocol address */ + cie = nhrp_cie_push(zb, NHRP_CODE_SUCCESS, NULL, NULL); + cie->prefix_length = 0xff; + cie->holding_time = htons(if_ad->holdtime); + cie->mtu = htons(if_ad->mtu); + + nhrp_ext_request(zb, hdr, ifp); + + /* Cisco NAT detection extension */ + hdr->flags |= htons(NHRP_FLAG_REGISTRATION_NAT); + ext = nhrp_ext_push(zb, hdr, NHRP_EXTENSION_NAT_ADDRESS); + cie = nhrp_cie_push(zb, NHRP_CODE_SUCCESS, &nifp->nbma, &if_ad->addr); + cie->prefix_length = 8 * sockunion_get_addrlen(&if_ad->addr); + nhrp_ext_complete(zb, ext); + + nhrp_packet_complete(zb, hdr); + nhrp_peer_send(r->peer, zb); + zbuf_free(zb); + + return 0; +} + +static void nhrp_reg_delete(struct nhrp_registration *r) +{ + nhrp_peer_notify_del(r->peer, &r->peer_notifier); + nhrp_peer_unref(r->peer); + list_del(&r->reglist_entry); + THREAD_OFF(r->t_register); + XFREE(MTYPE_NHRP_REGISTRATION, r); +} + +static struct nhrp_registration *nhrp_reg_by_nbma(struct nhrp_nhs *nhs, const union sockunion *nbma_addr) +{ + struct nhrp_registration *r; + + list_for_each_entry(r, &nhs->reglist_head, reglist_entry) + if (sockunion_same(&r->peer->vc->remote.nbma, nbma_addr)) + return r; + return NULL; +} + +static void nhrp_nhs_resolve_cb(struct resolver_query *q, int n, union sockunion *addrs) +{ + struct nhrp_nhs *nhs = container_of(q, struct nhrp_nhs, dns_resolve); + struct nhrp_interface *nifp = nhs->ifp->info; + struct nhrp_registration *reg, *regn; + int i; + + nhs->t_resolve = NULL; + if (n < 0) { + /* Failed, retry in a moment */ + THREAD_TIMER_ON(master, nhs->t_resolve, nhrp_nhs_resolve, nhs, 5); + return; + } + + THREAD_TIMER_ON(master, nhs->t_resolve, nhrp_nhs_resolve, nhs, 2*60*60); + + list_for_each_entry(reg, &nhs->reglist_head, reglist_entry) + reg->mark = 1; + + nhs->hub = 0; + for (i = 0; i < n; i++) { + if (sockunion_same(&addrs[i], &nifp->nbma)) { + nhs->hub = 1; + continue; + } + + reg = nhrp_reg_by_nbma(nhs, &addrs[i]); + if (reg) { + reg->mark = 0; + continue; + } + + reg = XCALLOC(MTYPE_NHRP_REGISTRATION, sizeof(*reg)); + reg->peer = nhrp_peer_get(nhs->ifp, &addrs[i]); + reg->nhs = nhs; + reg->timeout = 1; + list_init(®->reglist_entry); + list_add_tail(®->reglist_entry, &nhs->reglist_head); + nhrp_peer_notify_add(reg->peer, ®->peer_notifier, nhrp_reg_peer_notify); + THREAD_TIMER_MSEC_ON(master, reg->t_register, nhrp_reg_send_req, reg, 50); + } + + list_for_each_entry_safe(reg, regn, &nhs->reglist_head, reglist_entry) { + if (reg->mark) + nhrp_reg_delete(reg); + } +} + +static int nhrp_nhs_resolve(struct thread *t) +{ + struct nhrp_nhs *nhs = THREAD_ARG(t); + + resolver_resolve(&nhs->dns_resolve, AF_INET, nhs->nbma_fqdn, nhrp_nhs_resolve_cb); + + return 0; +} + +int nhrp_nhs_add(struct interface *ifp, afi_t afi, union sockunion *proto_addr, const char *nbma_fqdn) +{ + struct nhrp_interface *nifp = ifp->info; + struct nhrp_nhs *nhs; + + if (sockunion_family(proto_addr) != AF_UNSPEC && + sockunion_family(proto_addr) != afi2family(afi)) + return NHRP_ERR_PROTOCOL_ADDRESS_MISMATCH; + + list_for_each_entry(nhs, &nifp->afi[afi].nhslist_head, nhslist_entry) { + if (sockunion_family(&nhs->proto_addr) != AF_UNSPEC && + sockunion_family(proto_addr) != AF_UNSPEC && + sockunion_same(&nhs->proto_addr, proto_addr)) + return NHRP_ERR_ENTRY_EXISTS; + + if (strcmp(nhs->nbma_fqdn, nbma_fqdn) == 0) + return NHRP_ERR_ENTRY_EXISTS; + } + + nhs = XMALLOC(MTYPE_NHRP_NHS, sizeof(struct nhrp_nhs)); + if (!nhs) return NHRP_ERR_NO_MEMORY; + + *nhs = (struct nhrp_nhs) { + .afi = afi, + .ifp = ifp, + .proto_addr = *proto_addr, + .nbma_fqdn = strdup(nbma_fqdn), + .reglist_head = LIST_INITIALIZER(nhs->reglist_head), + }; + list_add_tail(&nhs->nhslist_entry, &nifp->afi[afi].nhslist_head); + THREAD_TIMER_MSEC_ON(master, nhs->t_resolve, nhrp_nhs_resolve, nhs, 1000); + + return NHRP_OK; +} + +int nhrp_nhs_del(struct interface *ifp, afi_t afi, union sockunion *proto_addr, const char *nbma_fqdn) +{ + struct nhrp_interface *nifp = ifp->info; + struct nhrp_nhs *nhs, *nnhs; + int ret = NHRP_ERR_ENTRY_NOT_FOUND; + + if (sockunion_family(proto_addr) != AF_UNSPEC && + sockunion_family(proto_addr) != afi2family(afi)) + return NHRP_ERR_PROTOCOL_ADDRESS_MISMATCH; + + list_for_each_entry_safe(nhs, nnhs, &nifp->afi[afi].nhslist_head, nhslist_entry) { + if (!sockunion_same(&nhs->proto_addr, proto_addr)) + continue; + if (strcmp(nhs->nbma_fqdn, nbma_fqdn) != 0) + continue; + + nhrp_nhs_free(nhs); + ret = NHRP_OK; + } + + return ret; +} + +int nhrp_nhs_free(struct nhrp_nhs *nhs) +{ + struct nhrp_registration *r, *rn; + + list_for_each_entry_safe(r, rn, &nhs->reglist_head, reglist_entry) + nhrp_reg_delete(r); + THREAD_OFF(nhs->t_resolve); + list_del(&nhs->nhslist_entry); + free((void*) nhs->nbma_fqdn); + XFREE(MTYPE_NHRP_NHS, nhs); + return 0; +} + +void nhrp_nhs_terminate(void) +{ + struct interface *ifp; + struct nhrp_interface *nifp; + struct nhrp_nhs *nhs, *tmp; + struct listnode *node; + afi_t afi; + + for (ALL_LIST_ELEMENTS_RO(iflist, node, ifp)) { + nifp = ifp->info; + for (afi = 0; afi < AFI_MAX; afi++) { + list_for_each_entry_safe(nhs, tmp, &nifp->afi[afi].nhslist_head, nhslist_entry) + nhrp_nhs_free(nhs); + } + } +} + +void nhrp_nhs_foreach(struct interface *ifp, afi_t afi, void (*cb)(struct nhrp_nhs *, struct nhrp_registration *, void *), void *ctx) +{ + struct nhrp_interface *nifp = ifp->info; + struct nhrp_nhs *nhs; + struct nhrp_registration *reg; + + list_for_each_entry(nhs, &nifp->afi[afi].nhslist_head, nhslist_entry) { + if (!list_empty(&nhs->reglist_head)) { + list_for_each_entry(reg, &nhs->reglist_head, reglist_entry) + cb(nhs, reg, ctx); + } else + cb(nhs, 0, ctx); + } +} diff --git a/nhrpd/nhrp_packet.c b/nhrpd/nhrp_packet.c new file mode 100644 index 000000000..5d2866a67 --- /dev/null +++ b/nhrpd/nhrp_packet.c @@ -0,0 +1,312 @@ +/* NHRP packet handling functions + * Copyright (c) 2014-2015 Timo Teräs + * + * This file is free software: you may copy, redistribute 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. + */ + +#include +#include "nhrpd.h" +#include "zbuf.h" +#include "thread.h" +#include "hash.h" + +#include "nhrp_protocol.h" +#include "os.h" + +struct nhrp_reqid_pool nhrp_packet_reqid; + +static uint16_t family2proto(int family) +{ + switch (family) { + case AF_INET: return ETH_P_IP; + case AF_INET6: return ETH_P_IPV6; + } + return 0; +} + +static int proto2family(uint16_t proto) +{ + switch (proto) { + case ETH_P_IP: return AF_INET; + case ETH_P_IPV6: return AF_INET6; + } + return AF_UNSPEC; +} + +struct nhrp_packet_header *nhrp_packet_push( + struct zbuf *zb, uint8_t type, + const union sockunion *src_nbma, + const union sockunion *src_proto, + const union sockunion *dst_proto) +{ + struct nhrp_packet_header *hdr; + + hdr = zbuf_push(zb, struct nhrp_packet_header); + if (!hdr) return NULL; + + *hdr = (struct nhrp_packet_header) { + .afnum = htons(family2afi(sockunion_family(src_nbma))), + .protocol_type = htons(family2proto(sockunion_family(src_proto))), + .version = NHRP_VERSION_RFC2332, + .type = type, + .hop_count = 64, + .src_nbma_address_len = sockunion_get_addrlen(src_nbma), + .src_protocol_address_len = sockunion_get_addrlen(src_proto), + .dst_protocol_address_len = sockunion_get_addrlen(dst_proto), + }; + + zbuf_put(zb, sockunion_get_addr(src_nbma), hdr->src_nbma_address_len); + zbuf_put(zb, sockunion_get_addr(src_proto), hdr->src_protocol_address_len); + zbuf_put(zb, sockunion_get_addr(dst_proto), hdr->dst_protocol_address_len); + + return hdr; +} + +struct nhrp_packet_header *nhrp_packet_pull( + struct zbuf *zb, + union sockunion *src_nbma, + union sockunion *src_proto, + union sockunion *dst_proto) +{ + struct nhrp_packet_header *hdr; + + hdr = zbuf_pull(zb, struct nhrp_packet_header); + if (!hdr) return NULL; + + sockunion_set( + src_nbma, afi2family(htons(hdr->afnum)), + zbuf_pulln(zb, hdr->src_nbma_address_len + hdr->src_nbma_subaddress_len), + hdr->src_nbma_address_len + hdr->src_nbma_subaddress_len); + sockunion_set( + src_proto, proto2family(htons(hdr->protocol_type)), + zbuf_pulln(zb, hdr->src_protocol_address_len), + hdr->src_protocol_address_len); + sockunion_set( + dst_proto, proto2family(htons(hdr->protocol_type)), + zbuf_pulln(zb, hdr->dst_protocol_address_len), + hdr->dst_protocol_address_len); + + return hdr; +} + +uint16_t nhrp_packet_calculate_checksum(const uint8_t *pdu, uint16_t len) +{ + const uint16_t *pdu16 = (const uint16_t *) pdu; + uint32_t csum = 0; + int i; + + for (i = 0; i < len / 2; i++) + csum += pdu16[i]; + if (len & 1) + csum += htons(pdu[len - 1]); + + while (csum & 0xffff0000) + csum = (csum & 0xffff) + (csum >> 16); + + return (~csum) & 0xffff; +} + +void nhrp_packet_complete(struct zbuf *zb, struct nhrp_packet_header *hdr) +{ + unsigned short size; + + if (hdr->extension_offset) + nhrp_ext_push(zb, hdr, NHRP_EXTENSION_END | NHRP_EXTENSION_FLAG_COMPULSORY); + + size = zb->tail - (uint8_t *)hdr; + hdr->packet_size = htons(size); + hdr->checksum = 0; + hdr->checksum = nhrp_packet_calculate_checksum((uint8_t *) hdr, size); +} + +struct nhrp_cie_header *nhrp_cie_push( + struct zbuf *zb, + uint8_t code, + const union sockunion *nbma, + const union sockunion *proto) +{ + struct nhrp_cie_header *cie; + + cie = zbuf_push(zb, struct nhrp_cie_header); + *cie = (struct nhrp_cie_header) { + .code = code, + }; + if (nbma) { + cie->nbma_address_len = sockunion_get_addrlen(nbma); + zbuf_put(zb, sockunion_get_addr(nbma), cie->nbma_address_len); + } + if (proto) { + cie->protocol_address_len = sockunion_get_addrlen(proto); + zbuf_put(zb, sockunion_get_addr(proto), cie->protocol_address_len); + } + + return cie; +} + +struct nhrp_cie_header *nhrp_cie_pull( + struct zbuf *zb, + struct nhrp_packet_header *hdr, + union sockunion *nbma, + union sockunion *proto) +{ + struct nhrp_cie_header *cie; + + cie = zbuf_pull(zb, struct nhrp_cie_header); + if (!cie) return NULL; + + if (cie->nbma_address_len + cie->nbma_subaddress_len) { + sockunion_set( + nbma, afi2family(htons(hdr->afnum)), + zbuf_pulln(zb, cie->nbma_address_len + cie->nbma_subaddress_len), + cie->nbma_address_len + cie->nbma_subaddress_len); + } else { + sockunion_family(nbma) = AF_UNSPEC; + } + + if (cie->protocol_address_len) { + sockunion_set( + proto, proto2family(htons(hdr->protocol_type)), + zbuf_pulln(zb, cie->protocol_address_len), + cie->protocol_address_len); + } else { + sockunion_family(proto) = AF_UNSPEC; + } + + return cie; +} + +struct nhrp_extension_header *nhrp_ext_push(struct zbuf *zb, struct nhrp_packet_header *hdr, uint16_t type) +{ + struct nhrp_extension_header *ext; + ext = zbuf_push(zb, struct nhrp_extension_header); + if (!ext) return NULL; + + if (!hdr->extension_offset) + hdr->extension_offset = htons(zb->tail - (uint8_t*) hdr - sizeof(struct nhrp_extension_header)); + + *ext = (struct nhrp_extension_header) { + .type = htons(type), + .length = 0, + }; + return ext; +} + +void nhrp_ext_complete(struct zbuf *zb, struct nhrp_extension_header *ext) +{ + ext->length = htons(zb->tail - (uint8_t*)ext - sizeof(struct nhrp_extension_header)); +} + +struct nhrp_extension_header *nhrp_ext_pull(struct zbuf *zb, struct zbuf *payload) +{ + struct nhrp_extension_header *ext; + uint16_t plen; + + ext = zbuf_pull(zb, struct nhrp_extension_header); + if (!ext) return NULL; + + plen = htons(ext->length); + zbuf_init(payload, zbuf_pulln(zb, plen), plen, plen); + return ext; +} + +void nhrp_ext_request(struct zbuf *zb, struct nhrp_packet_header *hdr, struct interface *ifp) +{ + /* Place holders for standard extensions */ + nhrp_ext_push(zb, hdr, NHRP_EXTENSION_FORWARD_TRANSIT_NHS | NHRP_EXTENSION_FLAG_COMPULSORY); + nhrp_ext_push(zb, hdr, NHRP_EXTENSION_REVERSE_TRANSIT_NHS | NHRP_EXTENSION_FLAG_COMPULSORY); + nhrp_ext_push(zb, hdr, NHRP_EXTENSION_RESPONDER_ADDRESS | NHRP_EXTENSION_FLAG_COMPULSORY); +} + +int nhrp_ext_reply(struct zbuf *zb, struct nhrp_packet_header *hdr, struct interface *ifp, struct nhrp_extension_header *ext, struct zbuf *extpayload) +{ + struct nhrp_interface *nifp = ifp->info; + struct nhrp_afi_data *ad = &nifp->afi[htons(hdr->afnum)]; + struct nhrp_extension_header *dst; + struct nhrp_cie_header *cie; + uint16_t type; + + type = htons(ext->type) & ~NHRP_EXTENSION_FLAG_COMPULSORY; + if (type == NHRP_EXTENSION_END) + return 0; + + dst = nhrp_ext_push(zb, hdr, htons(ext->type)); + if (!dst) goto err; + + switch (type) { + case NHRP_EXTENSION_RESPONDER_ADDRESS: + cie = nhrp_cie_push(zb, NHRP_CODE_SUCCESS, &nifp->nbma, &ad->addr); + if (!cie) goto err; + cie->holding_time = htons(ad->holdtime); + break; + default: + if (type & NHRP_EXTENSION_FLAG_COMPULSORY) + goto err; + case NHRP_EXTENSION_FORWARD_TRANSIT_NHS: + case NHRP_EXTENSION_REVERSE_TRANSIT_NHS: + /* Supported compulsory extensions, and any + * non-compulsory that is not explicitly handled, + * should be just copied. */ + zbuf_copy(zb, extpayload, zbuf_used(extpayload)); + break; + } + nhrp_ext_complete(zb, dst); + return 0; +err: + zbuf_set_werror(zb); + return -1; +} + +static int nhrp_packet_recvraw(struct thread *t) +{ + int fd = THREAD_FD(t), ifindex; + struct zbuf *zb; + struct interface *ifp; + struct nhrp_peer *p; + union sockunion remote_nbma; + uint8_t addr[64]; + size_t len, addrlen; + + thread_add_read(master, nhrp_packet_recvraw, 0, fd); + + zb = zbuf_alloc(1500); + if (!zb) return 0; + + len = zbuf_size(zb); + addrlen = sizeof(addr); + if (os_recvmsg(zb->buf, &len, &ifindex, addr, &addrlen) < 0) + goto err; + + zb->head = zb->buf; + zb->tail = zb->buf + len; + + switch (addrlen) { + case 4: + sockunion_set(&remote_nbma, AF_INET, addr, addrlen); + break; + default: + goto err; + } + + ifp = if_lookup_by_index(ifindex); + if (!ifp) goto err; + + p = nhrp_peer_get(ifp, &remote_nbma); + if (!p) goto err; + + nhrp_peer_recv(p, zb); + nhrp_peer_unref(p); + return 0; + +err: + zbuf_free(zb); + return 0; +} + +int nhrp_packet_init(void) +{ + thread_add_read(master, nhrp_packet_recvraw, 0, os_socket()); + return 0; +} diff --git a/nhrpd/nhrp_peer.c b/nhrpd/nhrp_peer.c new file mode 100644 index 000000000..5095d55a6 --- /dev/null +++ b/nhrpd/nhrp_peer.c @@ -0,0 +1,877 @@ +/* NHRP peer functions + * Copyright (c) 2014-2015 Timo Teräs + * + * This file is free software: you may copy, redistribute 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. + */ + +#include + +#include "zebra.h" +#include "memory.h" +#include "thread.h" +#include "hash.h" + +#include "nhrpd.h" +#include "nhrp_protocol.h" +#include "os.h" + +struct ipv6hdr { + uint8_t priority_version; + uint8_t flow_lbl[3]; + uint16_t payload_len; + uint8_t nexthdr; + uint8_t hop_limit; + struct in6_addr saddr; + struct in6_addr daddr; +}; + +static void nhrp_packet_debug(struct zbuf *zb, const char *dir); + +static void nhrp_peer_check_delete(struct nhrp_peer *p) +{ + struct nhrp_interface *nifp = p->ifp->info; + + if (p->ref || notifier_active(&p->notifier_list)) + return; + + THREAD_OFF(p->t_fallback); + hash_release(nifp->peer_hash, p); + nhrp_interface_notify_del(p->ifp, &p->ifp_notifier); + nhrp_vc_notify_del(p->vc, &p->vc_notifier); + XFREE(MTYPE_NHRP_PEER, p); +} + +static int nhrp_peer_notify_up(struct thread *t) +{ + struct nhrp_peer *p = THREAD_ARG(t); + struct nhrp_vc *vc = p->vc; + struct interface *ifp = p->ifp; + struct nhrp_interface *nifp = ifp->info; + + p->t_fallback = NULL; + if (nifp->enabled && (!nifp->ipsec_profile || vc->ipsec)) { + p->online = 1; + nhrp_peer_ref(p); + notifier_call(&p->notifier_list, NOTIFY_PEER_UP); + nhrp_peer_unref(p); + } + + return 0; +} + +static void __nhrp_peer_check(struct nhrp_peer *p) +{ + struct nhrp_vc *vc = p->vc; + struct interface *ifp = p->ifp; + struct nhrp_interface *nifp = ifp->info; + unsigned online; + + online = nifp->enabled && (!nifp->ipsec_profile || vc->ipsec); + if (p->online != online) { + THREAD_OFF(p->t_fallback); + if (online && notifier_active(&p->notifier_list)) { + /* If we requested the IPsec connection, delay + * the up notification a bit to allow things + * settle down. This allows IKE to install + * SPDs and SAs. */ + THREAD_TIMER_MSEC_ON( + master, p->t_fallback, + nhrp_peer_notify_up, p, 50); + } else { + nhrp_peer_ref(p); + p->online = online; + if (online) { + notifier_call(&p->notifier_list, NOTIFY_PEER_UP); + } else { + p->requested = p->fallback_requested = 0; + notifier_call(&p->notifier_list, NOTIFY_PEER_DOWN); + } + nhrp_peer_unref(p); + } + } +} + +static void nhrp_peer_vc_notify(struct notifier_block *n, unsigned long cmd) +{ + struct nhrp_peer *p = container_of(n, struct nhrp_peer, vc_notifier); + + switch (cmd) { + case NOTIFY_VC_IPSEC_CHANGED: + __nhrp_peer_check(p); + break; + case NOTIFY_VC_IPSEC_UPDATE_NBMA: + nhrp_peer_ref(p); + notifier_call(&p->notifier_list, NOTIFY_PEER_NBMA_CHANGING); + nhrp_peer_unref(p); + break; + } +} + +static void nhrp_peer_ifp_notify(struct notifier_block *n, unsigned long cmd) +{ + struct nhrp_peer *p = container_of(n, struct nhrp_peer, ifp_notifier); + struct nhrp_interface *nifp; + struct nhrp_vc *vc; + + nhrp_peer_ref(p); + switch (cmd) { + case NOTIFY_INTERFACE_UP: + case NOTIFY_INTERFACE_DOWN: + __nhrp_peer_check(p); + break; + case NOTIFY_INTERFACE_NBMA_CHANGED: + /* Source NBMA changed, rebind to new VC */ + nifp = p->ifp->info; + vc = nhrp_vc_get(&nifp->nbma, &p->vc->remote.nbma, 1); + if (vc && p->vc != vc) { + nhrp_vc_notify_del(p->vc, &p->vc_notifier); + p->vc = vc; + nhrp_vc_notify_add(p->vc, &p->vc_notifier, nhrp_peer_vc_notify); + __nhrp_peer_check(p); + } + /* Fall-through to post config update */ + case NOTIFY_INTERFACE_ADDRESS_CHANGED: + notifier_call(&p->notifier_list, NOTIFY_PEER_IFCONFIG_CHANGED); + break; + case NOTIFY_INTERFACE_MTU_CHANGED: + notifier_call(&p->notifier_list, NOTIFY_PEER_MTU_CHANGED); + break; + } + nhrp_peer_unref(p); +} + +static unsigned int nhrp_peer_key(void *peer_data) +{ + struct nhrp_peer *p = peer_data; + return sockunion_hash(&p->vc->remote.nbma); +} + +static int nhrp_peer_cmp(const void *cache_data, const void *key_data) +{ + const struct nhrp_peer *a = cache_data; + const struct nhrp_peer *b = key_data; + return a->ifp == b->ifp && a->vc == b->vc; +} + +static void *nhrp_peer_create(void *data) +{ + struct nhrp_peer *p, *key = data; + + p = XMALLOC(MTYPE_NHRP_PEER, sizeof(*p)); + if (p) { + *p = (struct nhrp_peer) { + .ref = 0, + .ifp = key->ifp, + .vc = key->vc, + .notifier_list = NOTIFIER_LIST_INITIALIZER(&p->notifier_list), + }; + nhrp_vc_notify_add(p->vc, &p->vc_notifier, nhrp_peer_vc_notify); + nhrp_interface_notify_add(p->ifp, &p->ifp_notifier, nhrp_peer_ifp_notify); + } + return p; +} + +struct nhrp_peer *nhrp_peer_get(struct interface *ifp, const union sockunion *remote_nbma) +{ + struct nhrp_interface *nifp = ifp->info; + struct nhrp_peer key, *p; + struct nhrp_vc *vc; + + if (!nifp->peer_hash) { + nifp->peer_hash = hash_create(nhrp_peer_key, nhrp_peer_cmp); + if (!nifp->peer_hash) return NULL; + } + + vc = nhrp_vc_get(&nifp->nbma, remote_nbma, 1); + if (!vc) return NULL; + + key.ifp = ifp; + key.vc = vc; + + p = hash_get(nifp->peer_hash, &key, nhrp_peer_create); + nhrp_peer_ref(p); + if (p->ref == 1) __nhrp_peer_check(p); + + return p; +} + +struct nhrp_peer *nhrp_peer_ref(struct nhrp_peer *p) +{ + if (p) p->ref++; + return p; +} + +void nhrp_peer_unref(struct nhrp_peer *p) +{ + if (p) { + p->ref--; + nhrp_peer_check_delete(p); + } +} + +static int nhrp_peer_request_timeout(struct thread *t) +{ + struct nhrp_peer *p = THREAD_ARG(t); + struct nhrp_vc *vc = p->vc; + struct interface *ifp = p->ifp; + struct nhrp_interface *nifp = ifp->info; + + p->t_fallback = NULL; + + if (p->online) + return 0; + + if (nifp->ipsec_fallback_profile && !p->prio && !p->fallback_requested) { + p->fallback_requested = 1; + vici_request_vc(nifp->ipsec_fallback_profile, + &vc->local.nbma, &vc->remote.nbma, p->prio); + THREAD_TIMER_ON(master, p->t_fallback, nhrp_peer_request_timeout, p, 30); + } else { + p->requested = p->fallback_requested = 0; + } + + return 0; +} + +int nhrp_peer_check(struct nhrp_peer *p, int establish) +{ + struct nhrp_vc *vc = p->vc; + struct interface *ifp = p->ifp; + struct nhrp_interface *nifp = ifp->info; + + if (p->online) + return 1; + if (!establish) + return 0; + if (p->requested) + return 0; + if (!nifp->ipsec_profile) + return 0; + if (sockunion_family(&vc->local.nbma) == AF_UNSPEC) + return 0; + + p->prio = establish > 1; + p->requested = 1; + vici_request_vc(nifp->ipsec_profile, &vc->local.nbma, &vc->remote.nbma, p->prio); + THREAD_TIMER_ON(master, p->t_fallback, nhrp_peer_request_timeout, p, + (nifp->ipsec_fallback_profile && !p->prio) ? 15 : 30); + + return 0; +} + +void nhrp_peer_notify_add(struct nhrp_peer *p, struct notifier_block *n, notifier_fn_t fn) +{ + notifier_add(n, &p->notifier_list, fn); +} + +void nhrp_peer_notify_del(struct nhrp_peer *p, struct notifier_block *n) +{ + notifier_del(n); + nhrp_peer_check_delete(p); +} + +void nhrp_peer_send(struct nhrp_peer *p, struct zbuf *zb) +{ + char buf[2][256]; + + nhrp_packet_debug(zb, "Send"); + + if (!p->online) + return; + + debugf(NHRP_DEBUG_KERNEL, "PACKET: Send %s -> %s", + sockunion2str(&p->vc->local.nbma, buf[0], sizeof buf[0]), + sockunion2str(&p->vc->remote.nbma, buf[1], sizeof buf[1])); + + os_sendmsg(zb->head, zbuf_used(zb), + p->ifp->ifindex, + sockunion_get_addr(&p->vc->remote.nbma), + sockunion_get_addrlen(&p->vc->remote.nbma)); + zbuf_reset(zb); +} + +static void nhrp_handle_resolution_req(struct nhrp_packet_parser *p) +{ + struct zbuf *zb, payload; + struct nhrp_packet_header *hdr; + struct nhrp_cie_header *cie; + struct nhrp_extension_header *ext; + struct nhrp_interface *nifp; + struct nhrp_peer *peer; + + if (!(p->if_ad->flags & NHRP_IFF_SHORTCUT)) { + debugf(NHRP_DEBUG_COMMON, "Shortcuts disabled"); + /* FIXME: Send error indication? */ + return; + } + + if (p->if_ad->network_id && + p->route_type == NHRP_ROUTE_OFF_NBMA && + p->route_prefix.prefixlen < 8) { + debugf(NHRP_DEBUG_COMMON, "Shortcut to more generic than /8 dropped"); + return; + } + + debugf(NHRP_DEBUG_COMMON, "Parsing and replying to Resolution Req"); + + if (nhrp_route_address(p->ifp, &p->src_proto, NULL, &peer) != NHRP_ROUTE_NBMA_NEXTHOP) + return; + +#if 0 + /* FIXME: Update requestors binding if CIE specifies holding time */ + nhrp_cache_update_binding( + NHRP_CACHE_CACHED, &p->src_proto, + nhrp_peer_get(p->ifp, &p->src_nbma), + htons(cie->holding_time)); +#endif + + nifp = peer->ifp->info; + + /* Create reply */ + zb = zbuf_alloc(1500); + hdr = nhrp_packet_push(zb, NHRP_PACKET_RESOLUTION_REPLY, &p->src_nbma, &p->src_proto, &p->dst_proto); + + /* Copied information from request */ + hdr->flags = p->hdr->flags & htons(NHRP_FLAG_RESOLUTION_SOURCE_IS_ROUTER|NHRP_FLAG_RESOLUTION_SOURCE_STABLE); + hdr->flags |= htons(NHRP_FLAG_RESOLUTION_DESTINATION_STABLE | NHRP_FLAG_RESOLUTION_AUTHORATIVE); + hdr->u.request_id = p->hdr->u.request_id; + + /* CIE payload */ + cie = nhrp_cie_push(zb, NHRP_CODE_SUCCESS, &nifp->nbma, &p->if_ad->addr); + cie->holding_time = htons(p->if_ad->holdtime); + cie->mtu = htons(p->if_ad->mtu); + if (p->if_ad->network_id && p->route_type == NHRP_ROUTE_OFF_NBMA) + cie->prefix_length = p->route_prefix.prefixlen; + else + cie->prefix_length = 8 * sockunion_get_addrlen(&p->if_ad->addr); + + /* Handle extensions */ + while ((ext = nhrp_ext_pull(&p->extensions, &payload)) != NULL) { + switch (htons(ext->type) & ~NHRP_EXTENSION_FLAG_COMPULSORY) { + case NHRP_EXTENSION_NAT_ADDRESS: + if (sockunion_family(&nifp->nat_nbma) == AF_UNSPEC) + break; + ext = nhrp_ext_push(zb, hdr, NHRP_EXTENSION_NAT_ADDRESS); + if (!ext) goto err; + cie = nhrp_cie_push(zb, NHRP_CODE_SUCCESS, &nifp->nat_nbma, &p->if_ad->addr); + if (!cie) goto err; + nhrp_ext_complete(zb, ext); + break; + default: + if (nhrp_ext_reply(zb, hdr, p->ifp, ext, &payload) < 0) + goto err; + break; + } + } + + nhrp_packet_complete(zb, hdr); + nhrp_peer_send(peer, zb); +err: + nhrp_peer_unref(peer); + zbuf_free(zb); +} + +static void nhrp_handle_registration_request(struct nhrp_packet_parser *p) +{ + struct interface *ifp = p->ifp; + struct zbuf *zb, payload; + struct nhrp_packet_header *hdr; + struct nhrp_cie_header *cie; + struct nhrp_extension_header *ext; + struct nhrp_cache *c; + union sockunion cie_nbma, cie_proto, *proto_addr, *nbma_addr, *nbma_natoa; + int holdtime, prefix_len, hostprefix_len, natted = 0; + size_t paylen; + void *pay; + + debugf(NHRP_DEBUG_COMMON, "Parsing and replying to Registration Req"); + hostprefix_len = 8 * sockunion_get_addrlen(&p->if_ad->addr); + + if (!sockunion_same(&p->src_nbma, &p->peer->vc->remote.nbma)) + natted = 1; + + /* Create reply */ + zb = zbuf_alloc(1500); + hdr = nhrp_packet_push(zb, NHRP_PACKET_REGISTRATION_REPLY, + &p->src_nbma, &p->src_proto, &p->if_ad->addr); + + /* Copied information from request */ + hdr->flags = p->hdr->flags & htons(NHRP_FLAG_REGISTRATION_UNIQUE | NHRP_FLAG_REGISTRATION_NAT); + hdr->u.request_id = p->hdr->u.request_id; + + /* Copy payload CIEs */ + paylen = zbuf_used(&p->payload); + pay = zbuf_pushn(zb, paylen); + if (!pay) goto err; + memcpy(pay, zbuf_pulln(&p->payload, paylen), paylen); + zbuf_init(&payload, pay, paylen, paylen); + + while ((cie = nhrp_cie_pull(&payload, hdr, &cie_nbma, &cie_proto)) != NULL) { + prefix_len = cie->prefix_length; + if (prefix_len == 0 || prefix_len >= hostprefix_len) + prefix_len = hostprefix_len; + + if (prefix_len != hostprefix_len && !(p->hdr->flags & htons(NHRP_FLAG_REGISTRATION_UNIQUE))) { + cie->code = NHRP_CODE_BINDING_NON_UNIQUE; + continue; + } + + /* We currently support only unique prefix registrations */ + if (prefix_len != hostprefix_len) { + cie->code = NHRP_CODE_ADMINISTRATIVELY_PROHIBITED; + continue; + } + + proto_addr = (sockunion_family(&cie_proto) == AF_UNSPEC) ? &p->src_proto : &cie_proto; + nbma_addr = (sockunion_family(&cie_nbma) == AF_UNSPEC) ? &p->src_nbma : &cie_nbma; + nbma_natoa = NULL; + if (natted) { + nbma_natoa = nbma_addr; + nbma_addr = &p->peer->vc->remote.nbma; + } + + holdtime = htons(cie->holding_time); + if (!holdtime) holdtime = p->if_ad->holdtime; + + c = nhrp_cache_get(ifp, proto_addr, 1); + if (!c) { + cie->code = NHRP_CODE_INSUFFICIENT_RESOURCES; + continue; + } + + if (!nhrp_cache_update_binding(c, NHRP_CACHE_DYNAMIC, holdtime, nhrp_peer_ref(p->peer), htons(cie->mtu), nbma_natoa)) { + cie->code = NHRP_CODE_ADMINISTRATIVELY_PROHIBITED; + continue; + } + + cie->code = NHRP_CODE_SUCCESS; + } + + /* Handle extensions */ + while ((ext = nhrp_ext_pull(&p->extensions, &payload)) != NULL) { + switch (htons(ext->type) & ~NHRP_EXTENSION_FLAG_COMPULSORY) { + case NHRP_EXTENSION_NAT_ADDRESS: + ext = nhrp_ext_push(zb, hdr, NHRP_EXTENSION_NAT_ADDRESS); + if (!ext) goto err; + zbuf_copy(zb, &payload, zbuf_used(&payload)); + if (natted) { + nhrp_cie_push(zb, NHRP_CODE_SUCCESS, + &p->peer->vc->remote.nbma, + &p->src_proto); + } + nhrp_ext_complete(zb, ext); + break; + default: + if (nhrp_ext_reply(zb, hdr, ifp, ext, &payload) < 0) + goto err; + break; + } + } + + nhrp_packet_complete(zb, hdr); + nhrp_peer_send(p->peer, zb); +err: + zbuf_free(zb); +} + +static int parse_ether_packet(struct zbuf *zb, uint16_t protocol_type, union sockunion *src, union sockunion *dst) +{ + switch (protocol_type) { + case ETH_P_IP: { + struct iphdr *iph = zbuf_pull(zb, struct iphdr); + if (iph) { + if (src) sockunion_set(src, AF_INET, (uint8_t*) &iph->saddr, sizeof(iph->saddr)); + if (dst) sockunion_set(dst, AF_INET, (uint8_t*) &iph->daddr, sizeof(iph->daddr)); + } + } + break; + case ETH_P_IPV6: { + struct ipv6hdr *iph = zbuf_pull(zb, struct ipv6hdr); + if (iph) { + if (src) sockunion_set(src, AF_INET6, (uint8_t*) &iph->saddr, sizeof(iph->saddr)); + if (dst) sockunion_set(dst, AF_INET6, (uint8_t*) &iph->daddr, sizeof(iph->daddr)); + } + } + break; + default: + return 0; + } + return 1; +} + +void nhrp_peer_send_indication(struct interface *ifp, uint16_t protocol_type, struct zbuf *pkt) +{ + union sockunion dst; + struct zbuf *zb, payload; + struct nhrp_interface *nifp = ifp->info; + struct nhrp_afi_data *if_ad; + struct nhrp_packet_header *hdr; + struct nhrp_peer *p; + char buf[2][SU_ADDRSTRLEN]; + + if (!nifp->enabled) return; + + payload = *pkt; + if (!parse_ether_packet(&payload, protocol_type, &dst, NULL)) + return; + + if (nhrp_route_address(ifp, &dst, NULL, &p) != NHRP_ROUTE_NBMA_NEXTHOP) + return; + + if_ad = &nifp->afi[family2afi(sockunion_family(&dst))]; + if (!(if_ad->flags & NHRP_IFF_REDIRECT)) { + debugf(NHRP_DEBUG_COMMON, "Send Traffic Indication to %s about packet to %s ignored", + sockunion2str(&p->vc->remote.nbma, buf[0], sizeof buf[0]), + sockunion2str(&dst, buf[1], sizeof buf[1])); + return; + } + + debugf(NHRP_DEBUG_COMMON, "Send Traffic Indication to %s (online=%d) about packet to %s", + sockunion2str(&p->vc->remote.nbma, buf[0], sizeof buf[0]), + p->online, + sockunion2str(&dst, buf[1], sizeof buf[1])); + + /* Create reply */ + zb = zbuf_alloc(1500); + hdr = nhrp_packet_push(zb, NHRP_PACKET_TRAFFIC_INDICATION, &nifp->nbma, &if_ad->addr, &dst); + hdr->hop_count = 0; + + /* Payload is the packet causing indication */ + zbuf_copy(zb, pkt, zbuf_used(pkt)); + nhrp_packet_complete(zb, hdr); + nhrp_peer_send(p, zb); + nhrp_peer_unref(p); + zbuf_free(zb); +} + +static void nhrp_handle_error_ind(struct nhrp_packet_parser *pp) +{ + struct zbuf origmsg = pp->payload; + struct nhrp_packet_header *hdr; + struct nhrp_reqid *reqid; + union sockunion src_nbma, src_proto, dst_proto; + char buf[2][SU_ADDRSTRLEN]; + + hdr = nhrp_packet_pull(&origmsg, &src_nbma, &src_proto, &dst_proto); + if (!hdr) return; + + debugf(NHRP_DEBUG_COMMON, "Error Indication from %s about packet to %s ignored", + sockunion2str(&pp->src_proto, buf[0], sizeof buf[0]), + sockunion2str(&dst_proto, buf[1], sizeof buf[1])); + + reqid = nhrp_reqid_lookup(&nhrp_packet_reqid, htonl(hdr->u.request_id)); + if (reqid) + reqid->cb(reqid, pp); +} + +static void nhrp_handle_traffic_ind(struct nhrp_packet_parser *p) +{ + union sockunion dst; + char buf[2][SU_ADDRSTRLEN]; + + if (!parse_ether_packet(&p->payload, htons(p->hdr->protocol_type), NULL, &dst)) + return; + + debugf(NHRP_DEBUG_COMMON, "Traffic Indication from %s about packet to %s: %s", + sockunion2str(&p->src_proto, buf[0], sizeof buf[0]), + sockunion2str(&dst, buf[1], sizeof buf[1]), + (p->if_ad->flags & NHRP_IFF_SHORTCUT) ? "trying shortcut" : "ignored"); + + if (p->if_ad->flags & NHRP_IFF_SHORTCUT) + nhrp_shortcut_initiate(&dst); +} + +enum packet_type_t { + PACKET_UNKNOWN = 0, + PACKET_REQUEST, + PACKET_REPLY, + PACKET_INDICATION, +}; + +static struct { + enum packet_type_t type; + const char *name; + void (*handler)(struct nhrp_packet_parser *); +} packet_types[] = { + [NHRP_PACKET_RESOLUTION_REQUEST] = { + .type = PACKET_REQUEST, + .name = "Resolution-Request", + .handler = nhrp_handle_resolution_req, + }, + [NHRP_PACKET_RESOLUTION_REPLY] = { + .type = PACKET_REPLY, + .name = "Resolution-Reply", + }, + [NHRP_PACKET_REGISTRATION_REQUEST] = { + .type = PACKET_REQUEST, + .name = "Registration-Request", + .handler = nhrp_handle_registration_request, + }, + [NHRP_PACKET_REGISTRATION_REPLY] = { + .type = PACKET_REPLY, + .name = "Registration-Reply", + }, + [NHRP_PACKET_PURGE_REQUEST] = { + .type = PACKET_REQUEST, + .name = "Purge-Request", + }, + [NHRP_PACKET_PURGE_REPLY] = { + .type = PACKET_REPLY, + .name = "Purge-Reply", + }, + [NHRP_PACKET_ERROR_INDICATION] = { + .type = PACKET_INDICATION, + .name = "Error-Indication", + .handler = nhrp_handle_error_ind, + }, + [NHRP_PACKET_TRAFFIC_INDICATION] = { + .type = PACKET_INDICATION, + .name = "Traffic-Indication", + .handler = nhrp_handle_traffic_ind, + } +}; + +static void nhrp_peer_forward(struct nhrp_peer *p, struct nhrp_packet_parser *pp) +{ + struct zbuf *zb, extpl; + struct nhrp_packet_header *hdr; + struct nhrp_extension_header *ext, *dst; + struct nhrp_cie_header *cie; + struct nhrp_interface *nifp = pp->ifp->info; + struct nhrp_afi_data *if_ad = pp->if_ad; + union sockunion cie_nbma, cie_protocol; + uint16_t type, len; + + if (pp->hdr->hop_count == 0) + return; + + /* Create forward packet - copy header */ + zb = zbuf_alloc(1500); + hdr = nhrp_packet_push(zb, pp->hdr->type, &pp->src_nbma, &pp->src_proto, &pp->dst_proto); + hdr->flags = pp->hdr->flags; + hdr->hop_count = pp->hdr->hop_count - 1; + hdr->u.request_id = pp->hdr->u.request_id; + + /* Copy payload */ + zbuf_copy(zb, &pp->payload, zbuf_used(&pp->payload)); + + /* Copy extensions */ + while ((ext = nhrp_ext_pull(&pp->extensions, &extpl)) != NULL) { + type = htons(ext->type) & ~NHRP_EXTENSION_FLAG_COMPULSORY; + len = htons(ext->length); + + if (type == NHRP_EXTENSION_END) + break; + + dst = nhrp_ext_push(zb, hdr, htons(ext->type)); + if (!dst) goto err; + + switch (type) { + case NHRP_EXTENSION_FORWARD_TRANSIT_NHS: + case NHRP_EXTENSION_REVERSE_TRANSIT_NHS: + zbuf_put(zb, extpl.head, len); + if ((type == NHRP_EXTENSION_REVERSE_TRANSIT_NHS) == + (packet_types[hdr->type].type == PACKET_REPLY)) { + /* Check NHS list for forwarding loop */ + while ((cie = nhrp_cie_pull(&extpl, pp->hdr, &cie_nbma, &cie_protocol)) != NULL) { + if (sockunion_same(&p->vc->remote.nbma, &cie_nbma)) + goto err; + } + /* Append our selves to the list */ + cie = nhrp_cie_push(zb, NHRP_CODE_SUCCESS, &nifp->nbma, &if_ad->addr); + if (!cie) goto err; + cie->holding_time = htons(if_ad->holdtime); + } + break; + default: + if (htons(ext->type) & NHRP_EXTENSION_FLAG_COMPULSORY) + /* FIXME: RFC says to just copy, but not + * append our selves to the transit NHS list */ + goto err; + case NHRP_EXTENSION_RESPONDER_ADDRESS: + /* Supported compulsory extensions, and any + * non-compulsory that is not explicitly handled, + * should be just copied. */ + zbuf_copy(zb, &extpl, len); + break; + } + nhrp_ext_complete(zb, dst); + } + + nhrp_packet_complete(zb, hdr); + nhrp_peer_send(p, zb); + zbuf_free(zb); + return; +err: + nhrp_packet_debug(pp->pkt, "FWD-FAIL"); + zbuf_free(zb); +} + +static void nhrp_packet_debug(struct zbuf *zb, const char *dir) +{ + char buf[2][SU_ADDRSTRLEN]; + union sockunion src_nbma, src_proto, dst_proto; + struct nhrp_packet_header *hdr; + struct zbuf zhdr; + int reply; + + if (likely(!(debug_flags & NHRP_DEBUG_COMMON))) + return; + + zbuf_init(&zhdr, zb->buf, zb->tail-zb->buf, zb->tail-zb->buf); + hdr = nhrp_packet_pull(&zhdr, &src_nbma, &src_proto, &dst_proto); + + sockunion2str(&src_proto, buf[0], sizeof buf[0]); + sockunion2str(&dst_proto, buf[1], sizeof buf[1]); + + reply = packet_types[hdr->type].type == PACKET_REPLY; + debugf(NHRP_DEBUG_COMMON, "%s %s(%d) %s -> %s", + dir, + packet_types[hdr->type].name ? : "Unknown", + hdr->type, + reply ? buf[1] : buf[0], + reply ? buf[0] : buf[1]); +} + +static int proto2afi(uint16_t proto) +{ + switch (proto) { + case ETH_P_IP: return AFI_IP; + case ETH_P_IPV6: return AFI_IP6; + } + return AF_UNSPEC; +} + +struct nhrp_route_info { + int local; + struct interface *ifp; + struct nhrp_vc *vc; +}; + +void nhrp_peer_recv(struct nhrp_peer *p, struct zbuf *zb) +{ + char buf[2][SU_ADDRSTRLEN]; + struct nhrp_packet_header *hdr; + struct nhrp_vc *vc = p->vc; + struct interface *ifp = p->ifp; + struct nhrp_interface *nifp = ifp->info; + struct nhrp_packet_parser pp; + struct nhrp_peer *peer = NULL; + struct nhrp_reqid *reqid; + const char *info = NULL; + union sockunion *target_addr; + unsigned paylen, extoff, extlen, realsize; + afi_t nbma_afi, proto_afi; + + debugf(NHRP_DEBUG_KERNEL, "PACKET: Recv %s -> %s", + sockunion2str(&vc->remote.nbma, buf[0], sizeof buf[0]), + sockunion2str(&vc->local.nbma, buf[1], sizeof buf[1])); + + if (!p->online) { + info = "peer not online"; + goto drop; + } + + if (nhrp_packet_calculate_checksum(zb->head, zbuf_used(zb)) != 0) { + info = "bad checksum"; + goto drop; + } + + realsize = zbuf_used(zb); + hdr = nhrp_packet_pull(zb, &pp.src_nbma, &pp.src_proto, &pp.dst_proto); + if (!hdr) { + info = "corrupt header"; + goto drop; + } + + pp.ifp = ifp; + pp.pkt = zb; + pp.hdr = hdr; + pp.peer = p; + + nbma_afi = htons(hdr->afnum); + proto_afi = proto2afi(htons(hdr->protocol_type)); + if (hdr->type > ZEBRA_NUM_OF(packet_types) || + hdr->version != NHRP_VERSION_RFC2332 || + nbma_afi >= AFI_MAX || proto_afi == AF_UNSPEC || + packet_types[hdr->type].type == PACKET_UNKNOWN || + htons(hdr->packet_size) > realsize) { + zlog_info("From %s: error: packet type %d, version %d, AFI %d, proto %x, size %d (real size %d)", + sockunion2str(&vc->remote.nbma, buf[0], sizeof buf[0]), + (int) hdr->type, (int) hdr->version, + (int) nbma_afi, (int) htons(hdr->protocol_type), + (int) htons(hdr->packet_size), (int) realsize); + goto drop; + } + pp.if_ad = &((struct nhrp_interface *)ifp->info)->afi[proto_afi]; + + extoff = htons(hdr->extension_offset); + if (extoff) { + if (extoff >= realsize) { + info = "extoff larger than packet"; + goto drop; + } + paylen = extoff - (zb->head - zb->buf); + } else { + paylen = zbuf_used(zb); + } + zbuf_init(&pp.payload, zbuf_pulln(zb, paylen), paylen, paylen); + extlen = zbuf_used(zb); + zbuf_init(&pp.extensions, zbuf_pulln(zb, extlen), extlen, extlen); + + if (!nifp->afi[proto_afi].network_id) { + info = "nhrp not enabled"; + goto drop; + } + + nhrp_packet_debug(zb, "Recv"); + + /* FIXME: Check authentication here. This extension needs to be + * pre-handled. */ + + /* Figure out if this is local */ + target_addr = (packet_types[hdr->type].type == PACKET_REPLY) ? &pp.src_proto : &pp.dst_proto; + + if (sockunion_same(&pp.src_proto, &pp.dst_proto)) + pp.route_type = NHRP_ROUTE_LOCAL; + else + pp.route_type = nhrp_route_address(pp.ifp, target_addr, &pp.route_prefix, &peer); + + switch (pp.route_type) { + case NHRP_ROUTE_LOCAL: + nhrp_packet_debug(zb, "!LOCAL"); + if (packet_types[hdr->type].type == PACKET_REPLY) { + reqid = nhrp_reqid_lookup(&nhrp_packet_reqid, htonl(hdr->u.request_id)); + if (reqid) { + reqid->cb(reqid, &pp); + break; + } else { + nhrp_packet_debug(zb, "!UNKNOWN-REQID"); + /* FIXME: send error-indication */ + } + } + case NHRP_ROUTE_OFF_NBMA: + if (packet_types[hdr->type].handler) { + packet_types[hdr->type].handler(&pp); + break; + } + break; + case NHRP_ROUTE_NBMA_NEXTHOP: + nhrp_peer_forward(peer, &pp); + break; + case NHRP_ROUTE_BLACKHOLE: + break; + } + +drop: + if (info) { + zlog_info("From %s: error: %s", + sockunion2str(&vc->remote.nbma, buf[0], sizeof buf[0]), + info); + } + if (peer) nhrp_peer_unref(peer); + zbuf_free(zb); +} diff --git a/nhrpd/nhrp_protocol.h b/nhrpd/nhrp_protocol.h new file mode 100644 index 000000000..a4bc9fa6b --- /dev/null +++ b/nhrpd/nhrp_protocol.h @@ -0,0 +1,128 @@ +/* nhrp_protocol.h - NHRP protocol definitions + * + * Copyright (c) 2007-2012 Timo Teräs + * + * This software is licensed under the MIT License. + * See MIT-LICENSE.txt for additional details. + */ + +#ifndef NHRP_PROTOCOL_H +#define NHRP_PROTOCOL_H + +#include + +/* NHRP Ethernet protocol number */ +#define ETH_P_NHRP 0x2001 + +/* NHRP Version */ +#define NHRP_VERSION_RFC2332 1 + +/* NHRP Packet Types */ +#define NHRP_PACKET_RESOLUTION_REQUEST 1 +#define NHRP_PACKET_RESOLUTION_REPLY 2 +#define NHRP_PACKET_REGISTRATION_REQUEST 3 +#define NHRP_PACKET_REGISTRATION_REPLY 4 +#define NHRP_PACKET_PURGE_REQUEST 5 +#define NHRP_PACKET_PURGE_REPLY 6 +#define NHRP_PACKET_ERROR_INDICATION 7 +#define NHRP_PACKET_TRAFFIC_INDICATION 8 + +/* NHRP Extension Types */ +#define NHRP_EXTENSION_FLAG_COMPULSORY 0x8000 +#define NHRP_EXTENSION_END 0 +#define NHRP_EXTENSION_PAYLOAD 0 +#define NHRP_EXTENSION_RESPONDER_ADDRESS 3 +#define NHRP_EXTENSION_FORWARD_TRANSIT_NHS 4 +#define NHRP_EXTENSION_REVERSE_TRANSIT_NHS 5 +#define NHRP_EXTENSION_AUTHENTICATION 7 +#define NHRP_EXTENSION_VENDOR 8 +#define NHRP_EXTENSION_NAT_ADDRESS 9 + +/* NHRP Error Indication Codes */ +#define NHRP_ERROR_UNRECOGNIZED_EXTENSION 1 +#define NHRP_ERROR_LOOP_DETECTED 2 +#define NHRP_ERROR_PROTOCOL_ADDRESS_UNREACHABLE 6 +#define NHRP_ERROR_PROTOCOL_ERROR 7 +#define NHRP_ERROR_SDU_SIZE_EXCEEDED 8 +#define NHRP_ERROR_INVALID_EXTENSION 9 +#define NHRP_ERROR_INVALID_RESOLUTION_REPLY 10 +#define NHRP_ERROR_AUTHENTICATION_FAILURE 11 +#define NHRP_ERROR_HOP_COUNT_EXCEEDED 15 + +/* NHRP CIE Codes */ +#define NHRP_CODE_SUCCESS 0 +#define NHRP_CODE_ADMINISTRATIVELY_PROHIBITED 4 +#define NHRP_CODE_INSUFFICIENT_RESOURCES 5 +#define NHRP_CODE_NO_BINDING_EXISTS 11 +#define NHRP_CODE_BINDING_NON_UNIQUE 13 +#define NHRP_CODE_UNIQUE_ADDRESS_REGISTERED 14 + +/* NHRP Flags for Resolution request/reply */ +#define NHRP_FLAG_RESOLUTION_SOURCE_IS_ROUTER 0x8000 +#define NHRP_FLAG_RESOLUTION_AUTHORATIVE 0x4000 +#define NHRP_FLAG_RESOLUTION_DESTINATION_STABLE 0x2000 +#define NHRP_FLAG_RESOLUTION_UNIQUE 0x1000 +#define NHRP_FLAG_RESOLUTION_SOURCE_STABLE 0x0800 +#define NHRP_FLAG_RESOLUTION_NAT 0x0002 + +/* NHRP Flags for Registration request/reply */ +#define NHRP_FLAG_REGISTRATION_UNIQUE 0x8000 +#define NHRP_FLAG_REGISTRATION_NAT 0x0002 + +/* NHRP Flags for Purge request/reply */ +#define NHRP_FLAG_PURGE_NO_REPLY 0x8000 + +/* NHRP Authentication extension types (ala Cisco) */ +#define NHRP_AUTHENTICATION_PLAINTEXT 0x00000001 + +/* NHRP Packet Structures */ +struct nhrp_packet_header { + /* Fixed header */ + uint16_t afnum; + uint16_t protocol_type; + uint8_t snap[5]; + uint8_t hop_count; + uint16_t packet_size; + uint16_t checksum; + uint16_t extension_offset; + uint8_t version; + uint8_t type; + uint8_t src_nbma_address_len; + uint8_t src_nbma_subaddress_len; + + /* Mandatory header */ + uint8_t src_protocol_address_len; + uint8_t dst_protocol_address_len; + uint16_t flags; + union { + uint32_t request_id; + struct { + uint16_t code; + uint16_t offset; + } error; + } u; +} __attribute__((packed)); + +struct nhrp_cie_header { + uint8_t code; + uint8_t prefix_length; + uint16_t unused; + uint16_t mtu; + uint16_t holding_time; + uint8_t nbma_address_len; + uint8_t nbma_subaddress_len; + uint8_t protocol_address_len; + uint8_t preference; +} __attribute__((packed)); + +struct nhrp_extension_header { + uint16_t type; + uint16_t length; +} __attribute__((packed)); + +struct nhrp_cisco_authentication_extension { + uint32_t type; + uint8_t secret[8]; +} __attribute__((packed)); + +#endif diff --git a/nhrpd/nhrp_route.c b/nhrpd/nhrp_route.c new file mode 100644 index 000000000..a80f3e0c2 --- /dev/null +++ b/nhrpd/nhrp_route.c @@ -0,0 +1,383 @@ +/* NHRP routing functions + * Copyright (c) 2014-2015 Timo Teräs + * + * This file is free software: you may copy, redistribute 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. + */ + +#include "nhrpd.h" +#include "table.h" +#include "memory.h" +#include "stream.h" +#include "log.h" +#include "zclient.h" + +static struct zclient *zclient; +static struct route_table *zebra_rib[AFI_MAX]; + +struct route_info { + union sockunion via; + struct interface *ifp; + struct interface *nhrp_ifp; +}; + +static void nhrp_zebra_connected(struct zclient *zclient) +{ + /* No real VRF support yet -- bind only to the default vrf */ + zclient_send_requests (zclient, VRF_DEFAULT); +} + +static struct route_node *nhrp_route_update_get(const struct prefix *p, int create) +{ + struct route_node *rn; + afi_t afi = family2afi(PREFIX_FAMILY(p)); + + if (!zebra_rib[afi]) + return NULL; + + if (create) { + rn = route_node_get(zebra_rib[afi], p); + if (!rn->info) { + rn->info = XCALLOC(MTYPE_NHRP_ROUTE, sizeof(struct route_info)); + route_lock_node(rn); + } + return rn; + } else { + return route_node_lookup(zebra_rib[afi], p); + } +} + +static void nhrp_route_update_put(struct route_node *rn) +{ + struct route_info *ri = rn->info; + + if (!ri->ifp && !ri->nhrp_ifp && sockunion_family(&ri->via) == AF_UNSPEC) { + XFREE(MTYPE_NHRP_ROUTE, rn->info); + rn->info = NULL; + route_unlock_node(rn); + } + route_unlock_node(rn); +} + +static void nhrp_route_update_zebra(const struct prefix *p, union sockunion *nexthop, struct interface *ifp) +{ + struct route_node *rn; + struct route_info *ri; + + rn = nhrp_route_update_get(p, (sockunion_family(nexthop) != AF_UNSPEC) || ifp); + if (rn) { + ri = rn->info; + ri->via = *nexthop; + ri->ifp = ifp; + nhrp_route_update_put(rn); + } +} + +void nhrp_route_update_nhrp(const struct prefix *p, struct interface *ifp) +{ + struct route_node *rn; + struct route_info *ri; + + rn = nhrp_route_update_get(p, ifp != NULL); + if (rn) { + ri = rn->info; + ri->nhrp_ifp = ifp; + nhrp_route_update_put(rn); + } +} + +void nhrp_route_announce(int add, enum nhrp_cache_type type, const struct prefix *p, struct interface *ifp, const union sockunion *nexthop, uint32_t mtu) +{ + int flags = 0; + + if (zclient->sock < 0) + return; + + switch (type) { + case NHRP_CACHE_NEGATIVE: + SET_FLAG(flags, ZEBRA_FLAG_REJECT); + break; + case NHRP_CACHE_DYNAMIC: + case NHRP_CACHE_NHS: + case NHRP_CACHE_STATIC: + /* Regular route, so these are announced + * to other routing daemons */ + break; + default: + SET_FLAG(flags, ZEBRA_FLAG_FIB_OVERRIDE); + break; + } + SET_FLAG(flags, ZEBRA_FLAG_INTERNAL); + + if (p->family == AF_INET) { + struct in_addr *nexthop_ipv4; + struct zapi_ipv4 api; + + memset(&api, 0, sizeof(api)); + api.flags = flags; + api.type = ZEBRA_ROUTE_NHRP; + api.safi = SAFI_UNICAST; + + SET_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP); + if (nexthop) { + nexthop_ipv4 = (struct in_addr *) sockunion_get_addr(nexthop); + api.nexthop_num = 1; + api.nexthop = &nexthop_ipv4; + } + if (ifp) { + SET_FLAG(api.message, ZAPI_MESSAGE_IFINDEX); + api.ifindex_num = 1; + api.ifindex = &ifp->ifindex; + } + if (mtu) { + SET_FLAG(api.message, ZAPI_MESSAGE_MTU); + api.mtu = mtu; + } + + if (unlikely(debug_flags & NHRP_DEBUG_ROUTE)) { + char buf[2][INET_ADDRSTRLEN]; + zlog_debug("Zebra send: IPv4 route %s %s/%d nexthop %s metric %u" + " count %d dev %s", + add ? "add" : "del", + inet_ntop(AF_INET, &p->u.prefix4, buf[0], sizeof(buf[0])), + p->prefixlen, + nexthop ? inet_ntop(AF_INET, api.nexthop[0], buf[1], sizeof(buf[1])) : "", + api.metric, api.nexthop_num, ifp->name); + } + + zapi_ipv4_route( + add ? ZEBRA_IPV4_ROUTE_ADD : ZEBRA_IPV4_ROUTE_DELETE, + zclient, (struct prefix_ipv4 *) p, &api); + } else if (p->family == AF_INET6) { + struct in6_addr *nexthop_ipv6; + struct zapi_ipv6 api; + + memset(&api, 0, sizeof(api)); + api.flags = flags; + api.type = ZEBRA_ROUTE_NHRP; + api.safi = SAFI_UNICAST; + + SET_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP); + if (nexthop) { + nexthop_ipv6 = (struct in6_addr *) sockunion_get_addr(nexthop); + api.nexthop_num = 1; + api.nexthop = &nexthop_ipv6; + } + if (ifp) { + SET_FLAG(api.message, ZAPI_MESSAGE_IFINDEX); + api.ifindex_num = 1; + api.ifindex = &ifp->ifindex; + } + if (mtu) { + SET_FLAG(api.message, ZAPI_MESSAGE_MTU); + api.mtu = mtu; + } + + if (unlikely(debug_flags & NHRP_DEBUG_ROUTE)) { + char buf[2][INET6_ADDRSTRLEN]; + zlog_debug("Zebra send: IPv6 route %s %s/%d nexthop %s metric %u" + " count %d dev %s", + add ? "add" : "del", + inet_ntop(AF_INET6, &p->u.prefix6, buf[0], sizeof(buf[0])), + p->prefixlen, + nexthop ? inet_ntop(AF_INET6, api.nexthop[0], buf[1], sizeof(buf[1])) : "", + api.metric, api.nexthop_num, ifp->name); + } + + zapi_ipv6_route( + add ? ZEBRA_IPV6_ROUTE_ADD : ZEBRA_IPV6_ROUTE_DELETE, + zclient, (struct prefix_ipv6 *) p, &api); + } +} + +int nhrp_route_read(int cmd, struct zclient *zclient, zebra_size_t length, vrf_id_t vrf_id) +{ + struct stream *s; + struct interface *ifp = NULL; + struct prefix prefix; + union sockunion nexthop_addr; + unsigned char message, nexthop_num, ifindex_num; + unsigned ifindex; + char buf[2][PREFIX_STRLEN]; + int i, afaddrlen, added; + + s = zclient->ibuf; + memset(&prefix, 0, sizeof(prefix)); + sockunion_family(&nexthop_addr) = AF_UNSPEC; + + /* Type, flags, message. */ + /*type =*/ stream_getc(s); + /*flags =*/ stream_getc(s); + message = stream_getc(s); + + /* Prefix */ + switch (cmd) { + case ZEBRA_IPV4_ROUTE_ADD: + case ZEBRA_IPV4_ROUTE_DELETE: + prefix.family = AF_INET; + break; + case ZEBRA_IPV6_ROUTE_ADD: + case ZEBRA_IPV6_ROUTE_DELETE: + prefix.family = AF_INET6; + break; + default: + return -1; + } + afaddrlen = family2addrsize(prefix.family); + prefix.prefixlen = stream_getc(s); + stream_get(&prefix.u.val, s, PSIZE(prefix.prefixlen)); + + /* Nexthop, ifindex, distance, metric. */ + if (CHECK_FLAG(message, ZAPI_MESSAGE_NEXTHOP|ZAPI_MESSAGE_IFINDEX)) { + nexthop_num = stream_getc(s); + for (i = 0; i < nexthop_num; i++) { + stream_get(buf[0], s, afaddrlen); + if (i == 0) sockunion_set(&nexthop_addr, prefix.family, (u_char*) buf[0], afaddrlen); + } + ifindex_num = stream_getc(s); + for (i = 0; i < ifindex_num; i++) { + ifindex = stream_getl(s); + if (i == 0 && ifindex != IFINDEX_INTERNAL) + ifp = if_lookup_by_index(ifindex); + } + } + if (CHECK_FLAG(message, ZAPI_MESSAGE_DISTANCE)) + /*distance =*/ stream_getc(s); + if (CHECK_FLAG(message, ZAPI_MESSAGE_METRIC)) + /*metric =*/ stream_getl(s); + + added = (cmd == ZEBRA_IPV4_ROUTE_ADD || cmd == ZEBRA_IPV6_ROUTE_ADD); + debugf(NHRP_DEBUG_ROUTE, "if-route-%s: %s via %s dev %s", + added ? "add" : "del", + prefix2str(&prefix, buf[0], sizeof buf[0]), + sockunion2str(&nexthop_addr, buf[1], sizeof buf[1]), + ifp ? ifp->name : "(none)"); + + nhrp_route_update_zebra(&prefix, &nexthop_addr, ifp); + nhrp_shortcut_prefix_change(&prefix, !added); + + return 0; +} + +int nhrp_route_get_nexthop(const union sockunion *addr, struct prefix *p, union sockunion *via, struct interface **ifp) +{ + struct route_node *rn; + struct route_info *ri; + struct prefix lookup; + afi_t afi = family2afi(sockunion_family(addr)); + char buf[PREFIX_STRLEN]; + + sockunion2hostprefix(addr, &lookup); + + rn = route_node_match(zebra_rib[afi], &lookup); + if (!rn) return 0; + + ri = rn->info; + if (ri->nhrp_ifp) { + debugf(NHRP_DEBUG_ROUTE, "lookup %s: nhrp_if=%s", + prefix2str(&lookup, buf, sizeof buf), + ri->nhrp_ifp->name); + + if (via) sockunion_family(via) = AF_UNSPEC; + if (ifp) *ifp = ri->nhrp_ifp; + } else { + debugf(NHRP_DEBUG_ROUTE, "lookup %s: zebra route dev %s", + prefix2str(&lookup, buf, sizeof buf), + ri->ifp ? ri->ifp->name : "(none)"); + + if (via) *via = ri->via; + if (ifp) *ifp = ri->ifp; + } + if (p) *p = rn->p; + route_unlock_node(rn); + return 1; +} + +enum nhrp_route_type nhrp_route_address(struct interface *in_ifp, union sockunion *addr, struct prefix *p, struct nhrp_peer **peer) +{ + struct interface *ifp = in_ifp; + struct nhrp_interface *nifp; + struct nhrp_cache *c; + union sockunion via[4]; + uint32_t network_id = 0; + afi_t afi = family2afi(sockunion_family(addr)); + int i; + + if (ifp) { + nifp = ifp->info; + network_id = nifp->afi[afi].network_id; + + c = nhrp_cache_get(ifp, addr, 0); + if (c && c->cur.type == NHRP_CACHE_LOCAL) { + if (p) memset(p, 0, sizeof(*p)); + return NHRP_ROUTE_LOCAL; + } + } + + for (i = 0; i < 4; i++) { + if (!nhrp_route_get_nexthop(addr, p, &via[i], &ifp)) + return NHRP_ROUTE_BLACKHOLE; + if (ifp) { + /* Departing from nbma network? */ + nifp = ifp->info; + if (network_id && network_id != nifp->afi[afi].network_id) + return NHRP_ROUTE_OFF_NBMA; + } + if (sockunion_family(&via[i]) == AF_UNSPEC) + break; + /* Resolve via node, but return the prefix of first match */ + addr = &via[i]; + p = NULL; + } + + if (ifp) { + c = nhrp_cache_get(ifp, addr, 0); + if (c && c->cur.type >= NHRP_CACHE_DYNAMIC) { + if (p) memset(p, 0, sizeof(*p)); + if (c->cur.type == NHRP_CACHE_LOCAL) + return NHRP_ROUTE_LOCAL; + if (peer) *peer = nhrp_peer_ref(c->cur.peer); + return NHRP_ROUTE_NBMA_NEXTHOP; + } + } + + return NHRP_ROUTE_BLACKHOLE; +} + +void nhrp_zebra_init(void) +{ + zebra_rib[AFI_IP] = route_table_init(); + zebra_rib[AFI_IP6] = route_table_init(); + + zclient = zclient_new(master); + zclient->zebra_connected = nhrp_zebra_connected; + zclient->interface_add = nhrp_interface_add; + zclient->interface_delete = nhrp_interface_delete; + zclient->interface_up = nhrp_interface_up; + zclient->interface_down = nhrp_interface_down; + zclient->interface_address_add = nhrp_interface_address_add; + zclient->interface_address_delete = nhrp_interface_address_delete; + zclient->ipv4_route_add = nhrp_route_read; + zclient->ipv4_route_delete = nhrp_route_read; + zclient->ipv6_route_add = nhrp_route_read; + zclient->ipv6_route_delete = nhrp_route_read; + + zclient_init(zclient, ZEBRA_ROUTE_NHRP); + zclient_redistribute(ZEBRA_REDISTRIBUTE_ADD, zclient, ZEBRA_ROUTE_KERNEL, VRF_DEFAULT); + zclient_redistribute(ZEBRA_REDISTRIBUTE_ADD, zclient, ZEBRA_ROUTE_CONNECT, VRF_DEFAULT); + zclient_redistribute(ZEBRA_REDISTRIBUTE_ADD, zclient, ZEBRA_ROUTE_STATIC, VRF_DEFAULT); + zclient_redistribute(ZEBRA_REDISTRIBUTE_ADD, zclient, ZEBRA_ROUTE_RIP, VRF_DEFAULT); + zclient_redistribute(ZEBRA_REDISTRIBUTE_ADD, zclient, ZEBRA_ROUTE_OSPF, VRF_DEFAULT); + zclient_redistribute(ZEBRA_REDISTRIBUTE_ADD, zclient, ZEBRA_ROUTE_ISIS, VRF_DEFAULT); + zclient_redistribute(ZEBRA_REDISTRIBUTE_ADD, zclient, ZEBRA_ROUTE_BGP, VRF_DEFAULT); +} + +void nhrp_zebra_terminate(void) +{ + zclient_stop(zclient); + route_table_finish(zebra_rib[AFI_IP]); + route_table_finish(zebra_rib[AFI_IP6]); +} + diff --git a/nhrpd/nhrp_shortcut.c b/nhrpd/nhrp_shortcut.c new file mode 100644 index 000000000..60c63929f --- /dev/null +++ b/nhrpd/nhrp_shortcut.c @@ -0,0 +1,402 @@ +/* NHRP shortcut related functions + * Copyright (c) 2014-2015 Timo Teräs + * + * This file is free software: you may copy, redistribute 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. + */ + +#include "nhrpd.h" +#include "table.h" +#include "memory.h" +#include "thread.h" +#include "log.h" +#include "nhrp_protocol.h" + +static struct route_table *shortcut_rib[AFI_MAX]; + +static int nhrp_shortcut_do_purge(struct thread *t); +static void nhrp_shortcut_delete(struct nhrp_shortcut *s); +static void nhrp_shortcut_send_resolution_req(struct nhrp_shortcut *s); + +static void nhrp_shortcut_check_use(struct nhrp_shortcut *s) +{ + char buf[PREFIX_STRLEN]; + + if (s->expiring && s->cache && s->cache->used) { + debugf(NHRP_DEBUG_ROUTE, "Shortcut %s used and expiring", + prefix2str(s->p, buf, sizeof buf)); + nhrp_shortcut_send_resolution_req(s); + } +} + +static int nhrp_shortcut_do_expire(struct thread *t) +{ + struct nhrp_shortcut *s = THREAD_ARG(t); + + s->t_timer = NULL; + THREAD_TIMER_ON(master, s->t_timer, nhrp_shortcut_do_purge, s, s->holding_time/3); + s->expiring = 1; + nhrp_shortcut_check_use(s); + + return 0; +} + +static void nhrp_shortcut_cache_notify(struct notifier_block *n, unsigned long cmd) +{ + struct nhrp_shortcut *s = container_of(n, struct nhrp_shortcut, cache_notifier); + + switch (cmd) { + case NOTIFY_CACHE_UP: + if (!s->route_installed) { + nhrp_route_announce(1, s->type, s->p, NULL, &s->cache->remote_addr, 0); + s->route_installed = 1; + } + break; + case NOTIFY_CACHE_USED: + nhrp_shortcut_check_use(s); + break; + case NOTIFY_CACHE_DOWN: + case NOTIFY_CACHE_DELETE: + if (s->route_installed) { + nhrp_route_announce(0, NHRP_CACHE_INVALID, s->p, NULL, NULL, 0); + s->route_installed = 0; + } + if (cmd == NOTIFY_CACHE_DELETE) + nhrp_shortcut_delete(s); + break; + } +} + +static void nhrp_shortcut_update_binding(struct nhrp_shortcut *s, enum nhrp_cache_type type, struct nhrp_cache *c, int holding_time) +{ + s->type = type; + if (c != s->cache) { + if (s->cache) { + nhrp_cache_notify_del(s->cache, &s->cache_notifier); + s->cache = NULL; + } + s->cache = c; + if (s->cache) { + nhrp_cache_notify_add(s->cache, &s->cache_notifier, nhrp_shortcut_cache_notify); + if (s->cache->route_installed) { + /* Force renewal of Zebra announce on prefix change */ + s->route_installed = 0; + nhrp_shortcut_cache_notify(&s->cache_notifier, NOTIFY_CACHE_UP); + } + } + if (!s->cache || !s->cache->route_installed) + nhrp_shortcut_cache_notify(&s->cache_notifier, NOTIFY_CACHE_DOWN); + } + if (s->type == NHRP_CACHE_NEGATIVE && !s->route_installed) { + nhrp_route_announce(1, s->type, s->p, NULL, NULL, 0); + s->route_installed = 1; + } else if (s->type == NHRP_CACHE_INVALID && s->route_installed) { + nhrp_route_announce(0, NHRP_CACHE_INVALID, s->p, NULL, NULL, 0); + s->route_installed = 0; + } + + THREAD_OFF(s->t_timer); + if (holding_time) { + s->expiring = 0; + s->holding_time = holding_time; + THREAD_TIMER_ON(master, s->t_timer, nhrp_shortcut_do_expire, s, 2*holding_time/3); + } +} + +static void nhrp_shortcut_delete(struct nhrp_shortcut *s) +{ + struct route_node *rn; + afi_t afi = family2afi(PREFIX_FAMILY(s->p)); + char buf[PREFIX_STRLEN]; + + THREAD_OFF(s->t_timer); + nhrp_reqid_free(&nhrp_packet_reqid, &s->reqid); + + debugf(NHRP_DEBUG_ROUTE, "Shortcut %s purged", + prefix2str(s->p, buf, sizeof buf)); + + nhrp_shortcut_update_binding(s, NHRP_CACHE_INVALID, NULL, 0); + + /* Delete node */ + rn = route_node_lookup(shortcut_rib[afi], s->p); + if (rn) { + XFREE(MTYPE_NHRP_SHORTCUT, rn->info); + rn->info = NULL; + route_unlock_node(rn); + route_unlock_node(rn); + } +} + +static int nhrp_shortcut_do_purge(struct thread *t) +{ + struct nhrp_shortcut *s = THREAD_ARG(t); + s->t_timer = NULL; + nhrp_shortcut_delete(s); + return 0; +} + +static struct nhrp_shortcut *nhrp_shortcut_get(struct prefix *p) +{ + struct nhrp_shortcut *s; + struct route_node *rn; + char buf[PREFIX_STRLEN]; + afi_t afi = family2afi(PREFIX_FAMILY(p)); + + if (!shortcut_rib[afi]) + return 0; + + rn = route_node_get(shortcut_rib[afi], p); + if (!rn->info) { + s = rn->info = XCALLOC(MTYPE_NHRP_SHORTCUT, sizeof(struct nhrp_shortcut)); + s->type = NHRP_CACHE_INVALID; + s->p = &rn->p; + + debugf(NHRP_DEBUG_ROUTE, "Shortcut %s created", + prefix2str(s->p, buf, sizeof buf)); + } else { + s = rn->info; + route_unlock_node(rn); + } + return s; +} + +static void nhrp_shortcut_recv_resolution_rep(struct nhrp_reqid *reqid, void *arg) +{ + struct nhrp_packet_parser *pp = arg; + struct nhrp_shortcut *s = container_of(reqid, struct nhrp_shortcut, reqid); + struct nhrp_shortcut *ps; + struct nhrp_extension_header *ext; + struct nhrp_cie_header *cie; + struct nhrp_cache *c = NULL; + union sockunion *proto, cie_proto, *nbma, *nbma_natoa, cie_nbma, nat_nbma; + struct prefix prefix, route_prefix; + struct zbuf extpl; + char bufp[PREFIX_STRLEN], buf[3][SU_ADDRSTRLEN]; + int holding_time = pp->if_ad->holdtime; + + nhrp_reqid_free(&nhrp_packet_reqid, &s->reqid); + THREAD_OFF(s->t_timer); + THREAD_TIMER_ON(master, s->t_timer, nhrp_shortcut_do_purge, s, 1); + + if (pp->hdr->type != NHRP_PACKET_RESOLUTION_REPLY) { + if (pp->hdr->type == NHRP_PACKET_ERROR_INDICATION && + pp->hdr->u.error.code == NHRP_ERROR_PROTOCOL_ADDRESS_UNREACHABLE) { + debugf(NHRP_DEBUG_COMMON, "Shortcut: Resolution: Protocol address unreachable"); + nhrp_shortcut_update_binding(s, NHRP_CACHE_NEGATIVE, NULL, holding_time); + } else { + debugf(NHRP_DEBUG_COMMON, "Shortcut: Resolution failed"); + } + return; + } + + /* Parse extensions */ + memset(&nat_nbma, 0, sizeof nat_nbma); + while ((ext = nhrp_ext_pull(&pp->extensions, &extpl)) != NULL) { + switch (htons(ext->type) & ~NHRP_EXTENSION_FLAG_COMPULSORY) { + case NHRP_EXTENSION_NAT_ADDRESS: + nhrp_cie_pull(&extpl, pp->hdr, &nat_nbma, &cie_proto); + break; + } + } + + /* Minor sanity check */ + prefix2sockunion(s->p, &cie_proto); + if (!sockunion_same(&cie_proto, &pp->dst_proto)) { + debugf(NHRP_DEBUG_COMMON, "Shortcut: Warning dst_proto altered from %s to %s", + sockunion2str(&cie_proto, buf[0], sizeof buf[0]), + sockunion2str(&pp->dst_proto, buf[1], sizeof buf[1])); + } + + /* One or more CIEs should be given as reply, we support only one */ + cie = nhrp_cie_pull(&pp->payload, pp->hdr, &cie_nbma, &cie_proto); + if (!cie || cie->code != NHRP_CODE_SUCCESS) { + debugf(NHRP_DEBUG_COMMON, "Shortcut: CIE code %d", cie ? cie->code : -1); + return; + } + + proto = sockunion_family(&cie_proto) != AF_UNSPEC ? &cie_proto : &pp->dst_proto; + if (cie->holding_time) + holding_time = htons(cie->holding_time); + + prefix = *s->p; + prefix.prefixlen = cie->prefix_length; + + /* Sanity check prefix length */ + if (prefix.prefixlen >= 8*prefix_blen(&prefix) || prefix.prefixlen == 0) { + prefix.prefixlen = 8*prefix_blen(&prefix); + } else if (nhrp_route_address(NULL, &pp->dst_proto, &route_prefix, NULL) == NHRP_ROUTE_NBMA_NEXTHOP) { + if (prefix.prefixlen < route_prefix.prefixlen) + prefix.prefixlen = route_prefix.prefixlen; + } + + debugf(NHRP_DEBUG_COMMON, "Shortcut: %s is at proto %s cie-nbma %s nat-nbma %s cie-holdtime %d", + prefix2str(&prefix, bufp, sizeof bufp), + sockunion2str(proto, buf[0], sizeof buf[0]), + sockunion2str(&cie_nbma, buf[1], sizeof buf[1]), + sockunion2str(&nat_nbma, buf[2], sizeof buf[2]), + htons(cie->holding_time)); + + /* Update cache entry for the protocol to nbma binding */ + if (sockunion_family(&nat_nbma) != AF_UNSPEC) { + nbma = &nat_nbma; + nbma_natoa = &cie_nbma; + } else { + nbma = &cie_nbma; + nbma_natoa = NULL; + } + if (sockunion_family(nbma)) { + c = nhrp_cache_get(pp->ifp, proto, 1); + if (c) { + nhrp_cache_update_binding( + c, NHRP_CACHE_CACHED, holding_time, + nhrp_peer_get(pp->ifp, nbma), + htons(cie->mtu), nbma_natoa); + } + } + + /* Update shortcut entry for subnet to protocol gw binding */ + if (c && !sockunion_same(proto, &pp->dst_proto)) { + ps = nhrp_shortcut_get(&prefix); + if (ps) { + ps->addr = s->addr; + nhrp_shortcut_update_binding(ps, NHRP_CACHE_CACHED, c, holding_time); + } + } + + debugf(NHRP_DEBUG_COMMON, "Shortcut: Resolution reply handled"); +} + +static void nhrp_shortcut_send_resolution_req(struct nhrp_shortcut *s) +{ + struct zbuf *zb; + struct nhrp_packet_header *hdr; + struct interface *ifp; + struct nhrp_interface *nifp; + struct nhrp_peer *peer; + + if (nhrp_route_address(NULL, &s->addr, NULL, &peer) != NHRP_ROUTE_NBMA_NEXTHOP) + return; + + if (s->type == NHRP_CACHE_INVALID || s->type == NHRP_CACHE_NEGATIVE) + s->type = NHRP_CACHE_INCOMPLETE; + + ifp = peer->ifp; + nifp = ifp->info; + + /* Create request */ + zb = zbuf_alloc(1500); + hdr = nhrp_packet_push(zb, NHRP_PACKET_RESOLUTION_REQUEST, + &nifp->nbma, &nifp->afi[family2afi(sockunion_family(&s->addr))].addr, &s->addr); + hdr->u.request_id = htonl(nhrp_reqid_alloc(&nhrp_packet_reqid, &s->reqid, nhrp_shortcut_recv_resolution_rep)); + hdr->flags = htons(NHRP_FLAG_RESOLUTION_SOURCE_IS_ROUTER | + NHRP_FLAG_RESOLUTION_AUTHORATIVE | + NHRP_FLAG_RESOLUTION_SOURCE_STABLE); + + /* RFC2332 - One or zero CIEs, if CIE is present contains: + * - Prefix length: widest acceptable prefix we accept (if U set, 0xff) + * - MTU: MTU of the source station + * - Holding Time: Max time to cache the source information + * */ + /* FIXME: Send holding time, and MTU */ + + nhrp_ext_request(zb, hdr, ifp); + + /* Cisco NAT detection extension */ + hdr->flags |= htons(NHRP_FLAG_RESOLUTION_NAT); + nhrp_ext_push(zb, hdr, NHRP_EXTENSION_NAT_ADDRESS); + + nhrp_packet_complete(zb, hdr); + + nhrp_peer_send(peer, zb); + nhrp_peer_unref(peer); + zbuf_free(zb); +} + +void nhrp_shortcut_initiate(union sockunion *addr) +{ + struct prefix p; + struct nhrp_shortcut *s; + + sockunion2hostprefix(addr, &p); + s = nhrp_shortcut_get(&p); + if (s && s->type != NHRP_CACHE_INCOMPLETE) { + s->addr = *addr; + THREAD_OFF(s->t_timer); + THREAD_TIMER_ON(master, s->t_timer, nhrp_shortcut_do_purge, s, 30); + nhrp_shortcut_send_resolution_req(s); + } +} + +void nhrp_shortcut_init(void) +{ + shortcut_rib[AFI_IP] = route_table_init(); + shortcut_rib[AFI_IP6] = route_table_init(); +} + +void nhrp_shortcut_terminate(void) +{ + route_table_finish(shortcut_rib[AFI_IP]); + route_table_finish(shortcut_rib[AFI_IP6]); +} + +void nhrp_shortcut_foreach(afi_t afi, void (*cb)(struct nhrp_shortcut *, void *), void *ctx) +{ + struct route_table *rt = shortcut_rib[afi]; + struct route_node *rn; + route_table_iter_t iter; + + if (!rt) return; + + route_table_iter_init(&iter, rt); + while ((rn = route_table_iter_next(&iter)) != NULL) { + if (rn->info) cb(rn->info, ctx); + } + route_table_iter_cleanup(&iter); +} + +struct purge_ctx { + const struct prefix *p; + int deleted; +}; + +void nhrp_shortcut_purge(struct nhrp_shortcut *s, int force) +{ + THREAD_OFF(s->t_timer); + nhrp_reqid_free(&nhrp_packet_reqid, &s->reqid); + + if (force) { + /* Immediate purge on route with draw or pending shortcut */ + THREAD_TIMER_MSEC_ON(master, s->t_timer, nhrp_shortcut_do_purge, s, 5); + } else { + /* Soft expire - force immediate renewal, but purge + * in few seconds to make sure stale route is not + * used too long. In practice most purges are caused + * by hub bgp change, but target usually stays same. + * This allows to keep nhrp route up, and to not + * cause temporary rerouting via hubs causing latency + * jitter. */ + THREAD_TIMER_MSEC_ON(master, s->t_timer, nhrp_shortcut_do_purge, s, 3000); + s->expiring = 1; + nhrp_shortcut_check_use(s); + } +} + +static void nhrp_shortcut_purge_prefix(struct nhrp_shortcut *s, void *ctx) +{ + struct purge_ctx *pctx = ctx; + + if (prefix_match(pctx->p, s->p)) + nhrp_shortcut_purge(s, pctx->deleted || !s->cache); +} + +void nhrp_shortcut_prefix_change(const struct prefix *p, int deleted) +{ + struct purge_ctx pctx = { + .p = p, + .deleted = deleted, + }; + nhrp_shortcut_foreach(family2afi(PREFIX_FAMILY(p)), nhrp_shortcut_purge_prefix, &pctx); +} + diff --git a/nhrpd/nhrp_vc.c b/nhrpd/nhrp_vc.c new file mode 100644 index 000000000..f9e1ee068 --- /dev/null +++ b/nhrpd/nhrp_vc.c @@ -0,0 +1,217 @@ +/* NHRP virtual connection + * Copyright (c) 2014-2015 Timo Teräs + * + * This file is free software: you may copy, redistribute 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. + */ + +#include "zebra.h" +#include "memory.h" +#include "stream.h" +#include "hash.h" +#include "thread.h" +#include "jhash.h" + +#include "nhrpd.h" +#include "os.h" + +struct child_sa { + uint32_t id; + struct nhrp_vc *vc; + struct list_head childlist_entry; +}; + +static struct hash *nhrp_vc_hash; +static struct list_head childlist_head[512]; + +static unsigned int nhrp_vc_key(void *peer_data) +{ + struct nhrp_vc *vc = peer_data; + return jhash_2words( + sockunion_hash(&vc->local.nbma), + sockunion_hash(&vc->remote.nbma), + 0); +} + +static int nhrp_vc_cmp(const void *cache_data, const void *key_data) +{ + const struct nhrp_vc *a = cache_data; + const struct nhrp_vc *b = key_data; + return sockunion_same(&a->local.nbma, &b->local.nbma) && + sockunion_same(&a->remote.nbma, &b->remote.nbma); +} + +static void *nhrp_vc_alloc(void *data) +{ + struct nhrp_vc *vc, *key = data; + + vc = XMALLOC(MTYPE_NHRP_VC, sizeof(struct nhrp_vc)); + if (vc) { + *vc = (struct nhrp_vc) { + .local.nbma = key->local.nbma, + .remote.nbma = key->remote.nbma, + .notifier_list = NOTIFIER_LIST_INITIALIZER(&vc->notifier_list), + }; + } + + return vc; +} + +static void nhrp_vc_free(void *data) +{ + XFREE(MTYPE_NHRP_VC, data); +} + +struct nhrp_vc *nhrp_vc_get(const union sockunion *src, const union sockunion *dst, int create) +{ + struct nhrp_vc key; + key.local.nbma = *src; + key.remote.nbma = *dst; + return hash_get(nhrp_vc_hash, &key, create ? nhrp_vc_alloc : 0); +} + +static void nhrp_vc_check_delete(struct nhrp_vc *vc) +{ + if (vc->updating || vc->ipsec || notifier_active(&vc->notifier_list)) + return; + hash_release(nhrp_vc_hash, vc); + nhrp_vc_free(vc); +} + +static void nhrp_vc_update(struct nhrp_vc *vc, long cmd) +{ + vc->updating = 1; + notifier_call(&vc->notifier_list, cmd); + vc->updating = 0; + nhrp_vc_check_delete(vc); +} + +static void nhrp_vc_ipsec_reset(struct nhrp_vc *vc) +{ + vc->local.id[0] = 0; + vc->local.certlen = 0; + vc->remote.id[0] = 0; + vc->remote.certlen = 0; +} + +int nhrp_vc_ipsec_updown(uint32_t child_id, struct nhrp_vc *vc) +{ + char buf[2][SU_ADDRSTRLEN]; + struct child_sa *sa = NULL, *lsa; + uint32_t child_hash = child_id % ZEBRA_NUM_OF(childlist_head); + int abort_migration = 0; + + list_for_each_entry(lsa, &childlist_head[child_hash], childlist_entry) { + if (lsa->id == child_id) { + sa = lsa; + break; + } + } + + if (!sa) { + if (!vc) return 0; + + sa = XMALLOC(MTYPE_NHRP_VC, sizeof(struct child_sa)); + if (!sa) return 0; + + *sa = (struct child_sa) { + .id = child_id, + .childlist_entry = LIST_INITIALIZER(sa->childlist_entry), + .vc = NULL, + }; + list_add_tail(&sa->childlist_entry, &childlist_head[child_hash]); + } + + if (sa->vc == vc) + return 0; + + if (vc) { + /* Attach first to new VC */ + vc->ipsec++; + nhrp_vc_update(vc, NOTIFY_VC_IPSEC_CHANGED); + } + if (sa->vc && vc) { + /* Notify old VC of migration */ + sa->vc->abort_migration = 0; + debugf(NHRP_DEBUG_COMMON, "IPsec NBMA change of %s to %s", + sockunion2str(&sa->vc->remote.nbma, buf[0], sizeof buf[0]), + sockunion2str(&vc->remote.nbma, buf[1], sizeof buf[1])); + nhrp_vc_update(sa->vc, NOTIFY_VC_IPSEC_UPDATE_NBMA); + abort_migration = sa->vc->abort_migration; + } + if (sa->vc) { + /* Deattach old VC */ + sa->vc->ipsec--; + if (!sa->vc->ipsec) nhrp_vc_ipsec_reset(sa->vc); + nhrp_vc_update(sa->vc, NOTIFY_VC_IPSEC_CHANGED); + } + + /* Update */ + sa->vc = vc; + if (!vc) { + list_del(&sa->childlist_entry); + XFREE(MTYPE_NHRP_VC, sa); + } + + return abort_migration; +} + +void nhrp_vc_notify_add(struct nhrp_vc *vc, struct notifier_block *n, notifier_fn_t action) +{ + notifier_add(n, &vc->notifier_list, action); +} + +void nhrp_vc_notify_del(struct nhrp_vc *vc, struct notifier_block *n) +{ + notifier_del(n); + nhrp_vc_check_delete(vc); +} + + +struct nhrp_vc_iterator_ctx { + void (*cb)(struct nhrp_vc *, void *); + void *ctx; +}; + +static void nhrp_vc_iterator(struct hash_backet *b, void *ctx) +{ + struct nhrp_vc_iterator_ctx *ic = ctx; + ic->cb(b->data, ic->ctx); +} + +void nhrp_vc_foreach(void (*cb)(struct nhrp_vc *, void *), void *ctx) +{ + struct nhrp_vc_iterator_ctx ic = { + .cb = cb, + .ctx = ctx, + }; + hash_iterate(nhrp_vc_hash, nhrp_vc_iterator, &ic); +} + +void nhrp_vc_init(void) +{ + size_t i; + + nhrp_vc_hash = hash_create(nhrp_vc_key, nhrp_vc_cmp); + for (i = 0; i < ZEBRA_NUM_OF(childlist_head); i++) + list_init(&childlist_head[i]); +} + +void nhrp_vc_reset(void) +{ + struct child_sa *sa, *n; + size_t i; + + for (i = 0; i < ZEBRA_NUM_OF(childlist_head); i++) { + list_for_each_entry_safe(sa, n, &childlist_head[i], childlist_entry) + nhrp_vc_ipsec_updown(sa->id, 0); + } +} + +void nhrp_vc_terminate(void) +{ + nhrp_vc_reset(); + hash_clean(nhrp_vc_hash, nhrp_vc_free); +} diff --git a/nhrpd/nhrp_vty.c b/nhrpd/nhrp_vty.c new file mode 100644 index 000000000..91269f27e --- /dev/null +++ b/nhrpd/nhrp_vty.c @@ -0,0 +1,983 @@ +/* NHRP vty handling + * Copyright (c) 2014-2015 Timo Teräs + * + * This file is free software: you may copy, redistribute 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. + */ + +#include "zebra.h" +#include "command.h" +#include "zclient.h" +#include "stream.h" + +#include "nhrpd.h" +#include "netlink.h" + +static struct cmd_node zebra_node = { + .node = ZEBRA_NODE, + .prompt = "%s(config-router)# ", + .vtysh = 1, +}; + +static struct cmd_node nhrp_interface_node = { + .node = INTERFACE_NODE, + .prompt = "%s(config-if)# ", + .vtysh = 1, +}; + +#define NHRP_DEBUG_FLAGS_CMD "(all|common|event|interface|kernel|route|vici)" + +#define NHRP_DEBUG_FLAGS_STR \ + "All messages\n" \ + "Common messages (default)\n" \ + "Event manager messages\n" \ + "Interface messages\n" \ + "Kernel messages\n" \ + "Route messages\n" \ + "VICI messages\n" + +static const struct message debug_flags_desc[] = { + { NHRP_DEBUG_ALL, "all" }, + { NHRP_DEBUG_COMMON, "common" }, + { NHRP_DEBUG_IF, "interface" }, + { NHRP_DEBUG_KERNEL, "kernel" }, + { NHRP_DEBUG_ROUTE, "route" }, + { NHRP_DEBUG_VICI, "vici" }, + { NHRP_DEBUG_EVENT, "event" }, + { 0, NULL }, +}; + +static const struct message interface_flags_desc[] = { + { NHRP_IFF_SHORTCUT, "shortcut" }, + { NHRP_IFF_REDIRECT, "redirect" }, + { NHRP_IFF_REG_NO_UNIQUE, "registration no-unique" }, + { 0, NULL }, +}; + +static int nhrp_vty_return(struct vty *vty, int ret) +{ + static const char * const errmsgs[] = { + [NHRP_ERR_FAIL] = "Command failed", + [NHRP_ERR_NO_MEMORY] = "Out of memory", + [NHRP_ERR_UNSUPPORTED_INTERFACE] = "NHRP not supported on this interface", + [NHRP_ERR_NHRP_NOT_ENABLED] = "NHRP not enabled (set 'nhrp network-id' first)", + [NHRP_ERR_ENTRY_EXISTS] = "Entry exists already", + [NHRP_ERR_ENTRY_NOT_FOUND] = "Entry not found", + [NHRP_ERR_PROTOCOL_ADDRESS_MISMATCH] = "Protocol address family does not match command (ip/ipv6 mismatch)", + }; + const char *str = NULL; + char buf[256]; + + if (ret == NHRP_OK) + return CMD_SUCCESS; + + if (ret > 0 && ret <= (int)ZEBRA_NUM_OF(errmsgs)) + if (errmsgs[ret]) + str = errmsgs[ret]; + + if (!str) { + str = buf; + snprintf(buf, sizeof(buf), "Unknown error %d", ret); + } + + vty_out (vty, "%% %s%s", str, VTY_NEWLINE); + + return CMD_WARNING; +} + +static int toggle_flag( + struct vty *vty, const struct message *flag_desc, + const char *name, int on_off, unsigned *flags) +{ + int i; + + for (i = 0; flag_desc[i].str != NULL; i++) { + if (strcmp(flag_desc[i].str, name) != 0) + continue; + if (on_off) + *flags |= flag_desc[i].key; + else + *flags &= ~flag_desc[i].key; + return CMD_SUCCESS; + } + + vty_out(vty, "%% Invalid value %s%s", name, VTY_NEWLINE); + return CMD_WARNING; +} + +#ifndef NO_DEBUG + +DEFUN(show_debugging_nhrp, show_debugging_nhrp_cmd, + "show debugging nhrp", + SHOW_STR + "Debugging information\n" + "NHRP configuration\n") +{ + int i; + + vty_out(vty, "NHRP debugging status:%s", VTY_NEWLINE); + + for (i = 0; debug_flags_desc[i].str != NULL; i++) { + if (debug_flags_desc[i].key == NHRP_DEBUG_ALL) + continue; + if (!(debug_flags_desc[i].key & debug_flags)) + continue; + + vty_out(vty, " NHRP %s debugging is on%s", + debug_flags_desc[i].str, VTY_NEWLINE); + } + + return CMD_SUCCESS; +} + +DEFUN(debug_nhrp, debug_nhrp_cmd, + "debug nhrp " NHRP_DEBUG_FLAGS_CMD, + "Enable debug messages for specific or all parts.\n" + "NHRP information\n" + NHRP_DEBUG_FLAGS_STR) +{ + return toggle_flag(vty, debug_flags_desc, argv[0], 1, &debug_flags); +} + +DEFUN(no_debug_nhrp, no_debug_nhrp_cmd, + "no debug nhrp " NHRP_DEBUG_FLAGS_CMD, + NO_STR + "Disable debug messages for specific or all parts.\n" + "NHRP information\n" + NHRP_DEBUG_FLAGS_STR) +{ + return toggle_flag(vty, debug_flags_desc, argv[0], 0, &debug_flags); +} + +#endif /* NO_DEBUG */ + +static int nhrp_config_write(struct vty *vty) +{ +#ifndef NO_DEBUG + if (debug_flags == NHRP_DEBUG_ALL) { + vty_out(vty, "debug nhrp all%s", VTY_NEWLINE); + } else { + int i; + + for (i = 0; debug_flags_desc[i].str != NULL; i++) { + if (debug_flags_desc[i].key == NHRP_DEBUG_ALL) + continue; + if (!(debug_flags & debug_flags_desc[i].key)) + continue; + vty_out(vty, "debug nhrp %s%s", debug_flags_desc[i].str, VTY_NEWLINE); + } + } + vty_out(vty, "!%s", VTY_NEWLINE); +#endif /* NO_DEBUG */ + + if (nhrp_event_socket_path) { + vty_out(vty, "nhrp event socket %s%s", + nhrp_event_socket_path, VTY_NEWLINE); + } + if (netlink_nflog_group) { + vty_out(vty, "nhrp nflog-group %d%s", + netlink_nflog_group, VTY_NEWLINE); + } + + return 0; +} + +#define IP_STR "IP information\n" +#define IPV6_STR "IPv6 information\n" +#define AFI_CMD "(ip|ipv6)" +#define AFI_STR IP_STR IPV6_STR +#define NHRP_STR "Next Hop Resolution Protocol functions\n" + +static afi_t cmd_to_afi(const char *cmd) +{ + return strncmp(cmd, "ipv6", 4) == 0 ? AFI_IP6 : AFI_IP; +} + +static const char *afi_to_cmd(afi_t afi) +{ + if (afi == AFI_IP6) return "ipv6"; + return "ip"; +} + +DEFUN(nhrp_event_socket, nhrp_event_socket_cmd, + "nhrp event socket SOCKET", + NHRP_STR + "Event Manager commands\n" + "Event Manager unix socket path\n" + "Unix path for the socket\n") +{ + evmgr_set_socket(argv[0]); + return CMD_SUCCESS; +} + +DEFUN(no_nhrp_event_socket, no_nhrp_event_socket_cmd, + "no nhrp event socket [SOCKET]", + NO_STR + NHRP_STR + "Event Manager commands\n" + "Event Manager unix socket path\n" + "Unix path for the socket\n") +{ + evmgr_set_socket(NULL); + return CMD_SUCCESS; +} + +DEFUN(nhrp_nflog_group, nhrp_nflog_group_cmd, + "nhrp nflog-group <1-65535>", + NHRP_STR + "Specify NFLOG group number\n" + "NFLOG group number\n") +{ + uint32_t nfgroup; + + VTY_GET_INTEGER_RANGE("nflog-group", nfgroup, argv[0], 1, 65535); + netlink_set_nflog_group(nfgroup); + + return CMD_SUCCESS; +} + +DEFUN(no_nhrp_nflog_group, no_nhrp_nflog_group_cmd, + "no nhrp nflog-group [<1-65535>]", + NO_STR + NHRP_STR + "Specify NFLOG group number\n" + "NFLOG group number\n") +{ + netlink_set_nflog_group(0); + return CMD_SUCCESS; +} + +DEFUN(tunnel_protection, tunnel_protection_cmd, + "tunnel protection vici profile PROFILE {fallback-profile FALLBACK}", + "NHRP/GRE integration\n" + "IPsec protection\n" + "VICI (StrongSwan)\n" + "IPsec profile\n" + "IPsec profile name\n" + "Fallback IPsec profile\n" + "Fallback IPsec profile name\n") +{ + struct interface *ifp = vty->index; + + nhrp_interface_set_protection(ifp, argv[0], argv[1]); + return CMD_SUCCESS; +} + +DEFUN(no_tunnel_protection, no_tunnel_protection_cmd, + "no tunnel protection", + NO_STR + "NHRP/GRE integration\n" + "IPsec protection\n") +{ + struct interface *ifp = vty->index; + + nhrp_interface_set_protection(ifp, NULL, NULL); + return CMD_SUCCESS; +} + +DEFUN(tunnel_source, tunnel_source_cmd, + "tunnel source INTERFACE", + "NHRP/GRE integration\n" + "Tunnel device binding tracking\n" + "Interface name\n") +{ + struct interface *ifp = vty->index; + nhrp_interface_set_source(ifp, argv[0]); + return CMD_SUCCESS; +} + +DEFUN(no_tunnel_source, no_tunnel_source_cmd, + "no tunnel source", + "NHRP/GRE integration\n" + "Tunnel device binding tracking\n" + "Interface name\n") +{ + struct interface *ifp = vty->index; + nhrp_interface_set_source(ifp, NULL); + return CMD_SUCCESS; +} + +DEFUN(if_nhrp_network_id, if_nhrp_network_id_cmd, + AFI_CMD " nhrp network-id <1-4294967295>", + AFI_STR + NHRP_STR + "Enable NHRP and specify network-id\n" + "System local ID to specify interface group\n") +{ + struct interface *ifp = vty->index; + struct nhrp_interface *nifp = ifp->info; + afi_t afi = cmd_to_afi(argv[0]); + + VTY_GET_INTEGER_RANGE("network-id", nifp->afi[afi].network_id, argv[1], 1, 4294967295); + nhrp_interface_update(ifp); + + return CMD_SUCCESS; +} + +DEFUN(if_no_nhrp_network_id, if_no_nhrp_network_id_cmd, + "no " AFI_CMD " nhrp network-id [<1-4294967295>]", + NO_STR + AFI_STR + NHRP_STR + "Enable NHRP and specify network-id\n" + "System local ID to specify interface group\n") +{ + struct interface *ifp = vty->index; + struct nhrp_interface *nifp = ifp->info; + afi_t afi = cmd_to_afi(argv[0]); + + nifp->afi[afi].network_id = 0; + nhrp_interface_update(ifp); + + return CMD_SUCCESS; +} + +DEFUN(if_nhrp_flags, if_nhrp_flags_cmd, + AFI_CMD " nhrp (shortcut|redirect)", + AFI_STR + NHRP_STR + "Allow shortcut establishment\n" + "Send redirect notifications\n") +{ + struct interface *ifp = vty->index; + struct nhrp_interface *nifp = ifp->info; + afi_t afi = cmd_to_afi(argv[0]); + + return toggle_flag(vty, interface_flags_desc, argv[1], 1, &nifp->afi[afi].flags); +} + +DEFUN(if_no_nhrp_flags, if_no_nhrp_flags_cmd, + "no " AFI_CMD " nhrp (shortcut|redirect)", + NO_STR + AFI_STR + NHRP_STR + "Allow shortcut establishment\n" + "Send redirect notifications\n") +{ + struct interface *ifp = vty->index; + struct nhrp_interface *nifp = ifp->info; + afi_t afi = cmd_to_afi(argv[0]); + + return toggle_flag(vty, interface_flags_desc, argv[1], 0, &nifp->afi[afi].flags); +} + +DEFUN(if_nhrp_reg_flags, if_nhrp_reg_flags_cmd, + AFI_CMD " nhrp registration (no-unique)", + AFI_STR + NHRP_STR + "Registration configuration\n" + "Don't set unique flag\n") +{ + struct interface *ifp = vty->index; + struct nhrp_interface *nifp = ifp->info; + afi_t afi = cmd_to_afi(argv[0]); + char name[256]; + snprintf(name, sizeof(name), "registration %s", argv[1]); + return toggle_flag(vty, interface_flags_desc, name, 1, &nifp->afi[afi].flags); +} + +DEFUN(if_no_nhrp_reg_flags, if_no_nhrp_reg_flags_cmd, + "no " AFI_CMD " nhrp registration (no-unique)", + NO_STR + AFI_STR + NHRP_STR + "Registration configuration\n" + "Don't set unique flag\n") +{ + struct interface *ifp = vty->index; + struct nhrp_interface *nifp = ifp->info; + afi_t afi = cmd_to_afi(argv[0]); + char name[256]; + snprintf(name, sizeof(name), "registration %s", argv[1]); + return toggle_flag(vty, interface_flags_desc, name, 0, &nifp->afi[afi].flags); +} + +DEFUN(if_nhrp_holdtime, if_nhrp_holdtime_cmd, + AFI_CMD " nhrp holdtime <1-65000>", + AFI_STR + NHRP_STR + "Specify NBMA address validity time\n" + "Time in seconds that NBMA addresses are advertised valid\n") +{ + struct interface *ifp = vty->index; + struct nhrp_interface *nifp = ifp->info; + afi_t afi = cmd_to_afi(argv[0]); + + VTY_GET_INTEGER_RANGE("holdtime", nifp->afi[afi].holdtime, argv[1], 1, 65000); + nhrp_interface_update(ifp); + + return CMD_SUCCESS; +} + +DEFUN(if_no_nhrp_holdtime, if_no_nhrp_holdtime_cmd, + "no " AFI_CMD " nhrp holdtime [1-65000]", + NO_STR + AFI_STR + NHRP_STR + "Specify NBMA address validity time\n" + "Time in seconds that NBMA addresses are advertised valid\n") +{ + struct interface *ifp = vty->index; + struct nhrp_interface *nifp = ifp->info; + afi_t afi = cmd_to_afi(argv[0]); + + nifp->afi[afi].holdtime = NHRPD_DEFAULT_HOLDTIME; + nhrp_interface_update(ifp); + + return CMD_SUCCESS; +} + +DEFUN(if_nhrp_mtu, if_nhrp_mtu_cmd, + "ip nhrp mtu (<576-1500>|opennhrp)", + IP_STR + NHRP_STR + "Configure NHRP advertised MTU\n" + "MTU value\n" + "Advertise bound interface MTU similar to OpenNHRP") +{ + struct interface *ifp = vty->index; + struct nhrp_interface *nifp = ifp->info; + + if (argv[0][0] == 'o') { + nifp->afi[AFI_IP].configured_mtu = -1; + } else { + VTY_GET_INTEGER_RANGE("mtu", nifp->afi[AFI_IP].configured_mtu, argv[0], 576, 1500); + } + nhrp_interface_update_mtu(ifp, AFI_IP); + + return CMD_SUCCESS; +} + +DEFUN(if_no_nhrp_mtu, if_no_nhrp_mtu_cmd, + "no ip nhrp mtu [(<576-1500>|opennhrp)]", + NO_STR + IP_STR + NHRP_STR + "Configure NHRP advertised MTU\n" + "MTU value\n" + "Advertise bound interface MTU similar to OpenNHRP") +{ + struct interface *ifp = vty->index; + struct nhrp_interface *nifp = ifp->info; + + nifp->afi[AFI_IP].configured_mtu = 0; + nhrp_interface_update_mtu(ifp, AFI_IP); + return CMD_SUCCESS; +} + +DEFUN(if_nhrp_map, if_nhrp_map_cmd, + AFI_CMD " nhrp map (A.B.C.D|X:X::X:X) (A.B.C.D|local)", + AFI_STR + NHRP_STR + "Nexthop Server configuration\n" + "IPv4 protocol address\n" + "IPv6 protocol address\n" + "IPv4 NBMA address\n" + "Handle protocol address locally\n") +{ + struct interface *ifp = vty->index; + afi_t afi = cmd_to_afi(argv[0]); + union sockunion proto_addr, nbma_addr; + struct nhrp_cache *c; + + if (str2sockunion(argv[1], &proto_addr) < 0 || + afi2family(afi) != sockunion_family(&proto_addr)) + return nhrp_vty_return(vty, NHRP_ERR_PROTOCOL_ADDRESS_MISMATCH); + + c = nhrp_cache_get(ifp, &proto_addr, 1); + if (!c) + return nhrp_vty_return(vty, NHRP_ERR_FAIL); + + c->map = 1; + if (strcmp(argv[2], "local") == 0) { + nhrp_cache_update_binding(c, NHRP_CACHE_LOCAL, 0, NULL, 0, NULL); + } else{ + if (str2sockunion(argv[2], &nbma_addr) < 0) + return nhrp_vty_return(vty, NHRP_ERR_FAIL); + nhrp_cache_update_binding(c, NHRP_CACHE_STATIC, 0, + nhrp_peer_get(ifp, &nbma_addr), 0, NULL); + } + + return CMD_SUCCESS; +} + +DEFUN(if_no_nhrp_map, if_no_nhrp_map_cmd, + "no " AFI_CMD " nhrp map (A.B.C.D|X:X::X:X)", + NO_STR + AFI_STR + NHRP_STR + "Nexthop Server configuration\n" + "IPv4 protocol address\n" + "IPv6 protocol address\n") +{ + struct interface *ifp = vty->index; + afi_t afi = cmd_to_afi(argv[0]); + union sockunion proto_addr; + struct nhrp_cache *c; + + if (str2sockunion(argv[1], &proto_addr) < 0 || + afi2family(afi) != sockunion_family(&proto_addr)) + return nhrp_vty_return(vty, NHRP_ERR_PROTOCOL_ADDRESS_MISMATCH); + + c = nhrp_cache_get(ifp, &proto_addr, 0); + if (!c || !c->map) + return nhrp_vty_return(vty, NHRP_ERR_ENTRY_NOT_FOUND); + + nhrp_cache_update_binding(c, c->cur.type, -1, NULL, 0, NULL); + return CMD_SUCCESS; +} + +DEFUN(if_nhrp_nhs, if_nhrp_nhs_cmd, + AFI_CMD " nhrp nhs (A.B.C.D|X:X::X:X|dynamic) nbma (A.B.C.D|FQDN)", + AFI_STR + NHRP_STR + "Nexthop Server configuration\n" + "IPv4 protocol address\n" + "IPv6 protocol address\n" + "Automatic detection of protocol address\n" + "IPv4 NBMA address\n" + "Fully qualified domain name for NBMA address(es)\n") +{ + struct interface *ifp = vty->index; + afi_t afi = cmd_to_afi(argv[0]); + union sockunion proto_addr; + int ret; + + if (str2sockunion(argv[1], &proto_addr) < 0) + sockunion_family(&proto_addr) = AF_UNSPEC; + + ret = nhrp_nhs_add(ifp, afi, &proto_addr, argv[2]); + return nhrp_vty_return(vty, ret); +} + +DEFUN(if_no_nhrp_nhs, if_no_nhrp_nhs_cmd, + "no " AFI_CMD " nhrp nhs (A.B.C.D|X:X::X:X|dynamic) nbma (A.B.C.D|FQDN)", + NO_STR + AFI_STR + NHRP_STR + "Nexthop Server configuration\n" + "IPv4 protocol address\n" + "IPv6 protocol address\n" + "Automatic detection of protocol address\n" + "IPv4 NBMA address\n" + "Fully qualified domain name for NBMA address(es)\n") +{ + struct interface *ifp = vty->index; + afi_t afi = cmd_to_afi(argv[0]); + union sockunion proto_addr; + int ret; + + if (str2sockunion(argv[1], &proto_addr) < 0) + sockunion_family(&proto_addr) = AF_UNSPEC; + + ret = nhrp_nhs_del(ifp, afi, &proto_addr, argv[2]); + return nhrp_vty_return(vty, ret); +} + +struct info_ctx { + struct vty *vty; + afi_t afi; + int count; +}; + +static void show_ip_nhrp_cache(struct nhrp_cache *c, void *pctx) +{ + struct info_ctx *ctx = pctx; + struct vty *vty = ctx->vty; + char buf[2][SU_ADDRSTRLEN]; + + if (ctx->afi != family2afi(sockunion_family(&c->remote_addr))) + return; + + if (!ctx->count) { + vty_out(vty, "%-8s %-8s %-24s %-24s %-6s %s%s", + "Iface", + "Type", + "Protocol", + "NBMA", + "Flags", + "Identity", + VTY_NEWLINE); + } + ctx->count++; + + vty_out(ctx->vty, "%-8s %-8s %-24s %-24s %c%c%c %s%s", + c->ifp->name, + nhrp_cache_type_str[c->cur.type], + sockunion2str(&c->remote_addr, buf[0], sizeof buf[0]), + c->cur.peer ? sockunion2str(&c->cur.peer->vc->remote.nbma, buf[1], sizeof buf[1]) : "-", + c->used ? 'U' : ' ', + c->t_timeout ? 'T' : ' ', + c->t_auth ? 'A' : ' ', + c->cur.peer ? c->cur.peer->vc->remote.id : "-", + VTY_NEWLINE); +} + +static void show_ip_nhrp_nhs(struct nhrp_nhs *n, struct nhrp_registration *reg, void *pctx) +{ + struct info_ctx *ctx = pctx; + struct vty *vty = ctx->vty; + char buf[2][SU_ADDRSTRLEN]; + + if (!ctx->count) { + vty_out(vty, "%-8s %-24s %-16s %-16s%s", + "Iface", + "FQDN", + "NBMA", + "Protocol", + VTY_NEWLINE); + } + ctx->count++; + + vty_out(vty, "%-8s %-24s %-16s %-16s%s", + n->ifp->name, + n->nbma_fqdn, + (reg && reg->peer) ? sockunion2str(®->peer->vc->remote.nbma, buf[0], sizeof buf[0]) : "-", + sockunion2str(reg ? ®->proto_addr : &n->proto_addr, buf[1], sizeof buf[1]), + VTY_NEWLINE); +} + +static void show_ip_nhrp_shortcut(struct nhrp_shortcut *s, void *pctx) +{ + struct info_ctx *ctx = pctx; + struct nhrp_cache *c; + struct vty *vty = ctx->vty; + char buf1[PREFIX_STRLEN], buf2[SU_ADDRSTRLEN]; + + if (!ctx->count) { + vty_out(vty, "%-8s %-24s %-24s %s%s", + "Type", + "Prefix", + "Via", + "Identity", + VTY_NEWLINE); + } + ctx->count++; + + c = s->cache; + vty_out(ctx->vty, "%-8s %-24s %-24s %s%s", + nhrp_cache_type_str[s->type], + prefix2str(s->p, buf1, sizeof buf1), + c ? sockunion2str(&c->remote_addr, buf2, sizeof buf2) : "", + (c && c->cur.peer) ? c->cur.peer->vc->remote.id : "", + VTY_NEWLINE); +} + +static void show_ip_opennhrp_cache(struct nhrp_cache *c, void *pctx) +{ + struct info_ctx *ctx = pctx; + struct vty *vty = ctx->vty; + char buf[SU_ADDRSTRLEN]; + + if (ctx->afi != family2afi(sockunion_family(&c->remote_addr))) + return; + + vty_out(ctx->vty, + "Type: %s%s" + "Flags:%s%s%s" + "Protocol-Address: %s/%zu%s", + nhrp_cache_type_str[c->cur.type], + VTY_NEWLINE, + (c->cur.peer && c->cur.peer->online) ? " up": "", + c->used ? " used": "", + VTY_NEWLINE, + sockunion2str(&c->remote_addr, buf, sizeof buf), + 8 * family2addrsize(sockunion_family(&c->remote_addr)), + VTY_NEWLINE); + + if (c->cur.peer) { + vty_out(ctx->vty, + "NBMA-Address: %s%s", + sockunion2str(&c->cur.peer->vc->remote.nbma, buf, sizeof buf), + VTY_NEWLINE); + } + + if (sockunion_family(&c->cur.remote_nbma_natoa) != AF_UNSPEC) { + vty_out(ctx->vty, + "NBMA-NAT-OA-Address: %s%s", + sockunion2str(&c->cur.remote_nbma_natoa, buf, sizeof buf), + VTY_NEWLINE); + } + + vty_out(ctx->vty, "%s", VTY_NEWLINE); +} + +DEFUN(show_ip_nhrp, show_ip_nhrp_cmd, + "show " AFI_CMD " nhrp (cache|nhs|shortcut|opennhrp|)", + SHOW_STR + AFI_STR + "NHRP information\n" + "Forwarding cache information\n" + "Next hop server information\n" + "Shortcut information\n" + "opennhrpctl style cache dump\n") +{ + struct listnode *node; + struct interface *ifp; + struct info_ctx ctx = { + .vty = vty, + .afi = cmd_to_afi(argv[0]), + }; + + if (!argv[1] || argv[1][0] == 'c') { + for (ALL_LIST_ELEMENTS_RO(iflist, node, ifp)) + nhrp_cache_foreach(ifp, show_ip_nhrp_cache, &ctx); + } else if (argv[1][0] == 'n') { + for (ALL_LIST_ELEMENTS_RO(iflist, node, ifp)) + nhrp_nhs_foreach(ifp, ctx.afi, show_ip_nhrp_nhs, &ctx); + } else if (argv[1][0] == 's') { + nhrp_shortcut_foreach(ctx.afi, show_ip_nhrp_shortcut, &ctx); + } else { + vty_out(vty, "Status: ok%s%s", VTY_NEWLINE, VTY_NEWLINE); + ctx.count++; + for (ALL_LIST_ELEMENTS_RO(iflist, node, ifp)) + nhrp_cache_foreach(ifp, show_ip_opennhrp_cache, &ctx); + } + + if (!ctx.count) { + vty_out(vty, "%% No entries%s", VTY_NEWLINE); + return CMD_WARNING; + } + + return CMD_SUCCESS; +} + +static void show_dmvpn_entry(struct nhrp_vc *vc, void *ctx) +{ + struct vty *vty = ctx; + char buf[2][SU_ADDRSTRLEN]; + + vty_out(vty, "%-24s %-24s %c %-4d %-24s%s", + sockunion2str(&vc->local.nbma, buf[0], sizeof buf[0]), + sockunion2str(&vc->remote.nbma, buf[1], sizeof buf[1]), + notifier_active(&vc->notifier_list) ? 'n' : ' ', + vc->ipsec, + vc->remote.id, + VTY_NEWLINE); +} + +DEFUN(show_dmvpn, show_dmvpn_cmd, + "show dmvpn", + SHOW_STR + "DMVPN information\n") +{ + vty_out(vty, "%-24s %-24s %-6s %-4s %-24s%s", + "Src", + "Dst", + "Flags", + "SAs", + "Identity", + VTY_NEWLINE); + + nhrp_vc_foreach(show_dmvpn_entry, vty); + + return CMD_SUCCESS; +} + +static void clear_nhrp_cache(struct nhrp_cache *c, void *data) +{ + struct info_ctx *ctx = data; + if (c->cur.type <= NHRP_CACHE_CACHED) { + nhrp_cache_update_binding(c, c->cur.type, -1, NULL, 0, NULL); + ctx->count++; + } +} + +static void clear_nhrp_shortcut(struct nhrp_shortcut *s, void *data) +{ + struct info_ctx *ctx = data; + nhrp_shortcut_purge(s, 1); + ctx->count++; +} + +DEFUN(clear_nhrp, clear_nhrp_cmd, + "clear " AFI_CMD " nhrp (cache|shortcut)", + CLEAR_STR + AFI_STR + NHRP_STR + "Dynamic cache entries\n" + "Shortcut entries\n") +{ + struct listnode *node; + struct interface *ifp; + struct info_ctx ctx = { + .vty = vty, + .afi = cmd_to_afi(argv[0]), + .count = 0, + }; + + if (!argv[1] || argv[1][0] == 'c') { + for (ALL_LIST_ELEMENTS_RO(iflist, node, ifp)) + nhrp_cache_foreach(ifp, clear_nhrp_cache, &ctx); + } else { + nhrp_shortcut_foreach(ctx.afi, clear_nhrp_shortcut, &ctx); + } + + if (!ctx.count) { + vty_out(vty, "%% No entries%s", VTY_NEWLINE); + return CMD_WARNING; + } + + vty_out(vty, "%% %d entries cleared%s", ctx.count, VTY_NEWLINE); + return CMD_SUCCESS; +} + +struct write_map_ctx { + struct vty *vty; + int family; + const char *aficmd; +}; + +static void interface_config_write_nhrp_map(struct nhrp_cache *c, void *data) +{ + struct write_map_ctx *ctx = data; + struct vty *vty = ctx->vty; + char buf[2][SU_ADDRSTRLEN]; + + if (!c->map) return; + if (sockunion_family(&c->remote_addr) != ctx->family) return; + + vty_out(vty, " %s nhrp map %s %s%s", + ctx->aficmd, + sockunion2str(&c->remote_addr, buf[0], sizeof buf[0]), + c->cur.type == NHRP_CACHE_LOCAL ? "local" : + sockunion2str(&c->cur.peer->vc->remote.nbma, buf[1], sizeof buf[1]), + VTY_NEWLINE); +} + +static int interface_config_write(struct vty *vty) +{ + struct write_map_ctx mapctx; + struct listnode *node; + struct interface *ifp; + struct nhrp_interface *nifp; + struct nhrp_nhs *nhs; + const char *aficmd; + afi_t afi; + char buf[SU_ADDRSTRLEN]; + int i; + + for (ALL_LIST_ELEMENTS_RO(iflist, node, ifp)) { + vty_out(vty, "interface %s%s", ifp->name, VTY_NEWLINE); + if (ifp->desc) + vty_out(vty, " description %s%s", ifp->desc, VTY_NEWLINE); + + nifp = ifp->info; + if (nifp->ipsec_profile) { + vty_out(vty, " tunnel protection vici profile %s", + nifp->ipsec_profile); + if (nifp->ipsec_fallback_profile) + vty_out(vty, " fallback-profile %s", + nifp->ipsec_fallback_profile); + vty_out(vty, "%s", VTY_NEWLINE); + } + if (nifp->source) + vty_out(vty, " tunnel source %s%s", + nifp->source, VTY_NEWLINE); + + for (afi = 0; afi < AFI_MAX; afi++) { + struct nhrp_afi_data *ad = &nifp->afi[afi]; + + aficmd = afi_to_cmd(afi); + + if (ad->network_id) + vty_out(vty, " %s nhrp network-id %u%s", + aficmd, ad->network_id, + VTY_NEWLINE); + + if (ad->holdtime != NHRPD_DEFAULT_HOLDTIME) + vty_out(vty, " %s nhrp holdtime %u%s", + aficmd, ad->holdtime, + VTY_NEWLINE); + + if (ad->configured_mtu < 0) + vty_out(vty, " %s nhrp mtu opennhrp%s", + aficmd, VTY_NEWLINE); + else if (ad->configured_mtu) + vty_out(vty, " %s nhrp mtu %u%s", + aficmd, ad->configured_mtu, + VTY_NEWLINE); + + for (i = 0; interface_flags_desc[i].str != NULL; i++) { + if (!(ad->flags & interface_flags_desc[i].key)) + continue; + vty_out(vty, " %s nhrp %s%s", + aficmd, interface_flags_desc[i].str, VTY_NEWLINE); + } + + mapctx = (struct write_map_ctx) { + .vty = vty, + .family = afi2family(afi), + .aficmd = aficmd, + }; + nhrp_cache_foreach(ifp, interface_config_write_nhrp_map, &mapctx); + + list_for_each_entry(nhs, &ad->nhslist_head, nhslist_entry) { + vty_out(vty, " %s nhrp nhs %s nbma %s%s", + aficmd, + sockunion_family(&nhs->proto_addr) == AF_UNSPEC ? "dynamic" : sockunion2str(&nhs->proto_addr, buf, sizeof buf), + nhs->nbma_fqdn, + VTY_NEWLINE); + } + } + + vty_out (vty, "!%s", VTY_NEWLINE); + } + + return 0; +} + +void nhrp_config_init(void) +{ + install_node(&zebra_node, nhrp_config_write); + install_default(ZEBRA_NODE); + + /* global commands */ + install_element(VIEW_NODE, &show_debugging_nhrp_cmd); + install_element(VIEW_NODE, &show_ip_nhrp_cmd); + install_element(VIEW_NODE, &show_dmvpn_cmd); + install_element(ENABLE_NODE, &show_debugging_nhrp_cmd); + install_element(ENABLE_NODE, &show_ip_nhrp_cmd); + install_element(ENABLE_NODE, &show_dmvpn_cmd); + install_element(ENABLE_NODE, &clear_nhrp_cmd); + + install_element(ENABLE_NODE, &debug_nhrp_cmd); + install_element(ENABLE_NODE, &no_debug_nhrp_cmd); + + install_element(CONFIG_NODE, &debug_nhrp_cmd); + install_element(CONFIG_NODE, &no_debug_nhrp_cmd); + + install_element(CONFIG_NODE, &nhrp_event_socket_cmd); + install_element(CONFIG_NODE, &no_nhrp_event_socket_cmd); + install_element(CONFIG_NODE, &nhrp_nflog_group_cmd); + install_element(CONFIG_NODE, &no_nhrp_nflog_group_cmd); + + /* interface specific commands */ + install_node(&nhrp_interface_node, interface_config_write); + install_default(INTERFACE_NODE); + + install_element(CONFIG_NODE, &interface_cmd); + install_element(CONFIG_NODE, &no_interface_cmd); + install_element(INTERFACE_NODE, &interface_cmd); + install_element(INTERFACE_NODE, &no_interface_cmd); + install_element(INTERFACE_NODE, &tunnel_protection_cmd); + install_element(INTERFACE_NODE, &no_tunnel_protection_cmd); + install_element(INTERFACE_NODE, &tunnel_source_cmd); + install_element(INTERFACE_NODE, &no_tunnel_source_cmd); + install_element(INTERFACE_NODE, &if_nhrp_network_id_cmd); + install_element(INTERFACE_NODE, &if_no_nhrp_network_id_cmd); + install_element(INTERFACE_NODE, &if_nhrp_holdtime_cmd); + install_element(INTERFACE_NODE, &if_no_nhrp_holdtime_cmd); + install_element(INTERFACE_NODE, &if_nhrp_mtu_cmd); + install_element(INTERFACE_NODE, &if_no_nhrp_mtu_cmd); + install_element(INTERFACE_NODE, &if_nhrp_flags_cmd); + install_element(INTERFACE_NODE, &if_no_nhrp_flags_cmd); + install_element(INTERFACE_NODE, &if_nhrp_reg_flags_cmd); + install_element(INTERFACE_NODE, &if_no_nhrp_reg_flags_cmd); + install_element(INTERFACE_NODE, &if_nhrp_map_cmd); + install_element(INTERFACE_NODE, &if_no_nhrp_map_cmd); + install_element(INTERFACE_NODE, &if_nhrp_nhs_cmd); + install_element(INTERFACE_NODE, &if_no_nhrp_nhs_cmd); +} diff --git a/nhrpd/nhrpd.h b/nhrpd/nhrpd.h new file mode 100644 index 000000000..9222ad4e4 --- /dev/null +++ b/nhrpd/nhrpd.h @@ -0,0 +1,413 @@ +/* NHRP daemon internal structures and function prototypes + * Copyright (c) 2014-2015 Timo Teräs + * + * This file is free software: you may copy, redistribute 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. + */ + +#ifndef NHRPD_H +#define NHRPD_H + +#include "list.h" + +#include "zbuf.h" +#include "zclient.h" +#include "debug.h" + +#define NHRPD_DEFAULT_HOLDTIME 7200 + +#define NHRP_VTY_PORT 2612 +#define NHRP_DEFAULT_CONFIG "nhrpd.conf" + +extern struct thread_master *master; + +enum { + NHRP_OK = 0, + NHRP_ERR_FAIL, + NHRP_ERR_NO_MEMORY, + NHRP_ERR_UNSUPPORTED_INTERFACE, + NHRP_ERR_NHRP_NOT_ENABLED, + NHRP_ERR_ENTRY_EXISTS, + NHRP_ERR_ENTRY_NOT_FOUND, + NHRP_ERR_PROTOCOL_ADDRESS_MISMATCH, +}; + +struct notifier_block; + +typedef void (*notifier_fn_t)(struct notifier_block *, unsigned long); + +struct notifier_block { + struct list_head notifier_entry; + notifier_fn_t action; +}; + +struct notifier_list { + struct list_head notifier_head; +}; + +#define NOTIFIER_LIST_INITIALIZER(l) \ + { .notifier_head = LIST_INITIALIZER((l)->notifier_head) } + +static inline void notifier_init(struct notifier_list *l) +{ + list_init(&l->notifier_head); +} + +static inline void notifier_add(struct notifier_block *n, struct notifier_list *l, notifier_fn_t action) +{ + n->action = action; + list_add_tail(&n->notifier_entry, &l->notifier_head); +} + +static inline void notifier_del(struct notifier_block *n) +{ + list_del(&n->notifier_entry); +} + +static inline void notifier_call(struct notifier_list *l, int cmd) +{ + struct notifier_block *n, *nn; + list_for_each_entry_safe(n, nn, &l->notifier_head, notifier_entry) + n->action(n, cmd); +} + +static inline int notifier_active(struct notifier_list *l) +{ + return !list_empty(&l->notifier_head); +} + +struct resolver_query { + void (*callback)(struct resolver_query *, int n, union sockunion *); +}; + +void resolver_init(void); +void resolver_resolve(struct resolver_query *query, int af, const char *hostname, void (*cb)(struct resolver_query *, int, union sockunion *)); + +void nhrp_zebra_init(void); +void nhrp_zebra_terminate(void); + +struct zbuf; +struct nhrp_vc; +struct nhrp_cache; +struct nhrp_nhs; +struct nhrp_interface; + +#define MAX_ID_LENGTH 64 +#define MAX_CERT_LENGTH 2048 + +enum nhrp_notify_type { + NOTIFY_INTERFACE_UP, + NOTIFY_INTERFACE_DOWN, + NOTIFY_INTERFACE_CHANGED, + NOTIFY_INTERFACE_ADDRESS_CHANGED, + NOTIFY_INTERFACE_NBMA_CHANGED, + NOTIFY_INTERFACE_MTU_CHANGED, + + NOTIFY_VC_IPSEC_CHANGED, + NOTIFY_VC_IPSEC_UPDATE_NBMA, + + NOTIFY_PEER_UP, + NOTIFY_PEER_DOWN, + NOTIFY_PEER_IFCONFIG_CHANGED, + NOTIFY_PEER_MTU_CHANGED, + NOTIFY_PEER_NBMA_CHANGING, + + NOTIFY_CACHE_UP, + NOTIFY_CACHE_DOWN, + NOTIFY_CACHE_DELETE, + NOTIFY_CACHE_USED, + NOTIFY_CACHE_BINDING_CHANGE, +}; + +struct nhrp_vc { + struct notifier_list notifier_list; + uint8_t ipsec; + uint8_t updating; + uint8_t abort_migration; + + struct nhrp_vc_peer { + union sockunion nbma; + char id[MAX_ID_LENGTH]; + uint16_t certlen; + uint8_t cert[MAX_CERT_LENGTH]; + } local, remote; +}; + +enum nhrp_route_type { + NHRP_ROUTE_BLACKHOLE, + NHRP_ROUTE_LOCAL, + NHRP_ROUTE_NBMA_NEXTHOP, + NHRP_ROUTE_OFF_NBMA, +}; + +struct nhrp_peer { + unsigned int ref; + unsigned online : 1; + unsigned requested : 1; + unsigned fallback_requested : 1; + unsigned prio : 1; + struct notifier_list notifier_list; + struct interface *ifp; + struct nhrp_vc *vc; + struct thread *t_fallback; + struct notifier_block vc_notifier, ifp_notifier; +}; + +struct nhrp_packet_parser { + struct interface *ifp; + struct nhrp_afi_data *if_ad; + struct nhrp_peer *peer; + struct zbuf *pkt; + struct zbuf payload; + struct zbuf extensions; + struct nhrp_packet_header *hdr; + enum nhrp_route_type route_type; + struct prefix route_prefix; + union sockunion src_nbma, src_proto, dst_proto; +}; + +struct nhrp_reqid_pool { + struct hash *reqid_hash; + uint32_t next_request_id; +}; + +struct nhrp_reqid { + uint32_t request_id; + void (*cb)(struct nhrp_reqid *, void *); +}; + +extern struct nhrp_reqid_pool nhrp_packet_reqid; +extern struct nhrp_reqid_pool nhrp_event_reqid; + +enum nhrp_cache_type { + NHRP_CACHE_INVALID = 0, + NHRP_CACHE_INCOMPLETE, + NHRP_CACHE_NEGATIVE, + NHRP_CACHE_CACHED, + NHRP_CACHE_DYNAMIC, + NHRP_CACHE_NHS, + NHRP_CACHE_STATIC, + NHRP_CACHE_LOCAL, + NHRP_CACHE_NUM_TYPES +}; + +extern const char * const nhrp_cache_type_str[]; +extern unsigned long nhrp_cache_counts[NHRP_CACHE_NUM_TYPES]; + +struct nhrp_cache { + struct interface *ifp; + union sockunion remote_addr; + + unsigned map : 1; + unsigned used : 1; + unsigned route_installed : 1; + unsigned nhrp_route_installed : 1; + + struct notifier_block peer_notifier; + struct notifier_block newpeer_notifier; + struct notifier_list notifier_list; + struct nhrp_reqid eventid; + struct thread *t_timeout; + struct thread *t_auth; + + struct { + enum nhrp_cache_type type; + union sockunion remote_nbma_natoa; + struct nhrp_peer *peer; + time_t expires; + uint32_t mtu; + } cur, new; +}; + +struct nhrp_shortcut { + struct prefix *p; + union sockunion addr; + + struct nhrp_reqid reqid; + struct thread *t_timer; + + enum nhrp_cache_type type; + unsigned int holding_time; + unsigned route_installed : 1; + unsigned expiring : 1; + + struct nhrp_cache *cache; + struct notifier_block cache_notifier; +}; + +struct nhrp_nhs { + struct interface *ifp; + struct list_head nhslist_entry; + + unsigned hub : 1; + afi_t afi; + union sockunion proto_addr; + const char *nbma_fqdn; /* IP-address or FQDN */ + + struct thread *t_resolve; + struct resolver_query dns_resolve; + struct list_head reglist_head; +}; + +struct nhrp_registration { + struct list_head reglist_entry; + struct thread *t_register; + struct nhrp_nhs *nhs; + struct nhrp_reqid reqid; + unsigned int timeout; + unsigned mark : 1; + union sockunion proto_addr; + struct nhrp_peer *peer; + struct notifier_block peer_notifier; +}; + +#define NHRP_IFF_SHORTCUT 0x0001 +#define NHRP_IFF_REDIRECT 0x0002 +#define NHRP_IFF_REG_NO_UNIQUE 0x0100 + +struct nhrp_interface { + struct interface *ifp; + + unsigned enabled : 1; + + char *ipsec_profile, *ipsec_fallback_profile, *source; + union sockunion nbma; + union sockunion nat_nbma; + unsigned int linkidx; + uint32_t grekey; + + struct hash *peer_hash; + struct hash *cache_hash; + + struct notifier_list notifier_list; + + struct interface *nbmaifp; + struct notifier_block nbmanifp_notifier; + + struct nhrp_afi_data { + unsigned flags; + unsigned short configured : 1; + union sockunion addr; + uint32_t network_id; + short configured_mtu; + unsigned short mtu; + unsigned int holdtime; + struct list_head nhslist_head; + } afi[AFI_MAX]; +}; + +int sock_open_unix(const char *path); + +void nhrp_interface_init(void); +void nhrp_interface_update(struct interface *ifp); +void nhrp_interface_update_mtu(struct interface *ifp, afi_t afi); + +int nhrp_interface_add(int cmd, struct zclient *client, zebra_size_t length, vrf_id_t vrf_id); +int nhrp_interface_delete(int cmd, struct zclient *client, zebra_size_t length, vrf_id_t vrf_id); +int nhrp_interface_up(int cmd, struct zclient *client, zebra_size_t length, vrf_id_t vrf_id); +int nhrp_interface_down(int cmd, struct zclient *client, zebra_size_t length, vrf_id_t vrf_id); +int nhrp_interface_address_add(int cmd, struct zclient *client, zebra_size_t length, vrf_id_t vrf_id); +int nhrp_interface_address_delete(int cmd, struct zclient *client, zebra_size_t length, vrf_id_t vrf_id); + +void nhrp_interface_notify_add(struct interface *ifp, struct notifier_block *n, notifier_fn_t fn); +void nhrp_interface_notify_del(struct interface *ifp, struct notifier_block *n); +void nhrp_interface_set_protection(struct interface *ifp, const char *profile, const char *fallback_profile); +void nhrp_interface_set_source(struct interface *ifp, const char *ifname); + +int nhrp_nhs_add(struct interface *ifp, afi_t afi, union sockunion *proto_addr, const char *nbma_fqdn); +int nhrp_nhs_del(struct interface *ifp, afi_t afi, union sockunion *proto_addr, const char *nbma_fqdn); +int nhrp_nhs_free(struct nhrp_nhs *nhs); +void nhrp_nhs_terminate(void); +void nhrp_nhs_foreach(struct interface *ifp, afi_t afi, void (*cb)(struct nhrp_nhs *, struct nhrp_registration *, void *), void *ctx); + +void nhrp_route_update_nhrp(const struct prefix *p, struct interface *ifp); +void nhrp_route_announce(int add, enum nhrp_cache_type type, const struct prefix *p, struct interface *ifp, const union sockunion *nexthop, uint32_t mtu); +int nhrp_route_read(int command, struct zclient *zclient, zebra_size_t length, vrf_id_t vrf_id); +int nhrp_route_get_nexthop(const union sockunion *addr, struct prefix *p, union sockunion *via, struct interface **ifp); +enum nhrp_route_type nhrp_route_address(struct interface *in_ifp, union sockunion *addr, struct prefix *p, struct nhrp_peer **peer); + +void nhrp_config_init(void); + +void nhrp_shortcut_init(void); +void nhrp_shortcut_terminate(void); +void nhrp_shortcut_initiate(union sockunion *addr); +void nhrp_shortcut_foreach(afi_t afi, void (*cb)(struct nhrp_shortcut *, void *), void *ctx); +void nhrp_shortcut_purge(struct nhrp_shortcut *s, int force); +void nhrp_shortcut_prefix_change(const struct prefix *p, int deleted); + +struct nhrp_cache *nhrp_cache_get(struct interface *ifp, union sockunion *remote_addr, int create); +void nhrp_cache_foreach(struct interface *ifp, void (*cb)(struct nhrp_cache *, void *), void *ctx); +void nhrp_cache_set_used(struct nhrp_cache *, int); +int nhrp_cache_update_binding(struct nhrp_cache *, enum nhrp_cache_type type, int holding_time, struct nhrp_peer *p, uint32_t mtu, union sockunion *nbma_natoa); +void nhrp_cache_notify_add(struct nhrp_cache *c, struct notifier_block *, notifier_fn_t); +void nhrp_cache_notify_del(struct nhrp_cache *c, struct notifier_block *); + +void nhrp_vc_init(void); +void nhrp_vc_terminate(void); +struct nhrp_vc *nhrp_vc_get(const union sockunion *src, const union sockunion *dst, int create); +int nhrp_vc_ipsec_updown(uint32_t child_id, struct nhrp_vc *vc); +void nhrp_vc_notify_add(struct nhrp_vc *, struct notifier_block *, notifier_fn_t); +void nhrp_vc_notify_del(struct nhrp_vc *, struct notifier_block *); +void nhrp_vc_foreach(void (*cb)(struct nhrp_vc *, void *), void *ctx); +void nhrp_vc_reset(void); + +void vici_init(void); +void vici_terminate(void); +void vici_request_vc(const char *profile, union sockunion *src, union sockunion *dst, int prio); + +extern const char *nhrp_event_socket_path; + +void evmgr_init(void); +void evmgr_terminate(void); +void evmgr_set_socket(const char *socket); +void evmgr_notify(const char *name, struct nhrp_cache *c, void (*cb)(struct nhrp_reqid *, void *)); + +struct nhrp_packet_header *nhrp_packet_push( + struct zbuf *zb, uint8_t type, + const union sockunion *src_nbma, + const union sockunion *src_proto, + const union sockunion *dst_proto); +void nhrp_packet_complete(struct zbuf *zb, struct nhrp_packet_header *hdr); +uint16_t nhrp_packet_calculate_checksum(const uint8_t *pdu, uint16_t len); + +struct nhrp_packet_header *nhrp_packet_pull( + struct zbuf *zb, + union sockunion *src_nbma, + union sockunion *src_proto, + union sockunion *dst_proto); + +struct nhrp_cie_header *nhrp_cie_push( + struct zbuf *zb, uint8_t code, + const union sockunion *nbma, + const union sockunion *proto); +struct nhrp_cie_header *nhrp_cie_pull( + struct zbuf *zb, + struct nhrp_packet_header *hdr, + union sockunion *nbma, + union sockunion *proto); + +struct nhrp_extension_header *nhrp_ext_push(struct zbuf *zb, struct nhrp_packet_header *hdr, uint16_t type); +void nhrp_ext_complete(struct zbuf *zb, struct nhrp_extension_header *ext); +struct nhrp_extension_header *nhrp_ext_pull(struct zbuf *zb, struct zbuf *payload); +void nhrp_ext_request(struct zbuf *zb, struct nhrp_packet_header *hdr, struct interface *); +int nhrp_ext_reply(struct zbuf *zb, struct nhrp_packet_header *hdr, struct interface *ifp, struct nhrp_extension_header *ext, struct zbuf *extpayload); + +uint32_t nhrp_reqid_alloc(struct nhrp_reqid_pool *, struct nhrp_reqid *r, void (*cb)(struct nhrp_reqid *, void *)); +void nhrp_reqid_free(struct nhrp_reqid_pool *, struct nhrp_reqid *r); +struct nhrp_reqid *nhrp_reqid_lookup(struct nhrp_reqid_pool *, uint32_t reqid); + +int nhrp_packet_init(void); + +struct nhrp_peer *nhrp_peer_get(struct interface *ifp, const union sockunion *remote_nbma); +struct nhrp_peer *nhrp_peer_ref(struct nhrp_peer *p); +void nhrp_peer_unref(struct nhrp_peer *p); +int nhrp_peer_check(struct nhrp_peer *p, int establish); +void nhrp_peer_notify_add(struct nhrp_peer *p, struct notifier_block *, notifier_fn_t); +void nhrp_peer_notify_del(struct nhrp_peer *p, struct notifier_block *); +void nhrp_peer_recv(struct nhrp_peer *p, struct zbuf *zb); +void nhrp_peer_send(struct nhrp_peer *p, struct zbuf *zb); +void nhrp_peer_send_indication(struct interface *ifp, uint16_t, struct zbuf *); + +#endif diff --git a/nhrpd/os.h b/nhrpd/os.h new file mode 100644 index 000000000..0fbe8b003 --- /dev/null +++ b/nhrpd/os.h @@ -0,0 +1,5 @@ + +int os_socket(void); +int os_sendmsg(const uint8_t *buf, size_t len, int ifindex, const uint8_t *addr, size_t addrlen); +int os_recvmsg(uint8_t *buf, size_t *len, int *ifindex, uint8_t *addr, size_t *addrlen); +int os_configure_dmvpn(unsigned int ifindex, const char *ifname, int af); diff --git a/nhrpd/reqid.c b/nhrpd/reqid.c new file mode 100644 index 000000000..24b319939 --- /dev/null +++ b/nhrpd/reqid.c @@ -0,0 +1,49 @@ +#include "zebra.h" +#include "hash.h" +#include "nhrpd.h" + +static unsigned int nhrp_reqid_key(void *data) +{ + struct nhrp_reqid *r = data; + return r->request_id; +} + +static int nhrp_reqid_cmp(const void *data, const void *key) +{ + const struct nhrp_reqid *a = data, *b = key; + return a->request_id == b->request_id; +} + +uint32_t nhrp_reqid_alloc(struct nhrp_reqid_pool *p, struct nhrp_reqid *r, void (*cb)(struct nhrp_reqid *, void *)) +{ + if (!p->reqid_hash) { + p->reqid_hash = hash_create(nhrp_reqid_key, nhrp_reqid_cmp); + p->next_request_id = 1; + } + + if (r->cb != cb) { + r->request_id = p->next_request_id; + if (++p->next_request_id == 0) p->next_request_id = 1; + r->cb = cb; + hash_get(p->reqid_hash, r, hash_alloc_intern); + } + return r->request_id; +} + +void nhrp_reqid_free(struct nhrp_reqid_pool *p, struct nhrp_reqid *r) +{ + if (r->cb) { + hash_release(p->reqid_hash, r); + r->cb = NULL; + } +} + +struct nhrp_reqid *nhrp_reqid_lookup(struct nhrp_reqid_pool *p, uint32_t reqid) +{ + struct nhrp_reqid key; + if (!p->reqid_hash) return 0; + key.request_id = reqid; + return hash_lookup(p->reqid_hash, &key); +} + + diff --git a/nhrpd/resolver.c b/nhrpd/resolver.c new file mode 100644 index 000000000..07bdb735a --- /dev/null +++ b/nhrpd/resolver.c @@ -0,0 +1,190 @@ +/* C-Ares integration to Quagga mainloop + * Copyright (c) 2014-2015 Timo Teräs + * + * This file is free software: you may copy, redistribute 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. + */ + +#include +#include + +#include "vector.h" +#include "thread.h" +#include "nhrpd.h" + +struct resolver_state { + ares_channel channel; + struct thread *timeout; + vector read_threads, write_threads; +}; + +static struct resolver_state state; + +#define THREAD_RUNNING ((struct thread *)-1) + +static void resolver_update_timeouts(struct resolver_state *r); + +static int resolver_cb_timeout(struct thread *t) +{ + struct resolver_state *r = THREAD_ARG(t); + + r->timeout = THREAD_RUNNING; + ares_process(r->channel, NULL, NULL); + r->timeout = NULL; + resolver_update_timeouts(r); + + return 0; +} + +static int resolver_cb_socket_readable(struct thread *t) +{ + struct resolver_state *r = THREAD_ARG(t); + int fd = THREAD_FD(t); + + vector_set_index(r->read_threads, fd, THREAD_RUNNING); + ares_process_fd(r->channel, fd, ARES_SOCKET_BAD); + if (vector_lookup(r->read_threads, fd) == THREAD_RUNNING) { + t = NULL; + THREAD_READ_ON(master, t, resolver_cb_socket_readable, r, fd); + vector_set_index(r->read_threads, fd, t); + } + resolver_update_timeouts(r); + + return 0; +} + +static int resolver_cb_socket_writable(struct thread *t) +{ + struct resolver_state *r = THREAD_ARG(t); + int fd = THREAD_FD(t); + + vector_set_index(r->write_threads, fd, THREAD_RUNNING); + ares_process_fd(r->channel, ARES_SOCKET_BAD, fd); + if (vector_lookup(r->write_threads, fd) == THREAD_RUNNING) { + t = NULL; + THREAD_WRITE_ON(master, t, resolver_cb_socket_writable, r, fd); + vector_set_index(r->write_threads, fd, t); + } + resolver_update_timeouts(r); + + return 0; +} + +static void resolver_update_timeouts(struct resolver_state *r) +{ + struct timeval *tv, tvbuf; + + if (r->timeout == THREAD_RUNNING) return; + + THREAD_OFF(r->timeout); + tv = ares_timeout(r->channel, NULL, &tvbuf); + if (tv) { + unsigned int timeoutms = tv->tv_sec * 1000 + tv->tv_usec / 1000; + THREAD_TIMER_MSEC_ON(master, r->timeout, resolver_cb_timeout, r, timeoutms); + } +} + +static void ares_socket_cb(void *data, ares_socket_t fd, int readable, int writable) +{ + struct resolver_state *r = (struct resolver_state *) data; + struct thread *t; + + if (readable) { + t = vector_lookup_ensure(r->read_threads, fd); + if (!t) { + THREAD_READ_ON(master, t, resolver_cb_socket_readable, r, fd); + vector_set_index(r->read_threads, fd, t); + } + } else { + t = vector_lookup(r->read_threads, fd); + if (t) { + if (t != THREAD_RUNNING) { + THREAD_OFF(t); + } + vector_unset(r->read_threads, fd); + } + } + + if (writable) { + t = vector_lookup_ensure(r->write_threads, fd); + if (!t) { + THREAD_READ_ON(master, t, resolver_cb_socket_writable, r, fd); + vector_set_index(r->write_threads, fd, t); + } + } else { + t = vector_lookup(r->write_threads, fd); + if (t) { + if (t != THREAD_RUNNING) { + THREAD_OFF(t); + } + vector_unset(r->write_threads, fd); + } + } +} + +void resolver_init(void) +{ + struct ares_options ares_opts; + + state.read_threads = vector_init(1); + state.write_threads = vector_init(1); + + ares_opts = (struct ares_options) { + .sock_state_cb = &ares_socket_cb, + .sock_state_cb_data = &state, + .timeout = 2, + .tries = 3, + }; + + ares_init_options(&state.channel, &ares_opts, + ARES_OPT_SOCK_STATE_CB | ARES_OPT_TIMEOUT | + ARES_OPT_TRIES); +} + + +static void ares_address_cb(void *arg, int status, int timeouts, struct hostent *he) +{ + struct resolver_query *query = (struct resolver_query *) arg; + union sockunion addr[16]; + size_t i; + + if (status != ARES_SUCCESS) { + debugf(NHRP_DEBUG_COMMON, "[%p] Resolving failed", query); + query->callback(query, -1, NULL); + query->callback = NULL; + return; + } + + for (i = 0; he->h_addr_list[i] != NULL && i < ZEBRA_NUM_OF(addr); i++) { + memset(&addr[i], 0, sizeof(addr[i])); + addr[i].sa.sa_family = he->h_addrtype; + switch (he->h_addrtype) { + case AF_INET: + memcpy(&addr[i].sin.sin_addr, (uint8_t *) he->h_addr_list[i], he->h_length); + break; + case AF_INET6: + memcpy(&addr[i].sin6.sin6_addr, (uint8_t *) he->h_addr_list[i], he->h_length); + break; + } + } + + debugf(NHRP_DEBUG_COMMON, "[%p] Resolved with %d results", query, (int) i); + query->callback(query, i, &addr[0]); + query->callback = NULL; +} + +void resolver_resolve(struct resolver_query *query, int af, const char *hostname, void (*callback)(struct resolver_query *, int, union sockunion *)) +{ + if (query->callback != NULL) { + zlog_err("Trying to resolve '%s', but previous query was not finished yet", hostname); + return; + } + + debugf(NHRP_DEBUG_COMMON, "[%p] Resolving '%s'", query, hostname); + + query->callback = callback; + ares_gethostbyname(state.channel, hostname, af, ares_address_cb, query); + resolver_update_timeouts(&state); +} diff --git a/nhrpd/vici.c b/nhrpd/vici.c new file mode 100644 index 000000000..5491bacf7 --- /dev/null +++ b/nhrpd/vici.c @@ -0,0 +1,502 @@ +/* strongSwan VICI protocol implementation for NHRP + * Copyright (c) 2014-2015 Timo Teräs + * + * This file is free software: you may copy, redistribute 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. + */ + +#include +#include +#include + +#include "thread.h" +#include "zbuf.h" +#include "log.h" +#include "nhrpd.h" + +#include "vici.h" + +#define ERRNO_IO_RETRY(EN) (((EN) == EAGAIN) || ((EN) == EWOULDBLOCK) || ((EN) == EINTR)) + +struct blob { + char *ptr; + int len; +}; + +static int blob_equal(const struct blob *b, const char *str) +{ + if (b->len != (int) strlen(str)) return 0; + return memcmp(b->ptr, str, b->len) == 0; +} + +static int blob2buf(const struct blob *b, char *buf, size_t n) +{ + if (b->len >= (int) n) return 0; + memcpy(buf, b->ptr, b->len); + buf[b->len] = 0; + return 1; +} + +struct vici_conn { + struct thread *t_reconnect, *t_read, *t_write; + struct zbuf ibuf; + struct zbuf_queue obuf; + int fd; + uint8_t ibuf_data[VICI_MAX_MSGLEN]; +}; + +struct vici_message_ctx { + const char *sections[8]; + int nsections; +}; + +static int vici_reconnect(struct thread *t); +static void vici_submit_request(struct vici_conn *vici, const char *name, ...); + +static void vici_zbuf_puts(struct zbuf *obuf, const char *str) +{ + size_t len = strlen(str); + zbuf_put8(obuf, len); + zbuf_put(obuf, str, len); +} + +static void vici_connection_error(struct vici_conn *vici) +{ + nhrp_vc_reset(); + + THREAD_OFF(vici->t_read); + THREAD_OFF(vici->t_write); + zbuf_reset(&vici->ibuf); + zbufq_reset(&vici->obuf); + + close(vici->fd); + vici->fd = -1; + THREAD_TIMER_ON(master, vici->t_reconnect, vici_reconnect, vici, 2); +} + +static void vici_parse_message( + struct vici_conn *vici, struct zbuf *msg, + void (*parser)(struct vici_message_ctx *ctx, enum vici_type_t msgtype, const struct blob *key, const struct blob *val), + struct vici_message_ctx *ctx) +{ + uint8_t *type; + struct blob key; + struct blob val; + + while ((type = zbuf_may_pull(msg, uint8_t)) != NULL) { + switch (*type) { + case VICI_SECTION_START: + key.len = zbuf_get8(msg); + key.ptr = zbuf_pulln(msg, key.len); + debugf(NHRP_DEBUG_VICI, "VICI: Section start '%.*s'", key.len, key.ptr); + parser(ctx, *type, &key, NULL); + ctx->nsections++; + break; + case VICI_SECTION_END: + debugf(NHRP_DEBUG_VICI, "VICI: Section end"); + parser(ctx, *type, NULL, NULL); + ctx->nsections--; + break; + case VICI_KEY_VALUE: + key.len = zbuf_get8(msg); + key.ptr = zbuf_pulln(msg, key.len); + val.len = zbuf_get_be16(msg); + val.ptr = zbuf_pulln(msg, val.len); + debugf(NHRP_DEBUG_VICI, "VICI: Key '%.*s'='%.*s'", key.len, key.ptr, val.len, val.ptr); + parser(ctx, *type, &key, &val); + break; + case VICI_LIST_START: + key.len = zbuf_get8(msg); + key.ptr = zbuf_pulln(msg, key.len); + debugf(NHRP_DEBUG_VICI, "VICI: List start '%.*s'", key.len, key.ptr); + break; + case VICI_LIST_ITEM: + val.len = zbuf_get_be16(msg); + val.ptr = zbuf_pulln(msg, val.len); + debugf(NHRP_DEBUG_VICI, "VICI: List item: '%.*s'", val.len, val.ptr); + parser(ctx, *type, &key, &val); + break; + case VICI_LIST_END: + debugf(NHRP_DEBUG_VICI, "VICI: List end"); + break; + default: + debugf(NHRP_DEBUG_VICI, "VICI: Unsupported message component type %d", *type); + return; + } + } +} + +struct handle_sa_ctx { + struct vici_message_ctx msgctx; + int event; + int child_ok; + int kill_ikesa; + uint32_t child_uniqueid, ike_uniqueid; + struct { + union sockunion host; + struct blob id, cert; + } local, remote; +}; + +static void parse_sa_message( + struct vici_message_ctx *ctx, + enum vici_type_t msgtype, + const struct blob *key, const struct blob *val) +{ + struct handle_sa_ctx *sactx = container_of(ctx, struct handle_sa_ctx, msgctx); + struct nhrp_vc *vc; + char buf[512]; + + switch (msgtype) { + case VICI_SECTION_START: + if (ctx->nsections == 3) { + /* Begin of child-sa section, reset child vars */ + sactx->child_uniqueid = 0; + sactx->child_ok = 0; + } + break; + case VICI_SECTION_END: + if (ctx->nsections == 3) { + /* End of child-sa section, update nhrp_vc */ + int up = sactx->child_ok || sactx->event == 1; + if (up) { + vc = nhrp_vc_get(&sactx->local.host, &sactx->remote.host, up); + if (vc) { + blob2buf(&sactx->local.id, vc->local.id, sizeof(vc->local.id)); + if (blob2buf(&sactx->local.cert, (char*)vc->local.cert, sizeof(vc->local.cert))) + vc->local.certlen = sactx->local.cert.len; + blob2buf(&sactx->remote.id, vc->remote.id, sizeof(vc->remote.id)); + if (blob2buf(&sactx->remote.cert, (char*)vc->remote.cert, sizeof(vc->remote.cert))) + vc->remote.certlen = sactx->remote.cert.len; + sactx->kill_ikesa |= nhrp_vc_ipsec_updown(sactx->child_uniqueid, vc); + } + } else { + nhrp_vc_ipsec_updown(sactx->child_uniqueid, 0); + } + } + break; + default: + switch (key->ptr[0]) { + case 'l': + if (blob_equal(key, "local-host") && ctx->nsections == 1) { + if (blob2buf(val, buf, sizeof(buf))) + str2sockunion(buf, &sactx->local.host); + } else if (blob_equal(key, "local-id") && ctx->nsections == 1) { + sactx->local.id = *val; + } else if (blob_equal(key, "local-cert-data") && ctx->nsections == 1) { + sactx->local.cert = *val; + } + break; + case 'r': + if (blob_equal(key, "remote-host") && ctx->nsections == 1) { + if (blob2buf(val, buf, sizeof(buf))) + str2sockunion(buf, &sactx->remote.host); + } else if (blob_equal(key, "remote-id") && ctx->nsections == 1) { + sactx->remote.id = *val; + } else if (blob_equal(key, "remote-cert-data") && ctx->nsections == 1) { + sactx->remote.cert = *val; + } + break; + case 'u': + if (blob_equal(key, "uniqueid") && blob2buf(val, buf, sizeof(buf))) { + if (ctx->nsections == 3) + sactx->child_uniqueid = strtoul(buf, NULL, 0); + else if (ctx->nsections == 1) + sactx->ike_uniqueid = strtoul(buf, NULL, 0); + } + break; + case 's': + if (blob_equal(key, "state") && ctx->nsections == 3) { + sactx->child_ok = + (sactx->event == 0 && + (blob_equal(val, "INSTALLED") || + blob_equal(val, "REKEYED"))); + } + break; + } + break; + } +} + +static void parse_cmd_response( + struct vici_message_ctx *ctx, + enum vici_type_t msgtype, + const struct blob *key, const struct blob *val) +{ + char buf[512]; + + switch (msgtype) { + case VICI_KEY_VALUE: + if (blob_equal(key, "errmsg") && blob2buf(val, buf, sizeof(buf))) + zlog_err("VICI: strongSwan: %s", buf); + break; + default: + break; + } +} + +static void vici_recv_sa(struct vici_conn *vici, struct zbuf *msg, int event) +{ + char buf[32]; + struct handle_sa_ctx ctx = { + .event = event, + }; + + vici_parse_message(vici, msg, parse_sa_message, &ctx.msgctx); + + if (ctx.kill_ikesa && ctx.ike_uniqueid) { + debugf(NHRP_DEBUG_COMMON, "VICI: Deleting IKE_SA %u", ctx.ike_uniqueid); + snprintf(buf, sizeof buf, "%u", ctx.ike_uniqueid); + vici_submit_request( + vici, "terminate", + VICI_KEY_VALUE, "ike-id", strlen(buf), buf, + VICI_END); + } +} + +static void vici_recv_message(struct vici_conn *vici, struct zbuf *msg) +{ + uint32_t msglen; + uint8_t msgtype; + struct blob name; + + msglen = zbuf_get_be32(msg); + msgtype = zbuf_get8(msg); + debugf(NHRP_DEBUG_VICI, "VICI: Message %d, %d bytes", msgtype, msglen); + + switch (msgtype) { + case VICI_EVENT: + name.len = zbuf_get8(msg); + name.ptr = zbuf_pulln(msg, name.len); + + debugf(NHRP_DEBUG_VICI, "VICI: Event '%.*s'", name.len, name.ptr); + if (blob_equal(&name, "list-sa") || + blob_equal(&name, "child-updown") || + blob_equal(&name, "child-rekey")) + vici_recv_sa(vici, msg, 0); + else if (blob_equal(&name, "child-state-installed") || + blob_equal(&name, "child-state-rekeyed")) + vici_recv_sa(vici, msg, 1); + else if (blob_equal(&name, "child-state-destroying")) + vici_recv_sa(vici, msg, 2); + break; + case VICI_CMD_RESPONSE: + vici_parse_message(vici, msg, parse_cmd_response, 0); + break; + case VICI_EVENT_UNKNOWN: + case VICI_CMD_UNKNOWN: + zlog_err("VICI: StrongSwan does not support mandatory events (unpatched?)"); + break; + case VICI_EVENT_CONFIRM: + break; + default: + zlog_notice("VICI: Unrecognized message type %d", msgtype); + break; + } +} + +static int vici_read(struct thread *t) +{ + struct vici_conn *vici = THREAD_ARG(t); + struct zbuf *ibuf = &vici->ibuf; + struct zbuf pktbuf; + + vici->t_read = NULL; + if (zbuf_read(ibuf, vici->fd, (size_t) -1) < 0) { + vici_connection_error(vici); + return 0; + } + + /* Process all messages in buffer */ + do { + uint32_t *hdrlen = zbuf_may_pull(ibuf, uint32_t); + if (!hdrlen) + break; + if (!zbuf_may_pulln(ibuf, ntohl(*hdrlen))) { + zbuf_reset_head(ibuf, hdrlen); + break; + } + + /* Handle packet */ + zbuf_init(&pktbuf, hdrlen, htonl(*hdrlen)+4, htonl(*hdrlen)+4); + vici_recv_message(vici, &pktbuf); + } while (1); + + THREAD_READ_ON(master, vici->t_read, vici_read, vici, vici->fd); + return 0; +} + +static int vici_write(struct thread *t) +{ + struct vici_conn *vici = THREAD_ARG(t); + int r; + + vici->t_write = NULL; + r = zbufq_write(&vici->obuf, vici->fd); + if (r > 0) { + THREAD_WRITE_ON(master, vici->t_write, vici_write, vici, vici->fd); + } else if (r < 0) { + vici_connection_error(vici); + } + + return 0; +} + +static void vici_submit(struct vici_conn *vici, struct zbuf *obuf) +{ + if (vici->fd < 0) { + zbuf_free(obuf); + return; + } + + zbufq_queue(&vici->obuf, obuf); + THREAD_WRITE_ON(master, vici->t_write, vici_write, vici, vici->fd); +} + +static void vici_submit_request(struct vici_conn *vici, const char *name, ...) +{ + struct zbuf *obuf; + uint32_t *hdrlen; + va_list va; + size_t len; + int type; + + obuf = zbuf_alloc(256); + if (!obuf) return; + + hdrlen = zbuf_push(obuf, uint32_t); + zbuf_put8(obuf, VICI_CMD_REQUEST); + vici_zbuf_puts(obuf, name); + + va_start(va, name); + for (type = va_arg(va, int); type != VICI_END; type = va_arg(va, int)) { + zbuf_put8(obuf, type); + switch (type) { + case VICI_KEY_VALUE: + vici_zbuf_puts(obuf, va_arg(va, const char *)); + len = va_arg(va, size_t); + zbuf_put_be16(obuf, len); + zbuf_put(obuf, va_arg(va, void *), len); + break; + case VICI_END: + break; + default: + break; + } + } + va_end(va); + *hdrlen = htonl(zbuf_used(obuf) - 4); + vici_submit(vici, obuf); +} + +static void vici_register_event(struct vici_conn *vici, const char *name) +{ + struct zbuf *obuf; + uint32_t *hdrlen; + uint8_t namelen; + + namelen = strlen(name); + obuf = zbuf_alloc(4 + 1 + 1 + namelen); + if (!obuf) return; + + hdrlen = zbuf_push(obuf, uint32_t); + zbuf_put8(obuf, VICI_EVENT_REGISTER); + zbuf_put8(obuf, namelen); + zbuf_put(obuf, name, namelen); + *hdrlen = htonl(zbuf_used(obuf) - 4); + + vici_submit(vici, obuf); +} + +static int vici_reconnect(struct thread *t) +{ + struct vici_conn *vici = THREAD_ARG(t); + int fd; + + vici->t_reconnect = NULL; + if (vici->fd >= 0) return 0; + + fd = sock_open_unix("/var/run/charon.vici"); + if (fd < 0) { + zlog_warn("%s: failure connecting VICI socket: %s", + __PRETTY_FUNCTION__, strerror(errno)); + THREAD_TIMER_ON(master, vici->t_reconnect, vici_reconnect, vici, 2); + return 0; + } + + debugf(NHRP_DEBUG_COMMON, "VICI: Connected"); + vici->fd = fd; + THREAD_READ_ON(master, vici->t_read, vici_read, vici, vici->fd); + + /* Send event subscribtions */ + //vici_register_event(vici, "child-updown"); + //vici_register_event(vici, "child-rekey"); + vici_register_event(vici, "child-state-installed"); + vici_register_event(vici, "child-state-rekeyed"); + vici_register_event(vici, "child-state-destroying"); + vici_register_event(vici, "list-sa"); + vici_submit_request(vici, "list-sas", VICI_END); + + return 0; +} + +static struct vici_conn vici_connection; + +void vici_init(void) +{ + struct vici_conn *vici = &vici_connection; + + vici->fd = -1; + zbuf_init(&vici->ibuf, vici->ibuf_data, sizeof(vici->ibuf_data), 0); + zbufq_init(&vici->obuf); + THREAD_TIMER_MSEC_ON(master, vici->t_reconnect, vici_reconnect, vici, 10); +} + +void vici_terminate(void) +{ +} + +void vici_request_vc(const char *profile, union sockunion *src, union sockunion *dst, int prio) +{ + struct vici_conn *vici = &vici_connection; + char buf[2][SU_ADDRSTRLEN]; + + sockunion2str(src, buf[0], sizeof buf[0]); + sockunion2str(dst, buf[1], sizeof buf[1]); + + vici_submit_request( + vici, "initiate", + VICI_KEY_VALUE, "child", strlen(profile), profile, + VICI_KEY_VALUE, "timeout", (size_t) 2, "-1", + VICI_KEY_VALUE, "async", (size_t) 1, "1", + VICI_KEY_VALUE, "init-limits", (size_t) 1, prio ? "0" : "1", + VICI_KEY_VALUE, "my-host", strlen(buf[0]), buf[0], + VICI_KEY_VALUE, "other-host", strlen(buf[1]), buf[1], + VICI_END); +} + +int sock_open_unix(const char *path) +{ + int ret, fd; + struct sockaddr_un addr; + + fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (fd < 0) + return -1; + + memset(&addr, 0, sizeof (struct sockaddr_un)); + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, path, strlen (path)); + + ret = connect(fd, (struct sockaddr *) &addr, sizeof(addr.sun_family) + strlen(addr.sun_path)); + if (ret < 0) { + close(fd); + return -1; + } + + fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK); + + return fd; +} diff --git a/nhrpd/vici.h b/nhrpd/vici.h new file mode 100644 index 000000000..24b900b43 --- /dev/null +++ b/nhrpd/vici.h @@ -0,0 +1,24 @@ + +enum vici_type_t { + VICI_START = 0, + VICI_SECTION_START = 1, + VICI_SECTION_END = 2, + VICI_KEY_VALUE = 3, + VICI_LIST_START = 4, + VICI_LIST_ITEM = 5, + VICI_LIST_END = 6, + VICI_END = 7 +}; + +enum vici_operation_t { + VICI_CMD_REQUEST = 0, + VICI_CMD_RESPONSE, + VICI_CMD_UNKNOWN, + VICI_EVENT_REGISTER, + VICI_EVENT_UNREGISTER, + VICI_EVENT_CONFIRM, + VICI_EVENT_UNKNOWN, + VICI_EVENT, +}; + +#define VICI_MAX_MSGLEN (512*1024) diff --git a/nhrpd/zbuf.c b/nhrpd/zbuf.c new file mode 100644 index 000000000..ead7cfd29 --- /dev/null +++ b/nhrpd/zbuf.c @@ -0,0 +1,219 @@ +/* Stream/packet buffer API implementation + * Copyright (c) 2014-2015 Timo Teräs + * + * This file is free software: you may copy, redistribute 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. + */ + +#define _GNU_SOURCE +#include +#include +#include +#include "zassert.h" +#include "zbuf.h" +#include "memory.h" +#include "memtypes.h" +#include "nhrpd.h" + +#define ERRNO_IO_RETRY(EN) (((EN) == EAGAIN) || ((EN) == EWOULDBLOCK) || ((EN) == EINTR)) + +struct zbuf *zbuf_alloc(size_t size) +{ + struct zbuf *zb; + + zb = XMALLOC(MTYPE_STREAM_DATA, sizeof(*zb) + size); + if (!zb) + return NULL; + + zbuf_init(zb, zb+1, size, 0); + zb->allocated = 1; + + return zb; +} + +void zbuf_init(struct zbuf *zb, void *buf, size_t len, size_t datalen) +{ + *zb = (struct zbuf) { + .buf = buf, + .end = (uint8_t *)buf + len, + .head = buf, + .tail = (uint8_t *)buf + datalen, + }; +} + +void zbuf_free(struct zbuf *zb) +{ + if (zb->allocated) + XFREE(MTYPE_STREAM_DATA, zb); +} + +void zbuf_reset(struct zbuf *zb) +{ + zb->head = zb->tail = zb->buf; + zb->error = 0; +} + +void zbuf_reset_head(struct zbuf *zb, void *ptr) +{ + zassert((void*)zb->buf <= ptr && ptr <= (void*)zb->tail); + zb->head = ptr; +} + +static void zbuf_remove_headroom(struct zbuf *zb) +{ + ssize_t headroom = zbuf_headroom(zb); + if (!headroom) + return; + memmove(zb->buf, zb->head, zbuf_used(zb)); + zb->head -= headroom; + zb->tail -= headroom; +} + +ssize_t zbuf_read(struct zbuf *zb, int fd, size_t maxlen) +{ + ssize_t r; + + if (zb->error) + return -3; + + zbuf_remove_headroom(zb); + if (maxlen > zbuf_tailroom(zb)) + maxlen = zbuf_tailroom(zb); + + r = read(fd, zb->tail, maxlen); + if (r > 0) zb->tail += r; + else if (r == 0) r = -2; + else if (r < 0 && ERRNO_IO_RETRY(errno)) r = 0; + + return r; +} + +ssize_t zbuf_write(struct zbuf *zb, int fd) +{ + ssize_t r; + + if (zb->error) + return -3; + + r = write(fd, zb->head, zbuf_used(zb)); + if (r > 0) { + zb->head += r; + if (zb->head == zb->tail) + zbuf_reset(zb); + } + else if (r == 0) r = -2; + else if (r < 0 && ERRNO_IO_RETRY(errno)) r = 0; + + return r; +} + +ssize_t zbuf_recv(struct zbuf *zb, int fd) +{ + ssize_t r; + + if (zb->error) + return -3; + + zbuf_remove_headroom(zb); + r = recv(fd, zb->tail, zbuf_tailroom(zb), 0); + if (r > 0) zb->tail += r; + else if (r == 0) r = -2; + else if (r < 0 && ERRNO_IO_RETRY(errno)) r = 0; + return r; +} + +ssize_t zbuf_send(struct zbuf *zb, int fd) +{ + ssize_t r; + + if (zb->error) + return -3; + + r = send(fd, zb->head, zbuf_used(zb), 0); + if (r >= 0) + zbuf_reset(zb); + + return r; +} + +void *zbuf_may_pull_until(struct zbuf *zb, const char *sep, struct zbuf *msg) +{ + size_t seplen = strlen(sep), len; + uint8_t *ptr; + + ptr = memmem(zb->head, zbuf_used(zb), sep, seplen); + if (!ptr) return NULL; + + len = ptr - zb->head + seplen; + zbuf_init(msg, zbuf_pulln(zb, len), len, len); + return msg->head; +} + +void zbufq_init(struct zbuf_queue *zbq) +{ + *zbq = (struct zbuf_queue) { + .queue_head = LIST_INITIALIZER(zbq->queue_head), + }; +} + +void zbufq_reset(struct zbuf_queue *zbq) +{ + struct zbuf *buf, *bufn; + + list_for_each_entry_safe(buf, bufn, &zbq->queue_head, queue_list) { + list_del(&buf->queue_list); + zbuf_free(buf); + } +} + +void zbufq_queue(struct zbuf_queue *zbq, struct zbuf *zb) +{ + list_add_tail(&zb->queue_list, &zbq->queue_head); +} + +int zbufq_write(struct zbuf_queue *zbq, int fd) +{ + struct iovec iov[16]; + struct zbuf *zb, *zbn; + ssize_t r; + size_t iovcnt = 0; + + list_for_each_entry_safe(zb, zbn, &zbq->queue_head, queue_list) { + iov[iovcnt++] = (struct iovec) { + .iov_base = zb->head, + .iov_len = zbuf_used(zb), + }; + if (iovcnt >= ZEBRA_NUM_OF(iov)) + break; + } + + r = writev(fd, iov, iovcnt); + if (r < 0) + return r; + + list_for_each_entry_safe(zb, zbn, &zbq->queue_head, queue_list) { + if (r < (ssize_t)zbuf_used(zb)) { + zb->head += r; + return 1; + } + + r -= zbuf_used(zb); + list_del(&zb->queue_list); + zbuf_free(zb); + } + + return 0; +} + +void zbuf_copy(struct zbuf *zdst, struct zbuf *zsrc, size_t len) +{ + const void *src; + void *dst; + + dst = zbuf_pushn(zdst, len); + src = zbuf_pulln(zsrc, len); + if (!dst || !src) return; + memcpy(dst, src, len); +} diff --git a/nhrpd/zbuf.h b/nhrpd/zbuf.h new file mode 100644 index 000000000..73d707344 --- /dev/null +++ b/nhrpd/zbuf.h @@ -0,0 +1,189 @@ +/* Stream/packet buffer API + * Copyright (c) 2014-2015 Timo Teräs + * + * This file is free software: you may copy, redistribute 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. + */ + +#ifndef ZBUF_H +#define ZBUF_H + +#include +#include +#include +#include + +#include "zassert.h" +#include "list.h" + +struct zbuf { + struct list_head queue_list; + unsigned allocated : 1; + unsigned error : 1; + uint8_t *buf, *end; + uint8_t *head, *tail; +}; + +struct zbuf_queue { + struct list_head queue_head; +}; + +struct zbuf *zbuf_alloc(size_t size); +void zbuf_init(struct zbuf *zb, void *buf, size_t len, size_t datalen); +void zbuf_free(struct zbuf *zb); + +static inline size_t zbuf_size(struct zbuf *zb) +{ + return zb->end - zb->buf; +} + +static inline size_t zbuf_used(struct zbuf *zb) +{ + return zb->tail - zb->head; +} + +static inline size_t zbuf_tailroom(struct zbuf *zb) +{ + return zb->end - zb->tail; +} + +static inline size_t zbuf_headroom(struct zbuf *zb) +{ + return zb->head - zb->buf; +} + +void zbuf_reset(struct zbuf *zb); +void zbuf_reset_head(struct zbuf *zb, void *ptr); +ssize_t zbuf_read(struct zbuf *zb, int fd, size_t maxlen); +ssize_t zbuf_write(struct zbuf *zb, int fd); +ssize_t zbuf_recv(struct zbuf *zb, int fd); +ssize_t zbuf_send(struct zbuf *zb, int fd); + +static inline void zbuf_set_rerror(struct zbuf *zb) +{ + zb->error = 1; + zb->head = zb->tail; +} + +static inline void zbuf_set_werror(struct zbuf *zb) +{ + zb->error = 1; + zb->tail = zb->end; +} + +static inline void *__zbuf_pull(struct zbuf *zb, size_t size, int error) +{ + void *head = zb->head; + if (size > zbuf_used(zb)) { + if (error) zbuf_set_rerror(zb); + return NULL; + } + zb->head += size; + return head; +} + +#define zbuf_pull(zb, type) ((type *)__zbuf_pull(zb, sizeof(type), 1)) +#define zbuf_pulln(zb, sz) ((void *)__zbuf_pull(zb, sz, 1)) +#define zbuf_may_pull(zb, type) ((type *)__zbuf_pull(zb, sizeof(type), 0)) +#define zbuf_may_pulln(zb, sz) ((void *)__zbuf_pull(zb, sz, 0)) + +void *zbuf_may_pull_until(struct zbuf *zb, const char *sep, struct zbuf *msg); + +static inline void zbuf_get(struct zbuf *zb, void *dst, size_t len) +{ + void *src = zbuf_pulln(zb, len); + if (src) memcpy(dst, src, len); +} + +static inline uint8_t zbuf_get8(struct zbuf *zb) +{ + uint8_t *src = zbuf_pull(zb, uint8_t); + if (src) return *src; + return 0; +} + +static inline uint32_t zbuf_get32(struct zbuf *zb) +{ + struct unaligned32 { + uint32_t value; + } __attribute__((packed)); + + struct unaligned32 *v = zbuf_pull(zb, struct unaligned32); + if (v) return v->value; + return 0; +} + +static inline uint16_t zbuf_get_be16(struct zbuf *zb) +{ + struct unaligned16 { + uint16_t value; + } __attribute__((packed)); + + struct unaligned16 *v = zbuf_pull(zb, struct unaligned16); + if (v) return be16toh(v->value); + return 0; +} + +static inline uint32_t zbuf_get_be32(struct zbuf *zb) +{ + return be32toh(zbuf_get32(zb)); +} + +static inline void *__zbuf_push(struct zbuf *zb, size_t size, int error) +{ + void *tail = zb->tail; + if (size > zbuf_tailroom(zb)) { + if (error) zbuf_set_werror(zb); + return NULL; + } + zb->tail += size; + return tail; +} + +#define zbuf_push(zb, type) ((type *)__zbuf_push(zb, sizeof(type), 1)) +#define zbuf_pushn(zb, sz) ((void *)__zbuf_push(zb, sz, 1)) +#define zbuf_may_push(zb, type) ((type *)__zbuf_may_push(zb, sizeof(type), 0)) +#define zbuf_may_pushn(zb, sz) ((void *)__zbuf_push(zb, sz, 0)) + +static inline void zbuf_put(struct zbuf *zb, const void *src, size_t len) +{ + void *dst = zbuf_pushn(zb, len); + if (dst) memcpy(dst, src, len); +} + +static inline void zbuf_put8(struct zbuf *zb, uint8_t val) +{ + uint8_t *dst = zbuf_push(zb, uint8_t); + if (dst) *dst = val; +} + +static inline void zbuf_put_be16(struct zbuf *zb, uint16_t val) +{ + struct unaligned16 { + uint16_t value; + } __attribute__((packed)); + + struct unaligned16 *v = zbuf_push(zb, struct unaligned16); + if (v) v->value = htobe16(val); +} + +static inline void zbuf_put_be32(struct zbuf *zb, uint32_t val) +{ + struct unaligned32 { + uint32_t value; + } __attribute__((packed)); + + struct unaligned32 *v = zbuf_push(zb, struct unaligned32); + if (v) v->value = htobe32(val); +} + +void zbuf_copy(struct zbuf *zb, struct zbuf *src, size_t len); + +void zbufq_init(struct zbuf_queue *); +void zbufq_reset(struct zbuf_queue *); +void zbufq_queue(struct zbuf_queue *, struct zbuf *); +int zbufq_write(struct zbuf_queue *, int); + +#endif diff --git a/nhrpd/znl.c b/nhrpd/znl.c new file mode 100644 index 000000000..2216d97eb --- /dev/null +++ b/nhrpd/znl.c @@ -0,0 +1,160 @@ +/* Netlink helpers for zbuf + * Copyright (c) 2014-2015 Timo Teräs + * + * This file is free software: you may copy, redistribute 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "znl.h" + +#define ZNL_ALIGN(len) (((len)+3) & ~3) + +void *znl_push(struct zbuf *zb, size_t n) +{ + return zbuf_pushn(zb, ZNL_ALIGN(n)); +} + +void *znl_pull(struct zbuf *zb, size_t n) +{ + return zbuf_pulln(zb, ZNL_ALIGN(n)); +} + +struct nlmsghdr *znl_nlmsg_push(struct zbuf *zb, uint16_t type, uint16_t flags) +{ + struct nlmsghdr *n; + + n = znl_push(zb, sizeof(*n)); + if (!n) return NULL; + + *n = (struct nlmsghdr) { + .nlmsg_type = type, + .nlmsg_flags = flags, + }; + return n; +} + +void znl_nlmsg_complete(struct zbuf *zb, struct nlmsghdr *n) +{ + n->nlmsg_len = zb->tail - (uint8_t*)n; +} + +struct nlmsghdr *znl_nlmsg_pull(struct zbuf *zb, struct zbuf *payload) +{ + struct nlmsghdr *n; + size_t plen; + + n = znl_pull(zb, sizeof(*n)); + if (!n) return NULL; + + plen = n->nlmsg_len - sizeof(*n); + zbuf_init(payload, znl_pull(zb, plen), plen, plen); + zbuf_may_pulln(zb, ZNL_ALIGN(plen) - plen); + + return n; +} + +struct rtattr *znl_rta_push(struct zbuf *zb, uint16_t type, const void *val, size_t len) +{ + struct rtattr *rta; + uint8_t *dst; + + rta = znl_push(zb, ZNL_ALIGN(sizeof(*rta)) + ZNL_ALIGN(len)); + if (!rta) return NULL; + + *rta = (struct rtattr) { + .rta_type = type, + .rta_len = ZNL_ALIGN(sizeof(*rta)) + len, + }; + + dst = (uint8_t *)(rta+1); + memcpy(dst, val, len); + memset(dst+len, 0, ZNL_ALIGN(len) - len); + + return rta; +} + +struct rtattr *znl_rta_push_u32(struct zbuf *zb, uint16_t type, uint32_t val) +{ + return znl_rta_push(zb, type, &val, sizeof(val)); +} + +struct rtattr *znl_rta_nested_push(struct zbuf *zb, uint16_t type) +{ + struct rtattr *rta; + + rta = znl_push(zb, sizeof(*rta)); + if (!rta) return NULL; + + *rta = (struct rtattr) { + .rta_type = type, + }; + return rta; +} + +void znl_rta_nested_complete(struct zbuf *zb, struct rtattr *rta) +{ + size_t len = zb->tail - (uint8_t*) rta; + size_t align = ZNL_ALIGN(len) - len; + + if (align) { + void *dst = zbuf_pushn(zb, align); + if (dst) memset(dst, 0, align); + } + rta->rta_len = len; +} + +struct rtattr *znl_rta_pull(struct zbuf *zb, struct zbuf *payload) +{ + struct rtattr *rta; + size_t plen; + + rta = znl_pull(zb, sizeof(*rta)); + if (!rta) return NULL; + + if (rta->rta_len > sizeof(*rta)) { + plen = rta->rta_len - sizeof(*rta); + zbuf_init(payload, znl_pull(zb, plen), plen, plen); + } else { + zbuf_init(payload, NULL, 0, 0); + } + + return rta; +} + +int znl_open(int protocol, int groups) +{ + struct sockaddr_nl addr; + int fd, buf = 128 * 1024; + + fd = socket(AF_NETLINK, SOCK_RAW, protocol); + if (fd < 0) + return -1; + + fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK); + fcntl(fd, F_SETFD, FD_CLOEXEC); + if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &buf, sizeof(buf)) < 0) + goto error; + + memset(&addr, 0, sizeof(addr)); + addr.nl_family = AF_NETLINK; + addr.nl_groups = groups; + if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) + goto error; + + return fd; +error: + close(fd); + return -1; +} + diff --git a/nhrpd/znl.h b/nhrpd/znl.h new file mode 100644 index 000000000..2cd630b5d --- /dev/null +++ b/nhrpd/znl.h @@ -0,0 +1,29 @@ +/* Netlink helpers for zbuf + * Copyright (c) 2014-2015 Timo Teräs + * + * This file is free software: you may copy, redistribute 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. + */ + +#include "zbuf.h" + +#define ZNL_BUFFER_SIZE 8192 + +void *znl_push(struct zbuf *zb, size_t n); +void *znl_pull(struct zbuf *zb, size_t n); + +struct nlmsghdr *znl_nlmsg_push(struct zbuf *zb, uint16_t type, uint16_t flags); +void znl_nlmsg_complete(struct zbuf *zb, struct nlmsghdr *n); +struct nlmsghdr *znl_nlmsg_pull(struct zbuf *zb, struct zbuf *payload); + +struct rtattr *znl_rta_push(struct zbuf *zb, uint16_t type, const void *val, size_t len); +struct rtattr *znl_rta_push_u32(struct zbuf *zb, uint16_t type, uint32_t val); +struct rtattr *znl_rta_nested_push(struct zbuf *zb, uint16_t type); +void znl_rta_nested_complete(struct zbuf *zb, struct rtattr *rta); + +struct rtattr *znl_rta_pull(struct zbuf *zb, struct zbuf *payload); + +int znl_open(int protocol, int groups); + diff --git a/ospf6d/Makefile.am b/ospf6d/Makefile.am index 01bc6fe06..75f225727 100644 --- a/ospf6d/Makefile.am +++ b/ospf6d/Makefile.am @@ -1,11 +1,10 @@ ## Process this file with automake to produce Makefile.in. -INCLUDES = @INCLUDES@ -I.. -I$(top_srcdir) -I$(top_srcdir)/lib @SNMP_INCLUDES@ +AM_CPPFLAGS = -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib DEFS = @DEFS@ -DSYSCONFDIR=\"$(sysconfdir)/\" INSTALL_SDATA=@INSTALL@ -m 600 -AM_CFLAGS = $(PICFLAGS) -AM_LDFLAGS = $(PILDFLAGS) +AM_CFLAGS = $(WERROR) noinst_LIBRARIES = libospf6.a sbin_PROGRAMS = ospf6d diff --git a/ospf6d/OSPFv3-MIB.txt b/ospf6d/OSPFv3-MIB.txt index 80e6cb2d3..258f533ef 100644 --- a/ospf6d/OSPFv3-MIB.txt +++ b/ospf6d/OSPFv3-MIB.txt @@ -1,2758 +1,3951 @@ -OSPFV3-MIB DEFINITIONS ::= BEGIN - -IMPORTS - MODULE-IDENTITY, OBJECT-TYPE, mib-2, experimental, - Counter32, Gauge32, Integer32, IpAddress, - Unsigned32 - FROM SNMPv2-SMI - TEXTUAL-CONVENTION, TruthValue, StorageType, RowStatus - FROM SNMPv2-TC - MODULE-COMPLIANCE, OBJECT-GROUP - FROM SNMPv2-CONF - InterfaceIndex - FROM IF-MIB - InetAddressType, InetAddress, InetAddressPrefixLength - FROM INET-ADDRESS-MIB - AreaID, RouterID, Metric, BigMetric, Status, - HelloRange, DesignatedRouterPriority - FROM OSPF-MIB; - -ospfv3MIB MODULE-IDENTITY - LAST-UPDATED "200404081200Z" - ORGANIZATION "IETF OSPF Working Group" - CONTACT-INFO - "WG E-Mail: ospf@peach.ease.lsoft.com - WG Chairs: John.Moy@sycamorenet.com - acee@redback.com - rohit@xebeo.com - - Dan Joyal - Nortel Networks - 600 Technology Park Drive - Billerica, MA 01821, USA - djoyal@nortelnetworks.com - - Vishwas Manral - SiNett Corporation - 2/1, First Floor - Embassy Icon Annex - Infantry Road - Bangalore 560001 - vishwas@sinett.com" - - DESCRIPTION - "The MIB module to describe OSPF version 3" - REVISION "200404081200Z" - DESCRIPTION -- RFC Editor assigns RFC xxxx - "Initial version, published as RFC xxxx" - ::= { experimental 102 } -- IANA assigns xx - --- Texual conventions - -UpToRefreshInterval ::= TEXTUAL-CONVENTION - STATUS current - DESCRIPTION - "The values one might be able to configure for - variables bounded by the Refresh Interval" - SYNTAX Integer32 (1..1800) - -RouterDeadRange ::= TEXTUAL-CONVENTION - STATUS current - DESCRIPTION - "The range of intervals in seconds that a routers hello - must have not been seen before a neighbor declares the - router down" - SYNTAX Integer32 (1..'FFFF'h) - - --- Top-level structure of MIB -ospfv3Objects OBJECT IDENTIFIER ::= { ospfv3MIB 1 } -ospfv3Conformance OBJECT IDENTIFIER ::= { ospfv3MIB 2 } - --- OSPFv3 General Variables - --- These parameters apply globally to the Router's --- OSPFv3 Process. - -ospfv3GeneralGroup OBJECT IDENTIFIER ::= { ospfv3Objects 1 } - -ospfv3RouterId OBJECT-TYPE - SYNTAX RouterID - MAX-ACCESS read-write - STATUS current - DESCRIPTION - "A 32-bit integer uniquely identifying the - router in the Autonomous System. - To ensure uniqueness, this may - default to the value of one of the - router's IPv4 interface addresses if IPv4 is - configured on the router." - ::= { ospfv3GeneralGroup 1 } - -ospfv3AdminStat OBJECT-TYPE - SYNTAX Status - MAX-ACCESS read-write - STATUS current - DESCRIPTION - "The administrative status of OSPFv3 in the - router. The value 'enabled' denotes that the - OSPFv3 Process is active on at least one inter- - face; 'disabled' disables it on all inter- - faces." - ::= { ospfv3GeneralGroup 2 } - -ospfv3VersionNumber OBJECT-TYPE - SYNTAX INTEGER { version3(3) } - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The version number of OSPF for IPv6 is 3." - ::= { ospfv3GeneralGroup 3 } - -ospfv3AreaBdrRtrStatus OBJECT-TYPE - SYNTAX TruthValue - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "A flag to note whether this router is an area - border router." - REFERENCE - "OSPF Version 2, Section 3 Splitting the AS into - Areas" - ::= { ospfv3GeneralGroup 4 } - -ospfv3ASBdrRtrStatus OBJECT-TYPE - SYNTAX TruthValue - MAX-ACCESS read-write - STATUS current - DESCRIPTION - "A flag to note whether this router is config- - ured as an Autonomous System border router." - REFERENCE - "OSPF Version 2, Section 3.3 Classification of - routers" - ::= { ospfv3GeneralGroup 5 } - -ospfv3AsScopeLsaCount OBJECT-TYPE - SYNTAX Gauge32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The number of AS-Scope (e.g. AS-External) link-state - advertisements in the link-state database." - ::= { ospfv3GeneralGroup 6 } - -ospfv3AsScopeLsaCksumSum OBJECT-TYPE - SYNTAX Integer32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The 32-bit unsigned sum of the LS checksums of - the AS-scoped link-state advertisements con- - tained in the link-state database. This sum - can be used to determine if there has been a - change in a router's link state database, and - to compare the link-state database of two - routers." - ::= { ospfv3GeneralGroup 7 } - -ospfv3OriginateNewLsas OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The number of new link-state advertisements - that have been originated. This number is in- - cremented each time the router originates a new - LSA." - ::= { ospfv3GeneralGroup 8 } - -ospfv3RxNewLsas OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The number of link-state advertisements re- - ceived determined to be new instantiations. - This number does not include newer instantia- - tions of self-originated link-state advertise- - ments." - ::= { ospfv3GeneralGroup 9 } - -ospfv3ExtLsaCount OBJECT-TYPE - SYNTAX Gauge32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The number of External(LS type 0x4005) in the link- - state database" - ::= { ospfv3GeneralGroup 10 } - - -ospfv3ExtAreaLsdbLimit OBJECT-TYPE - SYNTAX Integer32 (-1..'7FFFFFFF'h) - MAX-ACCESS read-write - STATUS current - DESCRIPTION - "The maximum number of non-default AS- - external-LSAs entries that can be stored in the - link-state database. If the value is -1, then - there is no limit. - - When the number of non-default AS-external-LSAs - in a router's link-state database reaches - ospfv3ExtAreaLsdbLimit, the router enters Overflow- - State. The router never holds more than - ospfv3ExtAreaLsdbLimit non-default AS-external-LSAs - in its database. OspfExtAreaLsdbLimit MUST be set - identically in all routers attached to the OSPFv3 - backbone and/or any regular OSPFv3 area. (i.e., - OSPFv3 stub areas and NSSAs are excluded)." - ::= { ospfv3GeneralGroup 11 } - -ospfv3MulticastExtensions OBJECT-TYPE - SYNTAX BITS { - intraAreaMulticast(0), - interAreaMulticast(1), - interAsMulticast(2) - } - - MAX-ACCESS read-write - STATUS current - DESCRIPTION - "A Bit Mask indicating whether the router is - forwarding IPv6 multicast datagrams - based on the algorithms defined in the Multi- - cast Extensions to OSPF. - - If intraAreaMulticast set, indicates that the router - can forward IPv6 multicast datagrams in the router's - directly attached areas (called intra-area mul- - ticast routing). - - If interAreaMulticast set, indicates that the router - can forward IPv6 multicast datagrams between OSPFv3 - areas (called inter-area multicast routing). - - If interAsMulticast set, indicates that the router can - forward IPv6 multicast datagrams between Auto- - nomous Systems (called inter-AS multicast rout- - ing). - - Only certain combinations of bit settings are - allowed, namely: - - All bits cleared (no multicasting) - - intraAreaMulticast only, - - intraAreaMulticast and interAreaMulticast, - - intraAreaMulticast and interAsMulticast - - intraAreaMulticast, interAreaMulticast and - interAsMulticast - By default, all bits are cleared." - ::= { ospfv3GeneralGroup 12 } - -ospfv3ExitOverflowInterval OBJECT-TYPE - SYNTAX Unsigned32 - UNITS "seconds" - MAX-ACCESS read-write - STATUS current - DESCRIPTION - "The number of seconds that, after entering - OverflowState, a router will attempt to leave - OverflowState. This allows the router to again - originate non-default AS-External-LSAs. When - set to 0, the router will not leave Overflow- - State until restarted." - ::= { ospfv3GeneralGroup 13 } - -ospfv3DemandExtensions OBJECT-TYPE - SYNTAX TruthValue - MAX-ACCESS read-write - STATUS current - DESCRIPTION - "The router's support for demand routing." - REFERENCE - "OSPF Version 2, Appendix on Demand Routing" - ::= { ospfv3GeneralGroup 14 } - -ospfv3TrafficEngineeringSupport OBJECT-TYPE - SYNTAX TruthValue - MAX-ACCESS read-write - STATUS current - DESCRIPTION - "The router's support for traffic engineering - extensions." - ::= { ospfv3GeneralGroup 15 } - -ospfv3ReferenceBandwidth OBJECT-TYPE - SYNTAX Unsigned32 - MAX-ACCESS read-write - STATUS current - DESCRIPTION - "Reference bandwidth in kilobits/second for - calculating default interface metrics. The - default value is 100,000 KBPS (100 MBPS)" - ::= { ospfv3GeneralGroup 16 } - -ospfv3RestartSupport OBJECT-TYPE - SYNTAX INTEGER { none (1), - plannedOnly (2), - plannedAndUnplanned (3) - } - MAX-ACCESS read-write - STATUS current - DESCRIPTION - "The router's support for OSPF hitless restart. - Options include: no restart support, only planned - restarts or both planned and unplanned restarts." - ::= { ospfv3GeneralGroup 17 } - -ospfv3RestartInterval OBJECT-TYPE - SYNTAX UpToRefreshInterval - UNITS "seconds" - MAX-ACCESS read-write - STATUS current - DESCRIPTION - "Configured OSPF hitless restart timeout interval." - ::= { ospfv3GeneralGroup 18 } - -ospfv3RestartStatus OBJECT-TYPE - SYNTAX INTEGER { notRestarting (1), - plannedRestart (2), - unplannedRestart (3) - } - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Current status of OSPF hitless restart." - ::= { ospfv3GeneralGroup 19 } - -ospfv3RestartAge OBJECT-TYPE - SYNTAX UpToRefreshInterval - UNITS "seconds" - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Remaining time in current OSPF hitless restart - interval." - ::= { ospfv3GeneralGroup 20 } - -ospfv3RestartExitReason OBJECT-TYPE - SYNTAX INTEGER { none (1), -- none attempted - inProgress (2), -- restart in - -- progress - completed (3), -- successfully - -- completed - timedOut (4), -- timed out - topologyChanged (5) -- aborted due to - -- topologychange. - } - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Describes the outcome of the last attempt at a - hitless restart. If the value is 'none', no restart - has yet been attempted. If the value is 'inProgress', - a restart attempt is currently underway." - ::= { ospfv3GeneralGroup 21 } - - --- The OSPFv3 Area Data Structure contains information --- regarding the various areas. The interfaces and --- virtual links are configured as part of these areas. --- Area 0.0.0.0, by definition, is the Backbone Area - -ospfv3AreaTable OBJECT-TYPE - SYNTAX SEQUENCE OF Ospfv3AreaEntry - MAX-ACCESS not-accessible - STATUS current - DESCRIPTION - "Information describing the configured parame- - ters and cumulative statistics of the router's - attached areas." - REFERENCE - "OSPF Version 2, Section 6 The Area Data Struc- - ture" - ::= { ospfv3Objects 2 } - -ospfv3AreaEntry OBJECT-TYPE - SYNTAX Ospfv3AreaEntry - MAX-ACCESS not-accessible - STATUS current - DESCRIPTION - "Information describing the configured parame- - ters and cumulative statistics of one of the - router's attached areas." - INDEX { ospfv3AreaId } - ::= { ospfv3AreaTable 1 } - -Ospfv3AreaEntry ::= SEQUENCE { - ospfv3AreaId - AreaID, - ospfv3ImportAsExtern - INTEGER, - ospfv3AreaSpfRuns - Counter32, - ospfv3AreaBdrRtrCount - Gauge32, - ospfv3AreaAsBdrRtrCount - Gauge32, - ospfv3AreaScopeLsaCount - Gauge32, - ospfv3AreaScopeLsaCksumSum - Integer32, - ospfv3AreaSummary - INTEGER, - ospfv3AreaStatus - RowStatus, - ospfv3StubMetric - BigMetric, - ospfv3AreaNssaTranslatorRole - INTEGER, - ospfv3AreaNssaTranslatorState - INTEGER, - ospfv3AreaNssaTranslatorStabilityInterval - Unsigned32, - ospfv3AreaNssaTranslatorEvents - Counter32, - ospfv3AreaStubMetricType - INTEGER - } - -ospfv3AreaId OBJECT-TYPE - SYNTAX AreaID - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "A 32-bit integer uniquely identifying an area. - Area ID 0.0.0.0 is used for the OSPFv3 backbone." - REFERENCE - "OSPF Version 2, Appendix C.2 Area parameters" - ::= { ospfv3AreaEntry 1 } - -ospfv3ImportAsExtern OBJECT-TYPE - SYNTAX INTEGER { - importExternal(1), -- normal area - importNoExternal(2), -- stub area - importNssa(3) -- not-so-stubby-area - } - MAX-ACCESS read-create - STATUS current - DESCRIPTION - "Indicates whether an area is a Stub area, NSSA, or - standard area. AS-scope LSAs are not imported into Stub - Areas or NSSAs. NSSAs import AS-External data as Type-7 - LSAs which have Area-scope" - REFERENCE - "OSPF Version 2, Appendix C.2 Area parameters" - DEFVAL { importExternal } - ::= { ospfv3AreaEntry 2 } - -ospfv3AreaSpfRuns OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The number of times that the intra-area route - table has been calculated using this area's - link-state database. This is typically done - using Dijkstra's algorithm." - ::= { ospfv3AreaEntry 3 } - -ospfv3AreaBdrRtrCount OBJECT-TYPE - SYNTAX Gauge32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The total number of area border routers reach- - able within this area. This is initially zero, - and is calculated in each SPF Pass." - ::= { ospfv3AreaEntry 4 } - -ospfv3AreaAsBdrRtrCount OBJECT-TYPE - SYNTAX Gauge32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The total number of Autonomous System border - routers reachable within this area. This is - initially zero, and is calculated in each SPF - Pass." - ::= { ospfv3AreaEntry 5 } - -ospfv3AreaScopeLsaCount OBJECT-TYPE - SYNTAX Gauge32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The total number of Area-Scope link-state - advertisements in this area's link-state - database." - ::= { ospfv3AreaEntry 6 } - -ospfv3AreaScopeLsaCksumSum OBJECT-TYPE - SYNTAX Integer32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The 32-bit unsigned sum of the Area-Scope link-state - advertisements' LS checksums contained in this - area's link-state database. The sum can be used - to determine if there has been a change in a - router's link state database, and to compare the - link-state database of two routers." - ::= { ospfv3AreaEntry 7 } - -ospfv3AreaSummary OBJECT-TYPE - SYNTAX INTEGER { - noAreaSummary(1), - sendAreaSummary(2) - } - MAX-ACCESS read-create - STATUS current - DESCRIPTION - "The variable ospfv3AreaSummary controls the im- - port of Inter-Area LSAs into stub areas. It has - no effect on other areas. - - If it is noAreaSummary, the router will neither - originate nor propagate Inter-Area LSAs into the - stub area. It will rely entirely on its de- - fault route. - - If it is sendAreaSummary, the router will both - summarize and propagate Inter-Area LSAs." - DEFVAL { sendAreaSummary } - ::= { ospfv3AreaEntry 8 } - -ospfv3AreaStatus OBJECT-TYPE - SYNTAX RowStatus - MAX-ACCESS read-create - STATUS current - DESCRIPTION - "This variable controls the status of the en- - try. The use of RowStatus is covered in more detail - in [6]." - ::= { ospfv3AreaEntry 9 } - -ospfv3StubMetric OBJECT-TYPE - SYNTAX BigMetric - MAX-ACCESS read-create - STATUS current - DESCRIPTION - "The metric value advertised for the default route - into Stub and NSSA areas." - ::= { ospfv3AreaEntry 10 } - -ospfv3AreaNssaTranslatorRole OBJECT-TYPE - SYNTAX INTEGER { always(1), candidate(2) } - MAX-ACCESS read-create - STATUS current - DESCRIPTION - "Indicates an NSSA Border router's ability to - perform NSSA translation of type-7 LSAs into - type-5 LSAs." - DEFVAL { candidate } - ::= { ospfv3AreaEntry 11 } - -ospfv3AreaNssaTranslatorState OBJECT-TYPE - SYNTAX INTEGER { - enabled(1), - elected(2), - disabled(3) - } - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Indicates if and how an NSSA Border router is - performing NSSA translation of type-7 LSAs into type-5 - LSAs. When this object is set to enabled, the NSSA - Border router's ospfv3AreaNssTranslatorRole - has been set to always. When this object is set to - elected, a candidate NSSA Border router is translating - type-7 LSAs into type-5. When this object is set to - disabled, a candidate NSSA Border router is NOT - translating type-7 LSAs into type-5." - ::= { ospfv3AreaEntry 12 } - -ospfv3AreaNssaTranslatorStabilityInterval OBJECT-TYPE - SYNTAX Unsigned32 - UNITS "seconds" - MAX-ACCESS read-create - STATUS current - DESCRIPTION - "The number of seconds after an elected translator - determines its services are no longer required, that - it should continue to perform its translation duties." - DEFVAL { 40 } - ::= { ospfv3AreaEntry 13 } - -ospfv3AreaNssaTranslatorEvents OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Indicates the number of Translator State changes - that have occurred since the last boot-up." - ::= { ospfv3AreaEntry 14 } - -ospfv3AreaStubMetricType OBJECT-TYPE - SYNTAX INTEGER { - ospfv3Metric (1), -- OSPF Metric - comparableCost (2), -- external type 1 - nonComparable (3) -- external type 2 - } - MAX-ACCESS read-create - STATUS current - DESCRIPTION - "This variable displays the type of metric ad- - vertised as a default route." - DEFVAL { ospfv3Metric } - ::= { ospfv3AreaEntry 15 } - --- OSPFv3 AS-Scope Link State Database - --- The Link State Database contains the AS-Scope Link State --- Advertisements from throughout the areas that the --- device is attached to. - -ospfv3AsLsdbTable OBJECT-TYPE - SYNTAX SEQUENCE OF Ospfv3AsLsdbEntry - MAX-ACCESS not-accessible - STATUS current - DESCRIPTION - "The OSPFv3 Process's AS-Scope Link State Database." - ::= { ospfv3Objects 3 } - -ospfv3AsLsdbEntry OBJECT-TYPE - SYNTAX Ospfv3AsLsdbEntry - MAX-ACCESS not-accessible - STATUS current - DESCRIPTION - "A single AS-Scope Link State Advertisement." - INDEX { ospfv3AsLsdbType, - ospfv3AsLsdbRouterId, - ospfv3AsLsdbLsid } - ::= { ospfv3AsLsdbTable 1 } - -Ospfv3AsLsdbEntry ::= SEQUENCE { - ospfv3AsLsdbType - Unsigned32, - ospfv3AsLsdbRouterId - RouterID, - ospfv3AsLsdbLsid - IpAddress, - ospfv3AsLsdbSequence - Integer32, - ospfv3AsLsdbAge - Integer32, - ospfv3AsLsdbChecksum - Integer32, - ospfv3AsLsdbAdvertisement - OCTET STRING, - ospfv3AsLsdbTypeKnown - TruthValue - } - -ospfv3AsLsdbType OBJECT-TYPE - SYNTAX Unsigned32 (0..4294967295) - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The type of the link state advertisement. - Each link state type has a separate advertise- - ment format. AS-Scope LSAs not recognized by - the router may be stored in the database." - ::= { ospfv3AsLsdbEntry 1 } - -ospfv3AsLsdbRouterId OBJECT-TYPE - SYNTAX RouterID - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The 32 bit number that uniquely identifies the - originating router in the Autonomous System." - REFERENCE - "OSPF Version 2, Appendix C.1 Global parameters" - ::= { ospfv3AsLsdbEntry 2 } - -ospfv3AsLsdbLsid OBJECT-TYPE - SYNTAX IpAddress - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The Link State ID is an LS Type Specific field - containing a unique identifier; - it identifies the piece of the routing domain - that is being described by the advertisement. - In contrast to OSPFv2, the LSID has no - addressing semantics." - ::= { ospfv3AsLsdbEntry 3 } - --- Note that the OSPF Sequence Number is a 32 bit signed --- integer. It starts with the value '80000001'h, --- or -'7FFFFFFF'h, and increments until '7FFFFFFF'h --- Thus, a typical sequence number will be very negative. - -ospfv3AsLsdbSequence OBJECT-TYPE - SYNTAX Integer32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The sequence number field is a signed 32-bit - integer. It is used to detect old and dupli- - cate link state advertisements. The space of - sequence numbers is linearly ordered. The - larger the sequence number the more recent the - advertisement." - REFERENCE - "OSPF Version 2, Section 12.1.6 LS sequence - number" - ::= { ospfv3AsLsdbEntry 4 } - -ospfv3AsLsdbAge OBJECT-TYPE - SYNTAX Integer32 -- Should be 0..MaxAge - -- unless DoNotAge bit is set - UNITS "seconds" - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "This field is the age of the link state adver- - tisement in seconds." - REFERENCE - "OSPF Version 2, Section 12.1.1 LS age" - ::= { ospfv3AsLsdbEntry 5 } - -ospfv3AsLsdbChecksum OBJECT-TYPE - SYNTAX Integer32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "This field is the checksum of the complete - contents of the advertisement, excepting the - age field. The age field is excepted so that - an advertisement's age can be incremented - without updating the checksum. The checksum - used is the same that is used for ISO connec- - tionless datagrams; it is commonly referred to - as the Fletcher checksum." - REFERENCE - "OSPF Version 2, Section 12.1.7 LS checksum" - ::= { ospfv3AsLsdbEntry 6 } - -ospfv3AsLsdbAdvertisement OBJECT-TYPE - SYNTAX OCTET STRING (SIZE (1..65535)) - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The entire Link State Advertisement, including - its header." - ::= { ospfv3AsLsdbEntry 7 } - -ospfv3AsLsdbTypeKnown OBJECT-TYPE - SYNTAX TruthValue - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Is the LSA type recognized by this Router?" - ::= { ospfv3AsLsdbEntry 8 } - - --- OSPFv3 Area-Scope Link State Database - --- The Link State Database contains the Area-Scope Link State --- Advertisements from throughout the area that the --- device is attached to. - -ospfv3AreaLsdbTable OBJECT-TYPE - SYNTAX SEQUENCE OF Ospfv3AreaLsdbEntry - MAX-ACCESS not-accessible - STATUS current - DESCRIPTION - "The OSPFv3 Process's Area-Scope Link State Database." - ::= { ospfv3Objects 4 } - -ospfv3AreaLsdbEntry OBJECT-TYPE - SYNTAX Ospfv3AreaLsdbEntry - MAX-ACCESS not-accessible - STATUS current - DESCRIPTION - "A single Area-Scope Link State Advertisement." - INDEX { ospfv3AreaLsdbAreaId, - ospfv3AreaLsdbType, - ospfv3AreaLsdbRouterId, - ospfv3AreaLsdbLsid } - ::= { ospfv3AreaLsdbTable 1 } - -Ospfv3AreaLsdbEntry ::= SEQUENCE { - ospfv3AreaLsdbAreaId - AreaID, - ospfv3AreaLsdbType - Unsigned32, - ospfv3AreaLsdbRouterId - RouterID, - ospfv3AreaLsdbLsid - IpAddress, - ospfv3AreaLsdbSequence - Integer32, - ospfv3AreaLsdbAge - Integer32, - ospfv3AreaLsdbChecksum - Integer32, - ospfv3AreaLsdbAdvertisement - OCTET STRING, - ospfv3AreaLsdbTypeKnown - TruthValue - } - -ospfv3AreaLsdbAreaId OBJECT-TYPE - SYNTAX AreaID - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The 32 bit identifier of the Area from which the - LSA was received." - REFERENCE - "OSPF Version 2, Appendix C.2 Area parameters" - ::= { ospfv3AreaLsdbEntry 1 } - -ospfv3AreaLsdbType OBJECT-TYPE - SYNTAX Unsigned32 (0..4294967295) - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The type of the link state advertisement. - Each link state type has a separate advertise- - ment format. Area-Scope LSAs unrecognized by the - router are also stored in this database." - ::= { ospfv3AreaLsdbEntry 2 } - -ospfv3AreaLsdbRouterId OBJECT-TYPE - SYNTAX RouterID - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The 32 bit number that uniquely identifies the - originating router in the Autonomous System." - REFERENCE - "OSPF Version 2, Appendix C.1 Global parameters" - ::= { ospfv3AreaLsdbEntry 3 } - -ospfv3AreaLsdbLsid OBJECT-TYPE - SYNTAX IpAddress - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The Link State ID is an LS Type Specific field - containing a unique identifier; - it identifies the piece of the routing domain - that is being described by the advertisement. - In contrast to OSPFv2, the LSID has no - addressing semantics." - ::= { ospfv3AreaLsdbEntry 4 } - --- Note that the OSPF Sequence Number is a 32 bit signed --- integer. It starts with the value '80000001'h, --- or -'7FFFFFFF'h, and increments until '7FFFFFFF'h --- Thus, a typical sequence number will be very negative. - -ospfv3AreaLsdbSequence OBJECT-TYPE - SYNTAX Integer32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The sequence number field is a signed 32-bit - integer. It is used to detect old and dupli- - cate link state advertisements. The space of - sequence numbers is linearly ordered. The - larger the sequence number the more recent the - advertisement." - REFERENCE - "OSPF Version 2, Section 12.1.6 LS sequence - number" - ::= { ospfv3AreaLsdbEntry 5 } - -ospfv3AreaLsdbAge OBJECT-TYPE - SYNTAX Integer32 -- Should be 0..MaxAge - -- unless DoNotAge bit is set - UNITS "seconds" - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "This field is the age of the link state adver- - tisement in seconds." - REFERENCE - "OSPF Version 2, Section 12.1.1 LS age" - ::= { ospfv3AreaLsdbEntry 6 } - -ospfv3AreaLsdbChecksum OBJECT-TYPE - SYNTAX Integer32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "This field is the checksum of the complete - contents of the advertisement, excepting the - age field. The age field is excepted so that - an advertisement's age can be incremented - without updating the checksum. The checksum - used is the same that is used for ISO connec- - tionless datagrams; it is commonly referred to - as the Fletcher checksum." - REFERENCE - "OSPF Version 2, Section 12.1.7 LS checksum" - ::= { ospfv3AreaLsdbEntry 7 } - -ospfv3AreaLsdbAdvertisement OBJECT-TYPE - SYNTAX OCTET STRING (SIZE (1..65535)) - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The entire Link State Advertisement, including - its header." - ::= { ospfv3AreaLsdbEntry 8 } - -ospfv3AreaLsdbTypeKnown OBJECT-TYPE - SYNTAX TruthValue - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Is the LSA type recognized by this Router?" - ::= { ospfv3AreaLsdbEntry 9 } - --- OSPFv3 Link-Scope Link State Database - --- The Link State Database contains the Link-Scope Link State --- Advertisements from the links that the --- device is attached to. - -ospfv3LinkLsdbTable OBJECT-TYPE - SYNTAX SEQUENCE OF Ospfv3LinkLsdbEntry - MAX-ACCESS not-accessible - STATUS current - DESCRIPTION - "The OSPFv3 Process's Link-Scope Link State Database." - ::= { ospfv3Objects 5 } - -ospfv3LinkLsdbEntry OBJECT-TYPE - SYNTAX Ospfv3LinkLsdbEntry - MAX-ACCESS not-accessible - STATUS current - DESCRIPTION - "A single Link-Scope Link State Advertisement." - INDEX { ospfv3LinkLsdbIfIndex, - ospfv3LinkLsdbType, - ospfv3LinkLsdbRouterId, - ospfv3LinkLsdbLsid } - ::= { ospfv3LinkLsdbTable 1 } - -Ospfv3LinkLsdbEntry ::= SEQUENCE { - ospfv3LinkLsdbIfIndex - InterfaceIndex, - ospfv3LinkLsdbType - Unsigned32, - ospfv3LinkLsdbRouterId - RouterID, - ospfv3LinkLsdbLsid - IpAddress, - ospfv3LinkLsdbSequence - Integer32, - ospfv3LinkLsdbAge - Integer32, - ospfv3LinkLsdbChecksum - Integer32, - ospfv3LinkLsdbAdvertisement - OCTET STRING, - ospfv3LinkLsdbTypeKnown - TruthValue - } - -ospfv3LinkLsdbIfIndex OBJECT-TYPE - SYNTAX InterfaceIndex - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The identifier of the link from which the LSA - was received." - REFERENCE - "OSPF Version 2, Appendix C.2 Area parameters" - ::= { ospfv3LinkLsdbEntry 1 } - -ospfv3LinkLsdbType OBJECT-TYPE - SYNTAX Unsigned32 (0..4294967295) - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The type of the link state advertisement. - Each link state type has a separate advertise- - ment format. Link-Scope LSAs unrecognized by the - router are also stored in this database." - ::= { ospfv3LinkLsdbEntry 2 } - -ospfv3LinkLsdbRouterId OBJECT-TYPE - SYNTAX RouterID - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The 32 bit number that uniquely identifies the - originating router in the Autonomous System." - REFERENCE - "OSPF Version 2, Appendix C.1 Global parameters" - ::= { ospfv3LinkLsdbEntry 3 } - -ospfv3LinkLsdbLsid OBJECT-TYPE - SYNTAX IpAddress - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The Link State ID is an LS Type Specific field - containing a unique identifier; - it identifies the piece of the routing domain - that is being described by the advertisement. - In contrast to OSPFv2, the LSID has no - addressing semantics." - ::= { ospfv3LinkLsdbEntry 4 } - --- Note that the OSPF Sequence Number is a 32 bit signed --- integer. It starts with the value '80000001'h, --- or -'7FFFFFFF'h, and increments until '7FFFFFFF'h --- Thus, a typical sequence number will be very negative. - -ospfv3LinkLsdbSequence OBJECT-TYPE - SYNTAX Integer32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The sequence number field is a signed 32-bit - integer. It is used to detect old and dupli- - cate link state advertisements. The space of - sequence numbers is linearly ordered. The - larger the sequence number the more recent the - advertisement." - REFERENCE - "OSPF Version 2, Section 12.1.6 LS sequence - number" - ::= { ospfv3LinkLsdbEntry 5 } - -ospfv3LinkLsdbAge OBJECT-TYPE - SYNTAX Integer32 -- Should be 0..MaxAge - -- unless DoNotAge bit is set - UNITS "seconds" - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "This field is the age of the link state - advertisement in seconds." - REFERENCE - "OSPF Version 2, Section 12.1.1 LS age" - ::= { ospfv3LinkLsdbEntry 6 } - -ospfv3LinkLsdbChecksum OBJECT-TYPE - SYNTAX Integer32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "This field is the checksum of the complete - contents of the advertisement, excepting the - age field. The age field is excepted so that - an advertisement's age can be incremented - without updating the checksum. The checksum - used is the same that is used for ISO connec- - tionless datagrams; it is commonly referred to - as the Fletcher checksum." - REFERENCE - "OSPF Version 2, Section 12.1.7 LS checksum" - ::= { ospfv3LinkLsdbEntry 7 } - -ospfv3LinkLsdbAdvertisement OBJECT-TYPE - SYNTAX OCTET STRING (SIZE (1..65535)) - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The entire Link State Advertisement, including - its header." - ::= { ospfv3LinkLsdbEntry 8 } - -ospfv3LinkLsdbTypeKnown OBJECT-TYPE - SYNTAX TruthValue - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Indicates whether the LSA type is recognized by this - Router." - ::= { ospfv3LinkLsdbEntry 9 } - - --- OSPF Host Table - --- The Host/Metric Table indicates what hosts are directly --- attached to the Router, and what metrics and types of --- service should be advertised for them. - -ospfv3HostTable OBJECT-TYPE - SYNTAX SEQUENCE OF Ospfv3HostEntry - MAX-ACCESS not-accessible - STATUS current - DESCRIPTION - "The list of Hosts, and their metrics, that the - router will advertise as host routes." - REFERENCE - "OSPF Version 2, Appendix C.6 Host route param- - eters" - ::= { ospfv3Objects 6 } - -ospfv3HostEntry OBJECT-TYPE - SYNTAX Ospfv3HostEntry - MAX-ACCESS not-accessible - STATUS current - DESCRIPTION - "A metric to be advertised when a given host is - reachable." - INDEX { ospfv3HostAddressType, - ospfv3HostAddress } - ::= { ospfv3HostTable 1 } - -Ospfv3HostEntry ::= SEQUENCE { - ospfv3HostAddressType - InetAddressType, - ospfv3HostAddress - InetAddress, - ospfv3HostMetric - Metric, - ospfv3HostStatus - RowStatus, - ospfv3HostAreaID - AreaID - } - -ospfv3HostAddressType OBJECT-TYPE - SYNTAX InetAddressType - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The address type of ospfv3HostAddress. Only IPv6 - addresses without zone index are expected." - REFERENCE - "OSPF Version 2, Appendix C.6 Host route parame- - ters" - ::= { ospfv3HostEntry 1 } - - -ospfv3HostAddress OBJECT-TYPE - SYNTAX InetAddress (SIZE (16)) - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The IPv6 Address of the Host. Must be a Global - or Site-local address." - REFERENCE - "OSPF Version 2, Appendix C.6 Host route parame- - ters" - ::= { ospfv3HostEntry 2 } - -ospfv3HostMetric OBJECT-TYPE - SYNTAX Metric - MAX-ACCESS read-create - STATUS current - DESCRIPTION - "The Metric to be advertised." - REFERENCE - "OSPF Version 2, Appendix C.6 Host route parame- - ters" - ::= { ospfv3HostEntry 3 } - -ospfv3HostStatus OBJECT-TYPE - SYNTAX RowStatus - MAX-ACCESS read-create - STATUS current - DESCRIPTION - "This variable controls the status of the en- - try. The use of RowStatus is covered in more detail - in [6]." - ::= { ospfv3HostEntry 4 } - -ospfv3HostAreaID OBJECT-TYPE - SYNTAX AreaID - MAX-ACCESS read-create - STATUS current - DESCRIPTION - "The Area the Host Entry is to be found within. - By default, the area that a subsuming OSPFv3 in- - terface is in, or 0.0.0.0" - REFERENCE - "OSPF Version 2, Appendix C.2 Area parameters" - ::= { ospfv3HostEntry 5 } - --- OSPFv3 Interface Table - -ospfv3IfTable OBJECT-TYPE - SYNTAX SEQUENCE OF Ospfv3IfEntry - MAX-ACCESS not-accessible - STATUS current - DESCRIPTION - "The OSPFv3 Interface Table describes the inter- - faces from the viewpoint of OSPFv3." - REFERENCE - "OSPF Version 2, Appendix C.3 Router interface - parameters" - ::= { ospfv3Objects 7 } - -ospfv3IfEntry OBJECT-TYPE - SYNTAX Ospfv3IfEntry - MAX-ACCESS not-accessible - STATUS current - DESCRIPTION - "The OSPFv3 Interface Entry describes one inter- - face from the viewpoint of OSPFv3." - INDEX { ospfv3IfIndex } - ::= { ospfv3IfTable 1 } - - -Ospfv3IfEntry ::= SEQUENCE { - ospfv3IfIndex - InterfaceIndex, - ospfv3IfAreaId - AreaID, - ospfv3IfType - INTEGER, - ospfv3IfAdminStat - Status, - ospfv3IfRtrPriority - DesignatedRouterPriority, - ospfv3IfTransitDelay - UpToRefreshInterval, - ospfv3IfRetransInterval - UpToRefreshInterval, - ospfv3IfHelloInterval - HelloRange, - ospfv3IfRtrDeadInterval - RouterDeadRange, - ospfv3IfPollInterval - Unsigned32, - ospfv3IfState - INTEGER, - ospfv3IfDesignatedRouter - RouterID, - ospfv3IfBackupDesignatedRouter - RouterID, - ospfv3IfEvents - Counter32, - ospfv3IfStatus - RowStatus, - ospfv3IfMulticastForwarding - INTEGER, - ospfv3IfDemand - TruthValue, - ospfv3IfMetricValue - Metric, - ospfv3IfLinkScopeLsaCount - Gauge32, - ospfv3IfLinkLsaCksumSum - Integer32, - ospfv3IfInstId - Integer32, - ospfv3IfDemandNbrProbe - TruthValue, - ospfv3IfDemandNbrProbeRetxLimit - Unsigned32, - ospfv3IfDemandNbrProbeInterval - Unsigned32 - } - -ospfv3IfIndex OBJECT-TYPE - SYNTAX InterfaceIndex - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The interface index of this OSPFv3 interface. - It corresponds to the interface index of the - IPv6 interface on which OSPFv3 is configured." - ::= { ospfv3IfEntry 1 } - -ospfv3IfAreaId OBJECT-TYPE - SYNTAX AreaID - MAX-ACCESS read-create - STATUS current - DESCRIPTION - "A 32-bit integer uniquely identifying the area - to which the interface connects. Area ID - 0.0.0.0 is used for the OSPFv3 backbone." - DEFVAL { '00000000'H } -- 0.0.0.0 - ::= { ospfv3IfEntry 2 } - -ospfv3IfType OBJECT-TYPE - SYNTAX INTEGER { - broadcast(1), - nbma(2), - pointToPoint(3), - pointToMultipoint(5) - } - MAX-ACCESS read-create - STATUS current - DESCRIPTION - "The OSPFv3 interface type." - ::= { ospfv3IfEntry 3 } - -ospfv3IfAdminStat OBJECT-TYPE - SYNTAX Status - MAX-ACCESS read-create - STATUS current - DESCRIPTION - "The OSPFv3 interface's administrative status. - The value formed on the interface, and the in- - terface will be advertised as an internal route - to some area. The value 'disabled' denotes - that the interface is external to OSPFv3." - DEFVAL { enabled } - ::= { ospfv3IfEntry 4 } - -ospfv3IfRtrPriority OBJECT-TYPE - SYNTAX DesignatedRouterPriority - MAX-ACCESS read-create - STATUS current - DESCRIPTION - "The priority of this interface. Used in - multi-access networks, this field is used in - the designated router election algorithm. The - value 0 signifies that the router is not eligi- - ble to become the designated router on this - particular network. In the event of a tie in - this value, routers will use their Router ID as - a tie breaker." - DEFVAL { 1 } - ::= { ospfv3IfEntry 5 } - -ospfv3IfTransitDelay OBJECT-TYPE - SYNTAX UpToRefreshInterval - UNITS "seconds" - MAX-ACCESS read-create - STATUS current - DESCRIPTION - "The estimated number of seconds it takes to - transmit a link state update packet over this - interface." - DEFVAL { 1 } - ::= { ospfv3IfEntry 6 } - -ospfv3IfRetransInterval OBJECT-TYPE - SYNTAX UpToRefreshInterval - UNITS "seconds" - MAX-ACCESS read-create - STATUS current - DESCRIPTION - "The number of seconds between link-state ad- - vertisement retransmissions, for adjacencies - belonging to this interface. This value is - also used when retransmitting database descrip- - tion and link-state request packets." - DEFVAL { 5 } - ::= { ospfv3IfEntry 7 } - -ospfv3IfHelloInterval OBJECT-TYPE - SYNTAX HelloRange - UNITS "seconds" - MAX-ACCESS read-create - STATUS current - DESCRIPTION - "The length of time, in seconds, between the - Hello packets that the router sends on the in- - terface. This value must be the same for all - routers attached to a common network." - DEFVAL { 10 } - ::= { ospfv3IfEntry 8 } - -ospfv3IfRtrDeadInterval OBJECT-TYPE - SYNTAX RouterDeadRange - UNITS "seconds" - MAX-ACCESS read-create - STATUS current - DESCRIPTION - "The number of seconds that a router's Hello - packets have not been seen before it's neigh- - bors declare the router down. This should be - some multiple of the Hello interval. This - value must be the same for all routers attached - to a common network." - DEFVAL { 40 } - ::= { ospfv3IfEntry 9 } - -ospfv3IfPollInterval OBJECT-TYPE - SYNTAX Unsigned32 - UNITS "seconds" - MAX-ACCESS read-create - STATUS current - DESCRIPTION - "The larger time interval, in seconds, between - the Hello packets sent to an inactive non- - broadcast multi- access neighbor." - DEFVAL { 120 } - ::= { ospfv3IfEntry 10 } - -ospfv3IfState OBJECT-TYPE - SYNTAX INTEGER { - down(1), - loopback(2), - waiting(3), - pointToPoint(4), - designatedRouter(5), - backupDesignatedRouter(6), - otherDesignatedRouter(7) - } - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The OSPFv3 Interface State." - ::= { ospfv3IfEntry 11 } - -ospfv3IfDesignatedRouter OBJECT-TYPE - SYNTAX RouterID - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The Router ID of the Designated Router." - ::= { ospfv3IfEntry 12 } - -ospfv3IfBackupDesignatedRouter OBJECT-TYPE - SYNTAX RouterID - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The Router ID of the Backup Designated - Router." - ::= { ospfv3IfEntry 14 } - -ospfv3IfEvents OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The number of times this OSPF interface has - changed its state, or an error has occurred." - ::= { ospfv3IfEntry 15 } - - ospfv3IfStatus OBJECT-TYPE - SYNTAX RowStatus - MAX-ACCESS read-create - STATUS current - DESCRIPTION - "This variable controls the status of the en- - try. The use of RowStatus is covered in more detail in - [6]." - ::= { ospfv3IfEntry 17 } - -ospfv3IfMulticastForwarding OBJECT-TYPE - SYNTAX INTEGER { - blocked(1), -- no multicast forwarding - multicast(2), -- using multicast address - unicast(3) -- to each OSPFv3 neighbor - } - MAX-ACCESS read-create - STATUS current - DESCRIPTION - "The way multicasts should forwarded on this - interface; not forwarded, forwarded as data - link multicasts, or forwarded as data link uni- - casts. Data link multicasting is not meaning- - ful on point to point and NBMA interfaces, and - setting ospfv3MulticastForwarding to 0 effective- - ly disables all multicast forwarding." - DEFVAL { blocked } - ::= { ospfv3IfEntry 18 } - -ospfv3IfDemand OBJECT-TYPE - SYNTAX TruthValue - MAX-ACCESS read-create - STATUS current - DESCRIPTION - "Indicates whether Demand OSPFv3 procedures (hel- - lo suppression to FULL neighbors and setting the - DoNotAge flag on propagated LSAs) should be per- - formed on this interface." - DEFVAL { false } - ::= { ospfv3IfEntry 19 } - -ospfv3IfMetricValue OBJECT-TYPE - SYNTAX Metric - MAX-ACCESS read-create - STATUS current - DESCRIPTION - "The metric assigned to this interface. - The default value of the Metric is - Reference Bandwidth / ifSpeed. The value of the - reference bandwidth is configured by the - ospfv3ReferenceBandwidth object." - ::= { ospfv3IfEntry 20 } - - ospfv3IfLinkScopeLsaCount OBJECT-TYPE - SYNTAX Gauge32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The total number of Link-Scope link-state - advertisements in this link's link-state database." - ::= { ospfv3IfEntry 21 } - - ospfv3IfLinkLsaCksumSum OBJECT-TYPE - SYNTAX Integer32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The 32-bit unsigned sum of the Link-Scope link-state - advertisements' LS checksums contained in this - link's link-state database. The sum can be used - to determine if there has been a change in a - router's link state database, and to compare the - link-state database of two routers." - ::= { ospfv3IfEntry 22 } - -ospfv3IfInstId OBJECT-TYPE - SYNTAX Integer32 - MAX-ACCESS read-create - STATUS current - DESCRIPTION - "Enables multiple instances of OSPFv3 to be run over - a single link. Each protocol instance would be assigned - a separate ID. This ID has local link significance - only." - DEFVAL { 0 } - ::= { ospfv3IfEntry 23 } - -ospfv3IfDemandNbrProbe OBJECT-TYPE - SYNTAX TruthValue - MAX-ACCESS read-create - STATUS current - DESCRIPTION - " Indicates whether or not neighbor probing is - enabled to determine whether or not the neighbor - is inactive. Neighbor probing is disabled by - default." - DEFVAL { false } - ::= { ospfv3IfEntry 24 } - - ospfv3IfDemandNbrProbeRetxLimit OBJECT-TYPE - SYNTAX Unsigned32 - UNITS "seconds" - MAX-ACCESS read-create - STATUS current - DESCRIPTION - " The number of consecutive LSA retransmissions before - the neighbor is deemed inactive and the neighbor - adjacency is brought down." - DEFVAL { 10 } - ::= { ospfv3IfEntry 25 } - - - ospfv3IfDemandNbrProbeInterval OBJECT-TYPE - SYNTAX Unsigned32 - UNITS "seconds" - MAX-ACCESS read-create - STATUS current - DESCRIPTION - " Defines how often the neighbor will be probed." - DEFVAL { 120 } - ::= { ospfv3IfEntry 26 } - - --- OSPFv3 Virtual Interface Table - --- The Virtual Interface Table describes the virtual --- links that the OSPFv3 Process is configured to --- carry on. - -ospfv3VirtIfTable OBJECT-TYPE - SYNTAX SEQUENCE OF Ospfv3VirtIfEntry - MAX-ACCESS not-accessible - STATUS current - DESCRIPTION - "Information about this router's virtual inter- - faces." - REFERENCE - "OSPF Version 2, Appendix C.4 Virtual link - parameters" - ::= { ospfv3Objects 8 } - -ospfv3VirtIfEntry OBJECT-TYPE - SYNTAX Ospfv3VirtIfEntry - MAX-ACCESS not-accessible - STATUS current - DESCRIPTION - "Information about a single Virtual Interface." - INDEX { ospfv3VirtIfAreaId, - ospfv3VirtIfNeighbor } - ::= { ospfv3VirtIfTable 1 } - -Ospfv3VirtIfEntry ::= SEQUENCE { - ospfv3VirtIfAreaId - AreaID, - ospfv3VirtIfNeighbor - RouterID, - ospfv3VirtIfIndex - InterfaceIndex, - ospfv3VirtIfTransitDelay - UpToRefreshInterval, - ospfv3VirtIfRetransInterval - UpToRefreshInterval, - ospfv3VirtIfHelloInterval - HelloRange, - ospfv3VirtIfRtrDeadInterval - RouterDeadRange, - ospfv3VirtIfState - INTEGER, - ospfv3VirtIfEvents - Counter32, - ospfv3VirtIfStatus - RowStatus, - ospfv3VirtIfLinkScopeLsaCount - Gauge32, - ospfv3VirtIfLinkLsaCksumSum - Integer32 - } - -ospfv3VirtIfAreaId OBJECT-TYPE - SYNTAX AreaID - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The Transit Area that the Virtual Link - traverses. By definition, this is not 0.0.0.0" - ::= { ospfv3VirtIfEntry 1 } - -ospfv3VirtIfNeighbor OBJECT-TYPE - SYNTAX RouterID - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The Router ID of the Virtual Neighbor." - ::= { ospfv3VirtIfEntry 2 } - -ospfv3VirtIfIndex OBJECT-TYPE - SYNTAX InterfaceIndex - MAX-ACCESS read-create - STATUS current - DESCRIPTION - "The interface ID assigned to this OSPFv3 virtual - interface. It is advertised in Hello's sent over - the virtal link and in the router's router-LSAs." - ::= { ospfv3VirtIfEntry 3 } - -ospfv3VirtIfTransitDelay OBJECT-TYPE - SYNTAX UpToRefreshInterval - UNITS "seconds" - MAX-ACCESS read-create - STATUS current - DESCRIPTION - "The estimated number of seconds it takes to - transmit a link- state update packet over this - interface." - DEFVAL { 1 } - ::= { ospfv3VirtIfEntry 4 } - -ospfv3VirtIfRetransInterval OBJECT-TYPE - SYNTAX UpToRefreshInterval - UNITS "seconds" - MAX-ACCESS read-create - STATUS current - DESCRIPTION - "The number of seconds between link-state ad- - vertisement retransmissions, for adjacencies - belonging to this interface. This value is - also used when retransmitting database descrip- - tion and link-state request packets. This - value should be well over the expected round- - trip time." - DEFVAL { 5 } - ::= { ospfv3VirtIfEntry 5 } - -ospfv3VirtIfHelloInterval OBJECT-TYPE - SYNTAX HelloRange - UNITS "seconds" - MAX-ACCESS read-create - STATUS current - DESCRIPTION - "The length of time, in seconds, between the - Hello packets that the router sends on the in- - terface. This value must be the same for the - virtual neighbor." - DEFVAL { 10 } - ::= { ospfv3VirtIfEntry 6 } - -ospfv3VirtIfRtrDeadInterval OBJECT-TYPE - SYNTAX RouterDeadRange - UNITS "seconds" - MAX-ACCESS read-create - STATUS current - DESCRIPTION - "The number of seconds that a router's Hello - packets have not been seen before it's neigh- - bors declare the router down. This should be - some multiple of the Hello interval. This - value must be the same for the virtual neigh- - bor." - DEFVAL { 60 } - ::= { ospfv3VirtIfEntry 7 } - -ospfv3VirtIfState OBJECT-TYPE - SYNTAX INTEGER { - down(1), -- these use the same encoding - pointToPoint(4) -- as the ospfv3IfTable - } - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "OSPF virtual interface states." - ::= { ospfv3VirtIfEntry 8 } - -ospfv3VirtIfEvents OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The number of state changes or error events on - this Virtual Link" - ::= { ospfv3VirtIfEntry 9 } - -ospfv3VirtIfStatus OBJECT-TYPE - SYNTAX RowStatus - MAX-ACCESS read-create - STATUS current - DESCRIPTION - "This variable controls the status of the en- - try. The use of RowStatus is covered in more detail - in [6]." - ::= { ospfv3VirtIfEntry 10 } - -ospfv3VirtIfLinkScopeLsaCount OBJECT-TYPE - SYNTAX Gauge32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The total number of Link-Scope link-state - advertisements in this virtual link's link-state - database." - ::= { ospfv3VirtIfEntry 11 } - -ospfv3VirtIfLinkLsaCksumSum OBJECT-TYPE - SYNTAX Integer32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The 32-bit unsigned sum of the Link-Scope link-state - advertisements' LS checksums contained in this - virtual link's link-state database. The sum can be used - to determine if there has been a change in a - router's link state database, and to compare the - link-state database of two routers." - ::= { ospfv3VirtIfEntry 12 } - - --- OSPFv3 Neighbor Table - --- The OSPFv3 Neighbor Table describes all neighbors in --- the locality of the subject router. - -ospfv3NbrTable OBJECT-TYPE - SYNTAX SEQUENCE OF Ospfv3NbrEntry - MAX-ACCESS not-accessible - STATUS current - DESCRIPTION - "A table of non-virtual neighbor information." - REFERENCE - "OSPF Version 2, Section 10 The Neighbor Data - Structure" - ::= { ospfv3Objects 9 } - -ospfv3NbrEntry OBJECT-TYPE - SYNTAX Ospfv3NbrEntry - MAX-ACCESS not-accessible - STATUS current - DESCRIPTION - "The information regarding a single neighbor." - REFERENCE - "OSPF Version 2, Section 10 The Neighbor Data - Structure" - INDEX { ospfv3NbrIfIndex, - ospfv3NbrRtrId } - ::= { ospfv3NbrTable 1 } - -Ospfv3NbrEntry ::= SEQUENCE { - ospfv3NbrIfIndex - InterfaceIndex, - ospfv3NbrRtrId - RouterID, - ospfv3NbrAddressType - InetAddressType, - ospfv3NbrAddress - InetAddress, - ospfv3NbrOptions - Integer32, - ospfv3NbrPriority - DesignatedRouterPriority, - ospfv3NbrState - INTEGER, - ospfv3NbrEvents - Counter32, - ospfv3NbrLsRetransQLen - Gauge32, - ospfv3NbrHelloSuppressed - TruthValue, - ospfv3NbrIfId - InterfaceIndex, - ospfv3NbrRestartHelperStatus - INTEGER, - ospfv3NbrRestartHelperAge - UpToRefreshInterval, - ospfv3NbrRestartHelperExitReason - INTEGER - } - -ospfv3NbrIfIndex OBJECT-TYPE - SYNTAX InterfaceIndex - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The local link ID of the link over which the - neighbor can be reached." - ::= { ospfv3NbrEntry 1 } - -ospfv3NbrRtrId OBJECT-TYPE - SYNTAX RouterID - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "A 32-bit integer (represented as a type IpAd- - dress) uniquely identifying the neighboring - router in the Autonomous System." - ::= { ospfv3NbrEntry 2 } - -ospfv3NbrAddressType OBJECT-TYPE - SYNTAX InetAddressType - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The address type of ospfv3NbrAddress. Only IPv6 - addresses without zone index are expected." - ::= { ospfv3NbrEntry 3 } - -ospfv3NbrAddress OBJECT-TYPE - SYNTAX InetAddress (SIZE (16)) - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The IPv6 address of the neighbor associated with - the local link." - ::= { ospfv3NbrEntry 4 } - -ospfv3NbrOptions OBJECT-TYPE - SYNTAX Integer32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "A Bit Mask corresponding to the neighbor's op- - tions field." - REFERENCE - "OSPF Version 3, Appendix A.2 the Options field" - ::= { ospfv3NbrEntry 5 } - -ospfv3NbrPriority OBJECT-TYPE - SYNTAX DesignatedRouterPriority - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The priority of this neighbor in the designat- - ed router election algorithm. The value 0 sig- - nifies that the neighbor is not eligible to be- - come the designated router on this particular - network." - ::= { ospfv3NbrEntry 6 } - -ospfv3NbrState OBJECT-TYPE - SYNTAX INTEGER { - down(1), - attempt(2), - init(3), - twoWay(4), - exchangeStart(5), - exchange(6), - loading(7), - full(8) - } - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The State of the relationship with this Neigh- - bor." - REFERENCE - "OSPF Version 2, Section 10.1 Neighbor States" - ::= { ospfv3NbrEntry 7 } - -ospfv3NbrEvents OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The number of times this neighbor relationship - has changed state, or an error has occurred." - ::= { ospfv3NbrEntry 8 } - -ospfv3NbrLsRetransQLen OBJECT-TYPE - SYNTAX Gauge32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The current length of the retransmission - queue." - ::= { ospfv3NbrEntry 9 } - -ospfv3NbrHelloSuppressed OBJECT-TYPE - SYNTAX TruthValue - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Indicates whether Hellos are being suppressed - to the neighbor" - ::= { ospfv3NbrEntry 10 } - -ospfv3NbrIfId OBJECT-TYPE - SYNTAX InterfaceIndex - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The interface ID that the neighbor advertises - in its Hello Packets on this link, that is, the - neighbor's local interface index." - ::= { ospfv3NbrEntry 11 } - -ospfv3NbrRestartHelperStatus OBJECT-TYPE - SYNTAX INTEGER { notHelping (1), - helping (2) - } - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Indicates whether the router is acting - as a hitless restart helper for the neighbor." - ::= { ospfv3NbrEntry 12 } - -ospfv3NbrRestartHelperAge OBJECT-TYPE - SYNTAX UpToRefreshInterval - UNITS "seconds" - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Remaining time in current OSPF hitless restart - interval, if the router is acting as a restart - helper for the neighbor." - ::= { ospfv3NbrEntry 13 } - -ospfv3NbrRestartHelperExitReason OBJECT-TYPE - SYNTAX INTEGER { none (1), -- not attempted - inProgress (2), -- restart in - -- progress - completed (3), -- successfully - -- completed - timedOut (4), -- timed out - topologyChanged (5) -- aborted due to - -- topology - -- change. - } - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Describes the outcome of the last attempt at acting - as a hitless restart helper for the neighbor." - ::= { ospfv3NbrEntry 14 } - - --- OSPFv3 NBMA Neighbor Table - --- The OSPFv3 NBMA Neighbor Table describes all configured --- NBMA neighbors and neighbors dynamically discovered by --- lower-level protocols such as Inverse Neighbor Discovery. - -ospfv3NbmaNbrTable OBJECT-TYPE - SYNTAX SEQUENCE OF Ospfv3NbmaNbrEntry - MAX-ACCESS not-accessible - STATUS current - DESCRIPTION - "A table of configured non-virtual neighbor - information and neighbors dynamically discovered - by lower-level protocols such as Inverse Neighbor - Discovery." - REFERENCE - "OSPF Version 2, Section 10 The Neighbor Data - Structure" - ::= { ospfv3Objects 10 } - -ospfv3NbmaNbrEntry OBJECT-TYPE - SYNTAX Ospfv3NbmaNbrEntry - MAX-ACCESS not-accessible - STATUS current - DESCRIPTION - "The information regarding a single configured - neighbor or neighbor discovered by lower-level - protocols such as Inverse Neighbor Discovery." - REFERENCE - "OSPF Version 2, Section 10 The Neighbor Data - Structure" - INDEX { ospfv3NbmaNbrIfIndex, - ospfv3NbmaNbrAddressType, - ospfv3NbmaNbrAddress } - ::= { ospfv3NbmaNbrTable 1 } - -Ospfv3NbmaNbrEntry ::= SEQUENCE { - ospfv3NbmaNbrIfIndex - InterfaceIndex, - ospfv3NbmaNbrAddressType - InetAddressType, - ospfv3NbmaNbrAddress - InetAddress, - ospfv3NbmaNbrPriority - DesignatedRouterPriority, - ospfv3NbmaNbrRtrId - RouterID, - ospfv3NbmaNbrState - INTEGER, - ospfv3NbmaNbrStorageType - StorageType, - ospfv3NbmaNbrStatus - RowStatus - } - -ospfv3NbmaNbrIfIndex OBJECT-TYPE - SYNTAX InterfaceIndex - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The local link ID of the link over which the - neighbor can be reached." - ::= { ospfv3NbmaNbrEntry 1 } - -ospfv3NbmaNbrAddressType OBJECT-TYPE - SYNTAX InetAddressType - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The address type of ospfv3NbrAddress. Only IPv6 - addresses without zone index are expected." - ::= { ospfv3NbmaNbrEntry 2 } - -ospfv3NbmaNbrAddress OBJECT-TYPE - SYNTAX InetAddress (SIZE (16)) - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The IPv6 address of the neighbor associated with - the local link." - ::= { ospfv3NbmaNbrEntry 3 } - -ospfv3NbmaNbrPriority OBJECT-TYPE - SYNTAX DesignatedRouterPriority - MAX-ACCESS read-create - STATUS current - DESCRIPTION - "The priority of this neighbor in the designat- - ed router election algorithm. The value 0 sig- - nifies that the neighbor is not eligible to be- - come the designated router on this particular - network." - DEFVAL { 1 } - ::= { ospfv3NbmaNbrEntry 4 } - -ospfv3NbmaNbrRtrId OBJECT-TYPE - SYNTAX RouterID - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "A 32-bit integer (represented as a type IpAd- - dress) uniquely identifying the neighboring - router in the Autonomous System. A value of - 0.0.0.0 is returned until a Hello is received - from the configured neighbor." - ::= { ospfv3NbmaNbrEntry 5 } - -ospfv3NbmaNbrState OBJECT-TYPE - SYNTAX INTEGER { - down(1), - attempt(2), - init(3), - twoWay(4), - exchangeStart(5), - exchange(6), - loading(7), - full(8) - } - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The State of the relationship with this Neigh- - bor." - REFERENCE - "OSPF Version 2, Section 10.1 Neighbor States" - ::= { ospfv3NbmaNbrEntry 6 } - -ospfv3NbmaNbrStorageType OBJECT-TYPE - SYNTAX StorageType - MAX-ACCESS read-create - STATUS current - DESCRIPTION - "The storage type for this conceptual row. - Conceptual rows having the value 'permanent' need not - allow write-access to any columnar objects in the row. - Manually configured entries will have a storage type - of nonVolatile while entries dynamically created as a - result of a lower-level protocol such as Inverse - Neighbor Discovery will have a storage type of - volatile." - DEFVAL { nonVolatile } - ::= { ospfv3NbmaNbrEntry 7 } - -ospfv3NbmaNbrStatus OBJECT-TYPE - SYNTAX RowStatus - MAX-ACCESS read-create - STATUS current - DESCRIPTION - "This variable controls the status of the en- - try. The use of RowStatus is covered in more detail - in [6]." - ::= { ospfv3NbmaNbrEntry 8 } - --- OSPFv3 Virtual Neighbor Table - --- This table describes all virtual neighbors. --- Since Virtual Links are configured in the --- virtual interface table, this table is read-only. - -ospfv3VirtNbrTable OBJECT-TYPE - SYNTAX SEQUENCE OF Ospfv3VirtNbrEntry - MAX-ACCESS not-accessible - STATUS current - DESCRIPTION - "A table of virtual neighbor information." - REFERENCE - "OSPF Version 2, Section 15 Virtual Links" - ::= { ospfv3Objects 11 } - -ospfv3VirtNbrEntry OBJECT-TYPE - SYNTAX Ospfv3VirtNbrEntry - MAX-ACCESS not-accessible - STATUS current - DESCRIPTION - "Virtual neighbor information." - INDEX { ospfv3VirtNbrArea, - ospfv3VirtNbrRtrId } - ::= { ospfv3VirtNbrTable 1 } - -Ospfv3VirtNbrEntry ::= SEQUENCE { - ospfv3VirtNbrArea - AreaID, - ospfv3VirtNbrRtrId - RouterID, - ospfv3VirtNbrIfIndex - InterfaceIndex, - ospfv3VirtNbrAddressType - InetAddressType, - ospfv3VirtNbrAddress - InetAddress, - ospfv3VirtNbrOptions - Integer32, - ospfv3VirtNbrState - INTEGER, - ospfv3VirtNbrEvents - Counter32, - ospfv3VirtNbrLsRetransQLen - Gauge32, - ospfv3VirtNbrHelloSuppressed - TruthValue, - ospfv3VirtNbrIfId - InterfaceIndex, - ospfv3VirtNbrRestartHelperStatus - INTEGER, - ospfv3VirtNbrRestartHelperAge - UpToRefreshInterval, - ospfv3VirtNbrRestartHelperExitReason - INTEGER - } - -ospfv3VirtNbrArea OBJECT-TYPE - SYNTAX AreaID - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The Transit Area Identifier." - ::= { ospfv3VirtNbrEntry 1 } - -ospfv3VirtNbrRtrId OBJECT-TYPE - SYNTAX RouterID - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "A 32-bit integer uniquely identifying the - neighboring router in the Autonomous System." - ::= { ospfv3VirtNbrEntry 2 } - -ospfv3VirtNbrIfIndex OBJECT-TYPE - SYNTAX InterfaceIndex - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The local interface ID for the virtual link over - which the neighbor can be reached." - ::= { ospfv3VirtNbrEntry 3 } - -ospfv3VirtNbrAddressType OBJECT-TYPE - SYNTAX InetAddressType - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The address type of ospfv3VirtNbrAddress. Only IPv6 - addresses without zone index are expected." - ::= { ospfv3VirtNbrEntry 4 } - -ospfv3VirtNbrAddress OBJECT-TYPE - SYNTAX InetAddress (SIZE (16)) - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The IPv6 address advertised by this Virtual Neighbor. - It must be a Site-Local or Global scope address." - ::= { ospfv3VirtNbrEntry 5 } - -ospfv3VirtNbrOptions OBJECT-TYPE - SYNTAX Integer32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "A Bit Mask corresponding to the neighbor's op- - tions field." - REFERENCE - "OSPF Version 3, Appendix A.2 the Options field" - ::= { ospfv3VirtNbrEntry 6 } - -ospfv3VirtNbrState OBJECT-TYPE - SYNTAX INTEGER { - down(1), - attempt(2), - init(3), - twoWay(4), - exchangeStart(5), - exchange(6), - loading(7), - full(8) - } - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The state of the Virtual Neighbor Relation- - ship." - ::= { ospfv3VirtNbrEntry 7 } - -ospfv3VirtNbrEvents OBJECT-TYPE - SYNTAX Counter32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The number of times this virtual link has - changed its state, or an error has occurred." - ::= { ospfv3VirtNbrEntry 8 } - -ospfv3VirtNbrLsRetransQLen OBJECT-TYPE - SYNTAX Gauge32 - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The current length of the retransmission - queue." - ::= { ospfv3VirtNbrEntry 9 } - -ospfv3VirtNbrHelloSuppressed OBJECT-TYPE - SYNTAX TruthValue - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Indicates whether Hellos are being suppressed - to the neighbor" - ::= { ospfv3VirtNbrEntry 10 } - -ospfv3VirtNbrIfId OBJECT-TYPE - SYNTAX InterfaceIndex - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The interface ID that the neighbor advertises - in its Hello Packets on this virtual link, that is, - the neighbor's local interface ID." - ::= { ospfv3VirtNbrEntry 11 } - - ospfv3VirtNbrRestartHelperStatus OBJECT-TYPE - SYNTAX INTEGER { notHelping (1), - helping (2) - } - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Indicates whether the router is acting - as a hitless restart helper for the neighbor." - ::= { ospfv3VirtNbrEntry 12 } - -ospfv3VirtNbrRestartHelperAge OBJECT-TYPE - SYNTAX UpToRefreshInterval - UNITS "seconds" - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Remaining time in current OSPF hitless restart - interval, if the router is acting as a restart - helper for the neighbor." - ::= { ospfv3VirtNbrEntry 13 } - -ospfv3VirtNbrRestartHelperExitReason OBJECT-TYPE - SYNTAX INTEGER { none (1), -- not attempted - inProgress (2), -- restart in - -- progress - completed (3), -- successfully - -- completed - timedOut (4), -- timed out - topologyChanged (5) -- aborted due to - -- topology - -- change. - } - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "Describes the outcome of the last attempt at acting - as a hitless restart helper for the neighbor." - ::= { ospfv3VirtNbrEntry 14 } - - --- --- The OSPFv3 Area Aggregate Table --- - -ospfv3AreaAggregateTable OBJECT-TYPE - SYNTAX SEQUENCE OF Ospfv3AreaAggregateEntry - MAX-ACCESS not-accessible - STATUS current - DESCRIPTION - "A range of IPv6 prefixes specified by a - prefix/prefix length pair. Note that if - ranges are configured such that one range sub- - sumes another range the most specific match is - the preferred one." - ::= { ospfv3Objects 12 } - -ospfv3AreaAggregateEntry OBJECT-TYPE - SYNTAX Ospfv3AreaAggregateEntry - MAX-ACCESS not-accessible - STATUS current - DESCRIPTION - "A range of IPv6 prefixes specified by a - prefix/prefix length pair. Note that if - ranges are configured such that one range sub- - sumes another range the most specific match is - the preferred one." - REFERENCE - "OSPF Version 2, Appendix C.2 Area parameters" - INDEX { ospfv3AreaAggregateAreaID, - ospfv3AreaAggregateAreaLsdbType, - ospfv3AreaAggregatePrefixType, - ospfv3AreaAggregatePrefix, - ospfv3AreaAggregatePrefixLength } - ::= { ospfv3AreaAggregateTable 1 } - -Ospfv3AreaAggregateEntry ::= SEQUENCE { - ospfv3AreaAggregateAreaID - AreaID, - ospfv3AreaAggregateAreaLsdbType - INTEGER, - ospfv3AreaAggregatePrefixType - InetAddressType, - ospfv3AreaAggregatePrefix - InetAddress, - ospfv3AreaAggregatePrefixLength - InetAddressPrefixLength, - ospfv3AreaAggregateStatus - RowStatus, - ospfv3AreaAggregateEffect - INTEGER, - ospfv3AreaAggregateRouteTag - INTEGER - } - -ospfv3AreaAggregateAreaID OBJECT-TYPE - SYNTAX AreaID - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The Area the Address Aggregate is to be found - within." - REFERENCE - "OSPF Version 2, Appendix C.2 Area parameters" - ::= { ospfv3AreaAggregateEntry 1 } - -ospfv3AreaAggregateAreaLsdbType OBJECT-TYPE - SYNTAX INTEGER { - interAreaPrefixLsa(8195), -- 0x2003 - nssaExternalLsa(8199) -- 0x2007 - } - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The type of the Address Aggregate. This field - specifies the Area Lsdb type that this Address Ag- - gregate applies to." - REFERENCE - "OSPF Version 2, Appendix A.4.1 The Link State - Advertisement header" - ::= { ospfv3AreaAggregateEntry 2 } - -ospfv3AreaAggregatePrefixType OBJECT-TYPE - SYNTAX InetAddressType - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The prefix type of ospfv3AreaAggregatePrefix. Only - IPv6 addresses are expected." - ::= { ospfv3AreaAggregateEntry 4 } - -ospfv3AreaAggregatePrefix OBJECT-TYPE - SYNTAX InetAddress (SIZE (0..16)) - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The IPv6 Prefix." - REFERENCE - "OSPF Version 2, Appendix C.2 Area parameters" - ::= { ospfv3AreaAggregateEntry 5 } - -ospfv3AreaAggregatePrefixLength OBJECT-TYPE - SYNTAX InetAddressPrefixLength (3..128) - UNITS "bits" - MAX-ACCESS read-only - STATUS current - DESCRIPTION - "The length of the prefix (in bits). A prefix can - not be shorter than 3 bits." - REFERENCE - "OSPF Version 2, Appendix C.2 Area parameters" - ::= { ospfv3AreaAggregateEntry 6 } - -ospfv3AreaAggregateStatus OBJECT-TYPE - SYNTAX RowStatus - MAX-ACCESS read-create - STATUS current - DESCRIPTION - "This variable controls the status of the en- - try. The use of RowStatus is covered in more detail - in [6]." - ::= { ospfv3AreaAggregateEntry 7 } - -ospfv3AreaAggregateEffect OBJECT-TYPE - SYNTAX INTEGER { - advertiseMatching(1), - doNotAdvertiseMatching(2) - } - MAX-ACCESS read-create - STATUS current - DESCRIPTION - "Prefixes subsumed by ranges either trigger the - advertisement of the indicated aggregate (ad- - vertiseMatching), or result in the prefix not - being advertised at all outside the area." - DEFVAL { advertiseMatching } - ::= { ospfv3AreaAggregateEntry 8 } - -ospfv3AreaAggregateRouteTag OBJECT-TYPE - SYNTAX Integer32 - MAX-ACCESS read-create - STATUS current - DESCRIPTION - "This tag is advertised only in the summarized - As-External LSA when summarizing from type-7 to - type-5." - DEFVAL { 0 } - ::= { ospfv3AreaAggregateEntry 9 } - - --- conformance information - -ospfv3Groups OBJECT IDENTIFIER ::= { ospfv3Conformance 1 } -ospfv3Compliances OBJECT IDENTIFIER ::= { ospfv3Conformance 2 } - --- compliance statements - -ospfv3Compliance MODULE-COMPLIANCE - STATUS current - DESCRIPTION "The compliance statement " - MODULE -- this module - MANDATORY-GROUPS { - ospfv3BasicGroup, - ospfv3AreaGroup, - ospfv3IfGroup, - ospfv3VirtIfGroup, - ospfv3NbrGroup, - ospfv3NbmaNbrGroup, - ospfv3VirtNbrGroup, - ospfv3AreaAggregateGroup - } - - GROUP ospfv3AsLsdbGroup - DESCRIPTION - "This group is required for OSPFv3 systems that - display their AS-scope link state database." - - GROUP ospfv3AreaLsdbGroup - DESCRIPTION - "This group is required for OSPFv3 systems that - display their Area-scope link state database." - - GROUP ospfv3LinkLsdbGroup - DESCRIPTION - "This group is required for OSPFv3 systems that - display their Link-scope link state database." - - GROUP ospfv3HostGroup - DESCRIPTION - "This group is required for OSPFv3 systems that - support attached hosts." - - OBJECT ospfv3NbrAddressType - SYNTAX InetAddressType { ipv6(2) } - DESCRIPTION - "An implementation is only required to support IPv6 - address without zone index." - - OBJECT ospfv3VirtNbrAddressType - SYNTAX InetAddressType { ipv6(2) } - DESCRIPTION - "An implementation is only required to support IPv6 - address without zone index." - - ::= { ospfv3Compliances 1 } - --- units of conformance - -ospfv3BasicGroup OBJECT-GROUP - OBJECTS { - ospfv3RouterId, - ospfv3AdminStat, - ospfv3VersionNumber, - ospfv3AreaBdrRtrStatus, - ospfv3ASBdrRtrStatus, - ospfv3AsScopeLsaCount, - ospfv3AsScopeLsaCksumSum, - ospfv3OriginateNewLsas, - ospfv3RxNewLsas, - ospfv3ExtLsaCount, - ospfv3ExtAreaLsdbLimit, - ospfv3MulticastExtensions, - ospfv3ExitOverflowInterval, - ospfv3DemandExtensions, - ospfv3TrafficEngineeringSupport, - ospfv3ReferenceBandwidth, - ospfv3RestartSupport, - ospfv3RestartInterval, - ospfv3RestartStatus, - ospfv3RestartAge, - ospfv3RestartExitReason - } - STATUS current - DESCRIPTION - "These objects are required for OSPFv3 systems." - ::= { ospfv3Groups 1 } - - -ospfv3AreaGroup OBJECT-GROUP - OBJECTS { - ospfv3ImportAsExtern, - ospfv3AreaSpfRuns, - ospfv3AreaBdrRtrCount, - ospfv3AreaAsBdrRtrCount, - ospfv3AreaScopeLsaCount, - ospfv3AreaScopeLsaCksumSum, - ospfv3AreaSummary, - ospfv3AreaStatus, - ospfv3StubMetric, - ospfv3AreaNssaTranslatorRole, - ospfv3AreaNssaTranslatorState, - ospfv3AreaNssaTranslatorStabilityInterval, - ospfv3AreaNssaTranslatorEvents, - ospfv3AreaStubMetricType - } - STATUS current - DESCRIPTION - "These objects are required for OSPFv3 systems - supporting areas." - ::= { ospfv3Groups 2 } - -ospfv3AsLsdbGroup OBJECT-GROUP - OBJECTS { - ospfv3AsLsdbSequence, - ospfv3AsLsdbAge, - ospfv3AsLsdbChecksum, - ospfv3AsLsdbAdvertisement, - ospfv3AsLsdbTypeKnown - } - STATUS current - DESCRIPTION - "These objects are required for OSPFv3 systems - that display their AS-scope link state database." - ::= { ospfv3Groups 3 } - -ospfv3AreaLsdbGroup OBJECT-GROUP - OBJECTS { - ospfv3AreaLsdbSequence, - ospfv3AreaLsdbAge, - ospfv3AreaLsdbChecksum, - ospfv3AreaLsdbAdvertisement, - ospfv3AreaLsdbTypeKnown - } - STATUS current - DESCRIPTION - "These objects are required for OSPFv3 systems - that display their Area-scope link state database." - ::= { ospfv3Groups 4 } - -ospfv3LinkLsdbGroup OBJECT-GROUP - OBJECTS { - ospfv3LinkLsdbSequence, - ospfv3LinkLsdbAge, - ospfv3LinkLsdbChecksum, - ospfv3LinkLsdbAdvertisement, - ospfv3LinkLsdbTypeKnown - } - STATUS current - DESCRIPTION - "These objects are required for OSPFv3 systems - that display their Link-scope link state database." - ::= { ospfv3Groups 5 } - -ospfv3HostGroup OBJECT-GROUP - OBJECTS { - ospfv3HostMetric, - ospfv3HostStatus, - ospfv3HostAreaID - } - STATUS current - DESCRIPTION - "These objects are required for OSPFv3 systems - that support attached hosts." - ::= { ospfv3Groups 6 } - -ospfv3IfGroup OBJECT-GROUP - OBJECTS { - ospfv3IfAreaId, - ospfv3IfType, - ospfv3IfAdminStat, - ospfv3IfRtrPriority, - ospfv3IfTransitDelay, - ospfv3IfRetransInterval, - ospfv3IfHelloInterval, - ospfv3IfRtrDeadInterval, - ospfv3IfPollInterval, - ospfv3IfState, - ospfv3IfDesignatedRouter, - ospfv3IfBackupDesignatedRouter, - ospfv3IfEvents, - ospfv3IfStatus, - ospfv3IfMulticastForwarding, - ospfv3IfDemand, - ospfv3IfMetricValue, - ospfv3IfLinkScopeLsaCount, - ospfv3IfLinkLsaCksumSum, - ospfv3IfInstId, - ospfv3IfDemandNbrProbe, - ospfv3IfDemandNbrProbeRetxLimit, - ospfv3IfDemandNbrProbeInterval - } - STATUS current - DESCRIPTION - "These interface objects are required for - OSPFv3 systems." - ::= { ospfv3Groups 7 } - -ospfv3VirtIfGroup OBJECT-GROUP - OBJECTS { - ospfv3VirtIfIndex, - ospfv3VirtIfTransitDelay, - ospfv3VirtIfRetransInterval, - ospfv3VirtIfHelloInterval, - ospfv3VirtIfRtrDeadInterval, - ospfv3VirtIfState, - ospfv3VirtIfEvents, - ospfv3VirtIfStatus, - ospfv3VirtIfLinkScopeLsaCount, - ospfv3VirtIfLinkLsaCksumSum - } - STATUS current - DESCRIPTION - "These virtual interface objects are required for - OSPFv3 systems." - ::= { ospfv3Groups 8 } - -ospfv3NbrGroup OBJECT-GROUP - OBJECTS { - ospfv3NbrAddressType, - ospfv3NbrAddress, - ospfv3NbrOptions, - ospfv3NbrPriority, - ospfv3NbrState, - ospfv3NbrEvents, - ospfv3NbrLsRetransQLen, - ospfv3NbrHelloSuppressed, - ospfv3NbrIfId, - ospfv3NbrRestartHelperStatus, - ospfv3NbrRestartHelperAge, - ospfv3NbrRestartHelperExitReason - } - STATUS current - DESCRIPTION - "These neighbor objects are required for - OSPFv3 systems." - ::= { ospfv3Groups 9 } - -ospfv3NbmaNbrGroup OBJECT-GROUP - OBJECTS { - ospfv3NbmaNbrPriority, - ospfv3NbmaNbrRtrId, - ospfv3NbmaNbrState, - ospfv3NbmaNbrStorageType, - ospfv3NbmaNbrStatus - } - STATUS current - DESCRIPTION - "These NBMA neighbor objects are required for - OSPFv3 systems." - ::= { ospfv3Groups 10 } - -ospfv3VirtNbrGroup OBJECT-GROUP - OBJECTS { - ospfv3VirtNbrIfIndex, - ospfv3VirtNbrAddressType, - ospfv3VirtNbrAddress, - ospfv3VirtNbrOptions, - ospfv3VirtNbrState, - ospfv3VirtNbrEvents, - ospfv3VirtNbrLsRetransQLen, - ospfv3VirtNbrHelloSuppressed, - ospfv3VirtNbrIfId, - ospfv3VirtNbrRestartHelperStatus, - ospfv3VirtNbrRestartHelperAge, - ospfv3VirtNbrRestartHelperExitReason - } - STATUS current - DESCRIPTION - "These virtual neighbor objects are required for - OSPFv3 systems." - ::= { ospfv3Groups 11 } - -ospfv3AreaAggregateGroup OBJECT-GROUP - OBJECTS { - ospfv3AreaAggregateStatus, - ospfv3AreaAggregateEffect, - ospfv3AreaAggregateRouteTag - } - STATUS current - DESCRIPTION - "These area aggregate objects are required for - OSPFv3 systems." - ::= { ospfv3Groups 12 } - -END + OSPFV3-MIB DEFINITIONS ::= BEGIN + + IMPORTS + MODULE-IDENTITY, OBJECT-TYPE, NOTIFICATION-TYPE, mib-2, + Counter32, Gauge32, Integer32, Unsigned32 + FROM SNMPv2-SMI + TEXTUAL-CONVENTION, TruthValue, RowStatus, TimeStamp + FROM SNMPv2-TC + MODULE-COMPLIANCE, OBJECT-GROUP, NOTIFICATION-GROUP + FROM SNMPv2-CONF + InterfaceIndex + FROM IF-MIB + InetAddressType, InetAddress, InetAddressPrefixLength, + InetAddressIPv6 + FROM INET-ADDRESS-MIB + Metric, BigMetric, Status, + HelloRange, DesignatedRouterPriority + FROM OSPF-MIB; + + ospfv3MIB MODULE-IDENTITY + LAST-UPDATED "200908130000Z" + ORGANIZATION "IETF OSPF Working Group" + CONTACT-INFO + "WG E-Mail: ospf@ietf.org + WG Chairs: Acee Lindem + acee@redback.com + + Abhay Roy + akr@cisco.com + + Editors: Dan Joyal + Nortel + 600 Technology Park Drive + Billerica, MA 01821, USA + djoyal@nortel.com + + Vishwas Manral + IP Infusion + Almora, Uttarakhand + India + vishwas@ipinfusion.com" + DESCRIPTION + "The MIB module for OSPF version 3. + + Copyright (c) 2009 IETF Trust and the persons + identified as authors of the code. All rights + reserved. + + Redistribution and use in source and binary forms, with + or without modification, are permitted provided that + the following conditions are met: + + - Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + + - 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. + + - Neither the name of Internet Society, IETF or IETF + Trust, nor the names of specific contributors, may be + used to endorse or promote products derived from this + software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. + + This version of this MIB module is part of RFC 5643; + see the RFC itself for full legal notices." + + REVISION "200908130000Z" + DESCRIPTION + "Initial version, published as RFC 5643" + ::= { mib-2 191 } + + -- Textual conventions + + Ospfv3UpToRefreshIntervalTC ::= TEXTUAL-CONVENTION + DISPLAY-HINT "d" + STATUS current + DESCRIPTION + "The values one might be able to configure for + variables bounded by the Refresh Interval." + REFERENCE + "OSPF Version 2, Appendix B, Architectural Constants" + SYNTAX Unsigned32 (1..1800) + + Ospfv3DeadIntervalRangeTC ::= TEXTUAL-CONVENTION + DISPLAY-HINT "d" + STATUS current + DESCRIPTION + "The range, in seconds, of dead interval value." + REFERENCE + "OSPF for IPv6, Appendix C.3, Router Interface + Parameters" + SYNTAX Unsigned32 (1..'FFFF'h) + + Ospfv3RouterIdTC ::= TEXTUAL-CONVENTION + DISPLAY-HINT "d" + STATUS current + DESCRIPTION + "A 32-bit, unsigned integer uniquely identifying the + router in the Autonomous System. To ensure + uniqueness, this may default to the value of one of + the router's IPv4 host addresses if IPv4 is + configured on the router." + REFERENCE + "OSPF for IPv6, Appendix C.1, Global Parameters" + SYNTAX Unsigned32 (1..'FFFFFFFF'h) + + Ospfv3LsIdTC ::= TEXTUAL-CONVENTION + DISPLAY-HINT "d" + STATUS current + DESCRIPTION + "A unique 32-bit identifier of the piece of the + routing domain that is being described by a link + state advertisement. In contrast to OSPFv2, the + Link State ID (LSID) has no addressing semantics." + REFERENCE + "OSPF Version 2, Section 12.1.4, Link State ID" + SYNTAX Unsigned32 (1..'FFFFFFFF'h) + + Ospfv3AreaIdTC ::= TEXTUAL-CONVENTION + DISPLAY-HINT "d" + STATUS current + DESCRIPTION + "An OSPFv3 Area Identifier. A value of zero + identifies the backbone area." + REFERENCE + "OSPF for IPv6, Appendix C.3 Router Interface + Parameters" + SYNTAX Unsigned32 (0..'FFFFFFFF'h) + + Ospfv3IfInstIdTC ::= TEXTUAL-CONVENTION + DISPLAY-HINT "d" + STATUS current + DESCRIPTION + "An OSPFv3 Interface Instance ID." + REFERENCE + "OSPF for IPv6, Appendix C.3, Router Interface + Parameters" + SYNTAX Unsigned32 (0..255) + + Ospfv3LsaSequenceTC ::= TEXTUAL-CONVENTION + DISPLAY-HINT "d" + STATUS current + DESCRIPTION + "The sequence number field is a signed 32-bit + integer. It is used to detect old and duplicate + link state advertisements. The space of + sequence numbers is linearly ordered. The + larger the sequence number, the more recent the + advertisement." + REFERENCE + "OSPF Version 2, Section 12.1.6, LS sequence + number" + SYNTAX Integer32 + + Ospfv3LsaAgeTC ::= TEXTUAL-CONVENTION + DISPLAY-HINT "d" + STATUS current + DESCRIPTION + "The age of the link state advertisement in + seconds. The high-order bit of the LS age + field is considered the DoNotAge bit for + support of on-demand circuits." + REFERENCE + "OSPF Version 2, Section 12.1.1, LS age; + Extending OSPF to Support Demand Circuits, + Section 2.2, The LS age field" + SYNTAX Unsigned32 (0..3600 | 32768..36368) + + -- Top-level structure of MIB + ospfv3Notifications OBJECT IDENTIFIER ::= { ospfv3MIB 0 } + ospfv3Objects OBJECT IDENTIFIER ::= { ospfv3MIB 1 } + ospfv3Conformance OBJECT IDENTIFIER ::= { ospfv3MIB 2 } + + -- OSPFv3 General Variables + + -- These parameters apply globally to the Router's + -- OSPFv3 Process. + + ospfv3GeneralGroup OBJECT IDENTIFIER ::= { ospfv3Objects 1 } + + ospfv3RouterId OBJECT-TYPE + SYNTAX Ospfv3RouterIdTC + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "A 32-bit unsigned integer uniquely identifying + the router in the Autonomous System. To ensure + uniqueness, this may default to the 32-bit + unsigned integer representation of one of + the router's IPv4 interface addresses (if IPv4 + is configured on the router). + + This object is persistent, and when written, the + entity SHOULD save the change to non-volatile + storage." + REFERENCE + "OSPF for IPv6, Appendix C.1, Global Parameters" + ::= { ospfv3GeneralGroup 1 } + + ospfv3AdminStatus OBJECT-TYPE + SYNTAX Status + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The administrative status of OSPFv3 in the + router. The value 'enabled' denotes that the + OSPFv3 Process is active on at least one + interface; 'disabled' disables it on all + interfaces. + + This object is persistent, and when written, the + entity SHOULD save the change to non-volatile + storage." + ::= { ospfv3GeneralGroup 2 } + + ospfv3VersionNumber OBJECT-TYPE + SYNTAX INTEGER { version3 (3) } + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The version number of OSPF for IPv6 is 3." + ::= { ospfv3GeneralGroup 3 } + + ospfv3AreaBdrRtrStatus OBJECT-TYPE + SYNTAX TruthValue + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "A flag to denote whether this router is an area + border router. The value of this object is true (1) + when the router is an area border router." + REFERENCE + "OSPF Version 2, Section 3, Splitting the AS into + Areas" + ::= { ospfv3GeneralGroup 4 } + + ospfv3ASBdrRtrStatus OBJECT-TYPE + SYNTAX TruthValue + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "A flag to note whether this router is + configured as an Autonomous System border router. + + This object is persistent, and when written, the + entity SHOULD save the change to non-volatile + storage." + REFERENCE + "OSPF Version 2, Section 3.3, Classification of + routers" + ::= { ospfv3GeneralGroup 5 } + + ospfv3AsScopeLsaCount OBJECT-TYPE + SYNTAX Gauge32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of AS-scope (e.g., AS-External) link state + advertisements in the link state database." + ::= { ospfv3GeneralGroup 6 } + + ospfv3AsScopeLsaCksumSum OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The 32-bit unsigned sum of the LS checksums of + the AS-scoped link state advertisements + contained in the link state database. This sum + can be used to determine if there has been a + change in a router's link state database or + to compare the link state database of two + routers." + ::= { ospfv3GeneralGroup 7 } + + ospfv3OriginateNewLsas OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of new link state advertisements + that have been originated. This number is + incremented each time the router originates a new + LSA. + + Discontinuities in the value of this counter + can occur at re-initialization of the management + system and at other times as indicated by the + value of ospfv3DiscontinuityTime." + ::= { ospfv3GeneralGroup 8 } + + ospfv3RxNewLsas OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of link state advertisements + received that are determined to be new + instantiations. This number does not include + newer instantiations of self-originated link state + advertisements. + + Discontinuities in the value of this counter + can occur at re-initialization of the management + system and at other times as indicated by the + value of ospfv3DiscontinuityTime." + ::= { ospfv3GeneralGroup 9 } + + ospfv3ExtLsaCount OBJECT-TYPE + SYNTAX Gauge32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of External (LS type 0x4005) in the + link state database." + ::= { ospfv3GeneralGroup 10 } + + ospfv3ExtAreaLsdbLimit OBJECT-TYPE + SYNTAX Integer32 (-1..'7FFFFFFF'h) + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The maximum number of non-default + AS-external-LSA entries that can be stored in the + link state database. If the value is -1, then + there is no limit. + + When the number of non-default AS-external-LSAs + in a router's link state database reaches + ospfv3ExtAreaLsdbLimit, the router enters Overflow + state. The router never holds more than + ospfv3ExtAreaLsdbLimit non-default AS-external-LSAs + in its database. ospfv3ExtAreaLsdbLimit MUST be set + identically in all routers attached to the OSPFv3 + backbone and/or any regular OSPFv3 area (i.e., + OSPFv3 stub areas and not-so-stubby-areas (NSSAs) + are excluded). + + This object is persistent, and when written, the + entity SHOULD save the change to non-volatile + storage." + ::= { ospfv3GeneralGroup 11 } + + ospfv3ExitOverflowInterval OBJECT-TYPE + SYNTAX Unsigned32 + UNITS "seconds" + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The number of seconds that, after entering + Overflow state, a router will attempt to leave + Overflow state. This allows the router to again + originate non-default, AS-External-LSAs. When + set to 0, the router will not leave Overflow + state until restarted. + + This object is persistent, and when written, the + entity SHOULD save the change to non-volatile + storage." + ::= { ospfv3GeneralGroup 12 } + + ospfv3DemandExtensions OBJECT-TYPE + SYNTAX TruthValue + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The router's support for demand circuits. + The value of this object is true (1) when + demand circuits are supported. + + This object is persistent, and when written, the + entity SHOULD save the change to non-volatile + storage." + REFERENCE + "OSPF Version 2; Extending OSPF to Support Demand + Circuits" + ::= { ospfv3GeneralGroup 13 } + + ospfv3ReferenceBandwidth OBJECT-TYPE + SYNTAX Unsigned32 + UNITS "kilobits per second" + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Reference bandwidth in kilobits per second for + calculating default interface metrics. The + default value is 100,000 KBPS (100 MBPS). + + This object is persistent, and when written, the + entity SHOULD save the change to non-volatile + storage." + REFERENCE + "OSPF Version 2, Appendix C.3, Router interface + parameters" + DEFVAL { 100000 } + ::= { ospfv3GeneralGroup 14 } + + ospfv3RestartSupport OBJECT-TYPE + SYNTAX INTEGER { none(1), + plannedOnly(2), + plannedAndUnplanned(3) + } + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "The router's support for OSPF graceful restart. + Options include no restart support, only planned + + restarts, or both planned and unplanned restarts. + + This object is persistent, and when written, the + entity SHOULD save the change to non-volatile + storage." + REFERENCE "Graceful OSPF Restart, Appendix B.1, Global + Parameters (Minimum subset)" + ::= { ospfv3GeneralGroup 15 } + + ospfv3RestartInterval OBJECT-TYPE + SYNTAX Ospfv3UpToRefreshIntervalTC + UNITS "seconds" + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Configured OSPF graceful restart timeout interval. + + This object is persistent, and when written, the + entity SHOULD save the change to non-volatile + storage." + REFERENCE "Graceful OSPF Restart, Appendix B.1, Global + Parameters (Minimum subset)" + DEFVAL { 120 } + ::= { ospfv3GeneralGroup 16 } + + ospfv3RestartStrictLsaChecking OBJECT-TYPE + SYNTAX TruthValue + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "Indicates if strict LSA checking is enabled for + graceful restart. A value of true (1) indicates that + strict LSA checking is enabled. + + This object is persistent, and when written, + the entity SHOULD save the change to non-volatile + storage." + REFERENCE "Graceful OSPF Restart, Appendix B.2, Global + Parameters (Optional)" + DEFVAL { true } + ::= { ospfv3GeneralGroup 17 } + + ospfv3RestartStatus OBJECT-TYPE + SYNTAX INTEGER { notRestarting(1), + plannedRestart(2), + unplannedRestart(3) + } + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The current status of OSPF graceful restart capability." + ::= { ospfv3GeneralGroup 18 } + + ospfv3RestartAge OBJECT-TYPE + SYNTAX Ospfv3UpToRefreshIntervalTC + UNITS "seconds" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Remaining time in the current OSPF graceful restart + interval." + ::= { ospfv3GeneralGroup 19 } + + ospfv3RestartExitReason OBJECT-TYPE + SYNTAX INTEGER { none(1), + inProgress(2), + completed(3), + timedOut(4), + topologyChanged(5) + } + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Describes the outcome of the last attempt at a + graceful restart. + + none: no restart has yet been attempted. + inProgress: a restart attempt is currently underway. + completed: the last restart completed successfully. + timedOut: the last restart timed out. + topologyChanged: the last restart was aborted due to + a topology change." + ::= { ospfv3GeneralGroup 20 } + + ospfv3NotificationEnable OBJECT-TYPE + SYNTAX TruthValue + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "This object provides a coarse level of control + over the generation of OSPFv3 notifications. + + If this object is set to true (1), then it enables + the generation of OSPFv3 notifications. If it is + set to false (2), these notifications are not + generated. + + This object is persistent, and when written, the + entity SHOULD save the change to non-volatile + storage." + ::= { ospfv3GeneralGroup 21 } + +ospfv3StubRouterSupport OBJECT-TYPE + SYNTAX TruthValue + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The router's support for stub router functionality. An + object value of true (1) indicates that stub router + functionality is supported." + REFERENCE + "OSPF Stub Router Advertisement" + ::= { ospfv3GeneralGroup 22 } + + ospfv3StubRouterAdvertisement OBJECT-TYPE + SYNTAX INTEGER { + doNotAdvertise(1), + advertise(2) + } + MAX-ACCESS read-write + STATUS current + DESCRIPTION + "This object controls the advertisement of + stub LSAs by the router. The value + doNotAdvertise (1) will result in the advertisement + of standard LSAs and is the default value. + + This object is persistent, and when written, + the entity SHOULD save the change to non-volatile + storage." + REFERENCE + "OSPF Stub Router Advertisement, Section 2, Proposed + Solution" + DEFVAL { doNotAdvertise } + ::= { ospfv3GeneralGroup 23 } + +ospfv3DiscontinuityTime OBJECT-TYPE + SYNTAX TimeStamp + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The value of sysUpTime on the most recent occasion + at which any one of this MIB's counters suffered + a discontinuity. + + If no such discontinuities have occurred since the last + re-initialization of the local management subsystem, + then this object contains a zero value." + ::= { ospfv3GeneralGroup 24 } + + ospfv3RestartTime OBJECT-TYPE + SYNTAX TimeStamp + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The value of sysUpTime on the most recent occasion + at which the ospfv3RestartExitReason was updated." + ::= { ospfv3GeneralGroup 25 } + + -- The OSPFv3 Area Data Structure contains information + -- regarding the various areas. The interfaces and + -- virtual links are configured as part of these areas. + -- Area 0, by definition, is the backbone area. + + ospfv3AreaTable OBJECT-TYPE + SYNTAX SEQUENCE OF Ospfv3AreaEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Information describing the configured + parameters and cumulative statistics of the router's + attached areas. The interfaces and + virtual links are configured as part of these areas. + Area 0, by definition, is the backbone area." + REFERENCE + "OSPF Version 2, Section 6, The Area Data + Structure" + ::= { ospfv3Objects 2 } + + ospfv3AreaEntry OBJECT-TYPE + SYNTAX Ospfv3AreaEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Information describing the configured + parameters and cumulative statistics of one of the + router's attached areas. + + The information in this table is persistent, + and when written, the entity SHOULD save the a + change to non-volatile storage." + INDEX { ospfv3AreaId } + ::= { ospfv3AreaTable 1 } + + Ospfv3AreaEntry ::= SEQUENCE { + ospfv3AreaId + Ospfv3AreaIdTC, + ospfv3AreaImportAsExtern + INTEGER, + ospfv3AreaSpfRuns + Counter32, + ospfv3AreaBdrRtrCount + Gauge32, + ospfv3AreaAsBdrRtrCount + Gauge32, + ospfv3AreaScopeLsaCount + Gauge32, + ospfv3AreaScopeLsaCksumSum + Unsigned32, + ospfv3AreaSummary + INTEGER, + ospfv3AreaRowStatus + RowStatus, + ospfv3AreaStubMetric + BigMetric, + ospfv3AreaNssaTranslatorRole + INTEGER, + ospfv3AreaNssaTranslatorState + INTEGER, + ospfv3AreaNssaTranslatorStabInterval + Unsigned32, + ospfv3AreaNssaTranslatorEvents + Counter32, + ospfv3AreaStubMetricType + INTEGER, + ospfv3AreaTEEnabled + TruthValue + } + + ospfv3AreaId OBJECT-TYPE + SYNTAX Ospfv3AreaIdTC + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A 32-bit unsigned integer uniquely identifying an area. + Area ID 0 is used for the OSPFv3 backbone." + REFERENCE + "OSPF Version 2, Appendix C.2, Area parameters" + ::= { ospfv3AreaEntry 1 } + + ospfv3AreaImportAsExtern OBJECT-TYPE + SYNTAX INTEGER { + importExternal(1), -- normal area + importNoExternal(2), -- stub area + importNssa(3) -- not-so-stubby-area + } + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "Indicates whether an area is a stub area, NSSA, or + standard area. AS-scope LSAs are not imported into stub + areas or NSSAs. NSSAs import AS-External data as NSSA + LSAs that have Area-scope." + REFERENCE + "OSPF Version 2, Appendix C.2, Area parameters" + DEFVAL { importExternal } + ::= { ospfv3AreaEntry 2 } + + ospfv3AreaSpfRuns OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of times that the intra-area route + table has been calculated using this area's + link state database. This is typically done + using Dijkstra's algorithm. + + Discontinuities in the value of this counter + can occur at re-initialization of the management + system and at other times as indicated by the + value of ospfv3DiscontinuityTime." + ::= { ospfv3AreaEntry 3 } + + ospfv3AreaBdrRtrCount OBJECT-TYPE + SYNTAX Gauge32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of area border routers + reachable within this area. This is initially zero, + and is calculated in each Shortest Path First (SPF) + pass." + DEFVAL { 0 } + ::= { ospfv3AreaEntry 4 } + + ospfv3AreaAsBdrRtrCount OBJECT-TYPE + SYNTAX Gauge32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of Autonomous System border + routers reachable within this area. This is + initially zero, and is calculated in each SPF + pass." + DEFVAL { 0 } + ::= { ospfv3AreaEntry 5 } + + ospfv3AreaScopeLsaCount OBJECT-TYPE + SYNTAX Gauge32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of Area-scope link state + advertisements in this area's link state + database." + DEFVAL { 0 } + ::= { ospfv3AreaEntry 6 } + + ospfv3AreaScopeLsaCksumSum OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The 32-bit unsigned sum of the Area-scope link state + advertisements' LS checksums contained in this + area's link state database. The sum can be used + to determine if there has been a change in a + router's link state database or to compare the + link state database of two routers." + ::= { ospfv3AreaEntry 7 } + + ospfv3AreaSummary OBJECT-TYPE + SYNTAX INTEGER { + noAreaSummary(1), + sendAreaSummary(2) + } + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The variable ospfv3AreaSummary controls the + import of Inter-Area LSAs into stub and + NSSA areas. It has no effect on other areas. + + If it is noAreaSummary, the router will neither + originate nor propagate Inter-Area LSAs into the + stub or NSSA area. It will only advertise a + default route. + + If it is sendAreaSummary, the router will both + summarize and propagate Inter-Area LSAs." + DEFVAL { sendAreaSummary } + ::= { ospfv3AreaEntry 8 } + + ospfv3AreaRowStatus OBJECT-TYPE + SYNTAX RowStatus + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "This object permits management of the table by + facilitating actions such as row creation, + construction, and destruction. + + The value of this object has no effect on + whether other objects in this conceptual row can be + modified." + ::= { ospfv3AreaEntry 9 } + + ospfv3AreaStubMetric OBJECT-TYPE + SYNTAX BigMetric + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The metric value advertised for the default route + into stub and NSSA areas. By default, this equals the + least metric among the interfaces to other areas." + ::= { ospfv3AreaEntry 10 } + + ospfv3AreaNssaTranslatorRole OBJECT-TYPE + SYNTAX INTEGER { always(1), candidate(2) } + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "Indicates an NSSA border router's policy to + perform NSSA translation of NSSA-LSAs into + AS-External-LSAs." + DEFVAL { candidate } + ::= { ospfv3AreaEntry 11 } + + ospfv3AreaNssaTranslatorState OBJECT-TYPE + SYNTAX INTEGER { + enabled(1), + elected(2), + disabled(3) + } + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Indicates if and how an NSSA border router is + performing NSSA translation of NSSA-LSAs into + AS-External-LSAs. When this object is set to + 'enabled', the NSSA border router's + ospfv3AreaNssaTranslatorRole has been set to 'always'. + When this object is set to 'elected', a candidate + NSSA border router is translating NSSA-LSAs into + AS-External-LSAs. When this object is set to + 'disabled', a candidate NSSA Border router is NOT + translating NSSA-LSAs into AS-External-LSAs." + ::= { ospfv3AreaEntry 12 } + + ospfv3AreaNssaTranslatorStabInterval OBJECT-TYPE + SYNTAX Unsigned32 + UNITS "seconds" + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The stability interval defined as the number of + seconds after an elected translator determines its + services are no longer required that it should + continue to perform its translation duties." + DEFVAL { 40 } + ::= { ospfv3AreaEntry 13 } + + ospfv3AreaNssaTranslatorEvents OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Indicates the number of Translator state changes + that have occurred since the last start-up of the + OSPFv3 routing process. + + Discontinuities in the value of this counter + can occur at re-initialization of the management + system and at other times as indicated by the + value of ospfv3DiscontinuityTime." + ::= { ospfv3AreaEntry 14 } + + ospfv3AreaStubMetricType OBJECT-TYPE + SYNTAX INTEGER { + ospfv3Metric(1), -- OSPF Metric + comparableCost(2), -- external type 1 + nonComparable(3) -- external type 2 + } + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "This variable assigns the type of metric + advertised as a default route." + DEFVAL { ospfv3Metric } + ::= { ospfv3AreaEntry 15 } + + ospfv3AreaTEEnabled OBJECT-TYPE + SYNTAX TruthValue + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "Indicates whether or not traffic engineering + is enabled in the area. The object is set + to the value true (1) to enable traffic engineering. + Traffic engineering is disabled by default." + DEFVAL { false } + ::= { ospfv3AreaEntry 16 } + + -- OSPFv3 AS-Scope Link State Database + + ospfv3AsLsdbTable OBJECT-TYPE + SYNTAX SEQUENCE OF Ospfv3AsLsdbEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The OSPFv3 Process's AS-scope link state database + (LSDB). The LSDB contains the AS-scope link state + advertisements from throughout the areas that the + device is attached to." + ::= { ospfv3Objects 3 } + + ospfv3AsLsdbEntry OBJECT-TYPE + SYNTAX Ospfv3AsLsdbEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A single AS-scope link state advertisement." + INDEX { ospfv3AsLsdbType, + ospfv3AsLsdbRouterId, + ospfv3AsLsdbLsid } + ::= { ospfv3AsLsdbTable 1 } + + Ospfv3AsLsdbEntry ::= SEQUENCE { + ospfv3AsLsdbType + Unsigned32, + ospfv3AsLsdbRouterId + Ospfv3RouterIdTC, + ospfv3AsLsdbLsid + Ospfv3LsIdTC, + ospfv3AsLsdbSequence + Ospfv3LsaSequenceTC, + ospfv3AsLsdbAge + Ospfv3LsaAgeTC, + ospfv3AsLsdbChecksum + Integer32, + ospfv3AsLsdbAdvertisement + OCTET STRING, + ospfv3AsLsdbTypeKnown + TruthValue + } + + ospfv3AsLsdbType OBJECT-TYPE + SYNTAX Unsigned32(0..'FFFFFFFF'h) + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The type of the link state advertisement. + Each link state type has a separate + advertisement format. AS-scope LSAs not recognized + by the router may be stored in the database." + ::= { ospfv3AsLsdbEntry 1 } + + ospfv3AsLsdbRouterId OBJECT-TYPE + SYNTAX Ospfv3RouterIdTC + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The 32-bit number that uniquely identifies the + originating router in the Autonomous System." + REFERENCE + "OSPF Version 2, Appendix C.1, Global parameters" + ::= { ospfv3AsLsdbEntry 2 } + + ospfv3AsLsdbLsid OBJECT-TYPE + SYNTAX Ospfv3LsIdTC + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The Link State ID is an LS type-specific field + containing a unique identifier; + it identifies the piece of the routing domain + that is being described by the advertisement. + In contrast to OSPFv2, the LSID has no + addressing semantics." + ::= { ospfv3AsLsdbEntry 3 } + + -- Note that the OSPF sequence number is a 32-bit signed + -- integer. It starts with the value '80000001'h + -- or -'7FFFFFFF'h, and increments until '7FFFFFFF'h. + -- Thus, a typical sequence number will be very negative. + + ospfv3AsLsdbSequence OBJECT-TYPE + SYNTAX Ospfv3LsaSequenceTC + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The sequence number field is a signed 32-bit + integer. It is used to detect old and duplicate + link state advertisements. The space of + sequence numbers is linearly ordered. The + larger the sequence number, the more recent the + advertisement." + REFERENCE + "OSPF Version 2, Section 12.1.6, LS sequence + number" + ::= { ospfv3AsLsdbEntry 4 } + + ospfv3AsLsdbAge OBJECT-TYPE + SYNTAX Ospfv3LsaAgeTC + UNITS "seconds" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "This field is the age of the link state + advertisement in seconds. The high-order bit + of the LS age field is considered the DoNotAge + bit for support of on-demand circuits." + REFERENCE + "OSPF Version 2, Section 12.1.1, LS age; + Extending OSPF to Support Demand Circuits, + Section 2.2, The LS age field." + ::= { ospfv3AsLsdbEntry 5 } + + ospfv3AsLsdbChecksum OBJECT-TYPE + SYNTAX Integer32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "This field is the checksum of the complete + contents of the advertisement, excepting the + age field. The age field is excepted so that + an advertisement's age can be incremented + without updating the checksum. The checksum + used is the same that is used for ISO + connectionless datagrams; it is commonly + referred to as the Fletcher checksum." + REFERENCE + "OSPF Version 2, Section 12.1.7, LS checksum" + ::= { ospfv3AsLsdbEntry 6 } + + ospfv3AsLsdbAdvertisement OBJECT-TYPE + SYNTAX OCTET STRING (SIZE (1..65535)) + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The entire link state advertisement, including + its header." + ::= { ospfv3AsLsdbEntry 7 } + + ospfv3AsLsdbTypeKnown OBJECT-TYPE + SYNTAX TruthValue + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The value true (1) indicates that the LSA type + is recognized by this router." + ::= { ospfv3AsLsdbEntry 8 } + + -- OSPFv3 Area-Scope Link State Database + + ospfv3AreaLsdbTable OBJECT-TYPE + SYNTAX SEQUENCE OF Ospfv3AreaLsdbEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The OSPFv3 Process's Area-scope LSDB. + The LSDB contains the Area-scope link state + advertisements from throughout the area that the + device is attached to." + ::= { ospfv3Objects 4 } + + ospfv3AreaLsdbEntry OBJECT-TYPE + SYNTAX Ospfv3AreaLsdbEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A single Area-scope link state advertisement." + INDEX { ospfv3AreaLsdbAreaId, + ospfv3AreaLsdbType, + ospfv3AreaLsdbRouterId, + ospfv3AreaLsdbLsid } + ::= { ospfv3AreaLsdbTable 1 } + + Ospfv3AreaLsdbEntry ::= SEQUENCE { + ospfv3AreaLsdbAreaId + Ospfv3AreaIdTC, + ospfv3AreaLsdbType + Unsigned32, + ospfv3AreaLsdbRouterId + Ospfv3RouterIdTC, + ospfv3AreaLsdbLsid + Ospfv3LsIdTC, + ospfv3AreaLsdbSequence + Ospfv3LsaSequenceTC, + ospfv3AreaLsdbAge + Ospfv3LsaAgeTC, + ospfv3AreaLsdbChecksum + Integer32, + ospfv3AreaLsdbAdvertisement + OCTET STRING, + ospfv3AreaLsdbTypeKnown + TruthValue + } + + ospfv3AreaLsdbAreaId OBJECT-TYPE + SYNTAX Ospfv3AreaIdTC + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The 32-bit identifier of the Area from which the + LSA was received." + REFERENCE + "OSPF Version 2, Appendix C.2, Area parameters" + ::= { ospfv3AreaLsdbEntry 1 } + + ospfv3AreaLsdbType OBJECT-TYPE + SYNTAX Unsigned32(0..'FFFFFFFF'h) + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The type of the link state advertisement. + Each link state type has a separate + advertisement format. Area-scope LSAs unrecognized + by the router are also stored in this database." + ::= { ospfv3AreaLsdbEntry 2 } + + ospfv3AreaLsdbRouterId OBJECT-TYPE + SYNTAX Ospfv3RouterIdTC + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The 32-bit number that uniquely identifies the + originating router in the Autonomous System." + REFERENCE + "OSPF Version 2, Appendix C.1, Global parameters" + ::= { ospfv3AreaLsdbEntry 3 } + + ospfv3AreaLsdbLsid OBJECT-TYPE + SYNTAX Ospfv3LsIdTC + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The Link State ID is an LS type-specific field + containing a unique identifier; + it identifies the piece of the routing domain + that is being described by the advertisement. + In contrast to OSPFv2, the LSID has no + addressing semantics." + ::= { ospfv3AreaLsdbEntry 4 } + + -- Note that the OSPF sequence number is a 32-bit signed + -- integer. It starts with the value '80000001'h + -- or -'7FFFFFFF'h, and increments until '7FFFFFFF'h. + -- Thus, a typical sequence number will be very negative. + + ospfv3AreaLsdbSequence OBJECT-TYPE + SYNTAX Ospfv3LsaSequenceTC + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The sequence number field is a signed 32-bit + integer. It is used to detect old and + duplicate link state advertisements. The space + of sequence numbers is linearly ordered. The + larger the sequence number, the more recent the + advertisement." + REFERENCE + "OSPF Version 2, Section 12.1.6, LS sequence + number" + ::= { ospfv3AreaLsdbEntry 5 } + + ospfv3AreaLsdbAge OBJECT-TYPE + SYNTAX Ospfv3LsaAgeTC + UNITS "seconds" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "This field is the age of the link state + advertisement in seconds. The high-order bit + of the LS age field is considered the DoNotAge + bit for support of on-demand circuits." + REFERENCE + "OSPF Version 2, Section 12.1.1, LS age; + Extending OSPF to Support Demand Circuits, + Section 2.2, The LS age field." + ::= { ospfv3AreaLsdbEntry 6 } + + ospfv3AreaLsdbChecksum OBJECT-TYPE + SYNTAX Integer32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "This field is the checksum of the complete + contents of the advertisement, excepting the + age field. The age field is excepted so that + an advertisement's age can be incremented + without updating the checksum. The checksum + used is the same that is used for ISO + connectionless datagrams; it is commonly + referred to as the Fletcher checksum." + REFERENCE + "OSPF Version 2, Section 12.1.7, LS checksum" + ::= { ospfv3AreaLsdbEntry 7 } + + ospfv3AreaLsdbAdvertisement OBJECT-TYPE + SYNTAX OCTET STRING (SIZE (1..65535)) + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The entire link state advertisement, including + its header." + ::= { ospfv3AreaLsdbEntry 8 } + + ospfv3AreaLsdbTypeKnown OBJECT-TYPE + SYNTAX TruthValue + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The value true (1) indicates that the LSA type is + recognized by this router." + ::= { ospfv3AreaLsdbEntry 9 } + + -- OSPFv3 Link-Scope Link State Database, for non-virtual interfaces + + ospfv3LinkLsdbTable OBJECT-TYPE + SYNTAX SEQUENCE OF Ospfv3LinkLsdbEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The OSPFv3 Process's Link-scope LSDB for non-virtual + interfaces. The LSDB contains the Link-scope link + state advertisements from the interfaces that the + device is attached to." + ::= { ospfv3Objects 5 } + + ospfv3LinkLsdbEntry OBJECT-TYPE + SYNTAX Ospfv3LinkLsdbEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A single Link-scope link state advertisement." + INDEX { ospfv3LinkLsdbIfIndex, + ospfv3LinkLsdbIfInstId, + ospfv3LinkLsdbType, + ospfv3LinkLsdbRouterId, + ospfv3LinkLsdbLsid } + ::= { ospfv3LinkLsdbTable 1 } + + Ospfv3LinkLsdbEntry ::= SEQUENCE { + ospfv3LinkLsdbIfIndex + InterfaceIndex, + ospfv3LinkLsdbIfInstId + Ospfv3IfInstIdTC, + ospfv3LinkLsdbType + Unsigned32, + ospfv3LinkLsdbRouterId + Ospfv3RouterIdTC, + ospfv3LinkLsdbLsid + Ospfv3LsIdTC, + ospfv3LinkLsdbSequence + Ospfv3LsaSequenceTC, + ospfv3LinkLsdbAge + Ospfv3LsaAgeTC, + ospfv3LinkLsdbChecksum + Integer32, + ospfv3LinkLsdbAdvertisement + OCTET STRING, + ospfv3LinkLsdbTypeKnown + TruthValue + } + + ospfv3LinkLsdbIfIndex OBJECT-TYPE + SYNTAX InterfaceIndex + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The identifier of the link from which the LSA + was received." + ::= { ospfv3LinkLsdbEntry 1 } + + ospfv3LinkLsdbIfInstId OBJECT-TYPE + SYNTAX Ospfv3IfInstIdTC + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The identifier of the interface instance from + which the LSA was received." + ::= { ospfv3LinkLsdbEntry 2 } + + ospfv3LinkLsdbType OBJECT-TYPE + SYNTAX Unsigned32(0..'FFFFFFFF'h) + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The type of the link state advertisement. + Each link state type has a separate + advertisement format. Link-scope LSAs unrecognized + by the router are also stored in this database." + ::= { ospfv3LinkLsdbEntry 3 } + + ospfv3LinkLsdbRouterId OBJECT-TYPE + SYNTAX Ospfv3RouterIdTC + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The 32-bit number that uniquely identifies the + originating router in the Autonomous System." + REFERENCE + "OSPF Version 2, Appendix C.1, Global parameters" + ::= { ospfv3LinkLsdbEntry 4 } + + ospfv3LinkLsdbLsid OBJECT-TYPE + SYNTAX Ospfv3LsIdTC + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The Link State ID is an LS type-specific field + containing a unique identifier; + it identifies the piece of the routing domain + that is being described by the advertisement. + In contrast to OSPFv2, the LSID has no + addressing semantics. However, in OSPFv3 + the Link State ID always contains the flooding + scope of the LSA." + ::= { ospfv3LinkLsdbEntry 5 } + + -- Note that the OSPF sequence number is a 32-bit signed + -- integer. It starts with the value '80000001'h + -- or -'7FFFFFFF'h, and increments until '7FFFFFFF'h. + -- Thus, a typical sequence number will be very negative. + + ospfv3LinkLsdbSequence OBJECT-TYPE + SYNTAX Ospfv3LsaSequenceTC + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The sequence number field is a signed 32-bit + integer. It is used to detect old and duplicate + link state advertisements. The space of + sequence numbers is linearly ordered. The + larger the sequence number, the more recent the + advertisement." + REFERENCE + "OSPF Version 2, Section 12.1.6, LS sequence + number" + ::= { ospfv3LinkLsdbEntry 6 } + + ospfv3LinkLsdbAge OBJECT-TYPE + SYNTAX Ospfv3LsaAgeTC + UNITS "seconds" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "This field is the age of the link state + advertisement in seconds. The high-order bit + of the LS age field is considered the DoNotAge + bit for support of on-demand circuits." + REFERENCE + "OSPF Version 2, Section 12.1.1, LS age; + Extending OSPF to Support Demand Circuits, + Section 2.2, The LS age field." + ::= { ospfv3LinkLsdbEntry 7 } + + ospfv3LinkLsdbChecksum OBJECT-TYPE + SYNTAX Integer32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "This field is the checksum of the complete + contents of the advertisement, excepting the + age field. The age field is excepted so that + an advertisement's age can be incremented + without updating the checksum. The checksum + used is the same that is used for ISO + connectionless datagrams; it is commonly + referred to as the Fletcher checksum." + REFERENCE + "OSPF Version 2, Section 12.1.7, LS checksum" + ::= { ospfv3LinkLsdbEntry 8 } + + ospfv3LinkLsdbAdvertisement OBJECT-TYPE + SYNTAX OCTET STRING (SIZE (1..65535)) + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The entire link state advertisement, including + its header." + ::= { ospfv3LinkLsdbEntry 9 } + + ospfv3LinkLsdbTypeKnown OBJECT-TYPE + SYNTAX TruthValue + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The value true (1) indicates that the LSA type is + recognized by this router." + ::= { ospfv3LinkLsdbEntry 10 } + + -- OSPF Host Table + + ospfv3HostTable OBJECT-TYPE + SYNTAX SEQUENCE OF Ospfv3HostEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The Host/Metric Table indicates what hosts are + directly attached to the router and their + corresponding metrics." + REFERENCE + "OSPF Version 2, Appendix C.7, Host route + parameters" + ::= { ospfv3Objects 6 } + + ospfv3HostEntry OBJECT-TYPE + SYNTAX Ospfv3HostEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A metric to be advertised when a given host is + reachable. + + The information in this table is persistent, and + when written, the entity SHOULD save the change + to non-volatile storage." + INDEX { ospfv3HostAddressType, + ospfv3HostAddress } + ::= { ospfv3HostTable 1 } + + Ospfv3HostEntry ::= SEQUENCE { + ospfv3HostAddressType + InetAddressType, + ospfv3HostAddress + InetAddress, + ospfv3HostMetric + Metric, + ospfv3HostRowStatus + RowStatus, + ospfv3HostAreaID + Ospfv3AreaIdTC + } + + ospfv3HostAddressType OBJECT-TYPE + SYNTAX InetAddressType + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The address type of ospfv3HostAddress. Only IPv6 + global address type is expected." + REFERENCE + "OSPF Version 2, Appendix C.7, Host route + parameters" + ::= { ospfv3HostEntry 1 } + + ospfv3HostAddress OBJECT-TYPE + SYNTAX InetAddress + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The IPv6 address of the host. Must be an + IPv6 global address." + REFERENCE + "OSPF Version 2, Appendix C.7, Host route + parameters" + ::= { ospfv3HostEntry 2 } + + ospfv3HostMetric OBJECT-TYPE + SYNTAX Metric + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The metric to be advertised." + REFERENCE + "OSPF Version 2, Appendix C.7, Host route + parameters" + ::= { ospfv3HostEntry 3 } + + ospfv3HostRowStatus OBJECT-TYPE + SYNTAX RowStatus + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "This object permits management of the table by + facilitating actions such as row creation, + construction, and destruction. + + The value of this object has no effect on + whether other objects in this conceptual row can be + modified." + ::= { ospfv3HostEntry 4 } + + ospfv3HostAreaID OBJECT-TYPE + SYNTAX Ospfv3AreaIdTC + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The Area the host entry is to be found within. + By default, the area for the subsuming OSPFv3 + interface, or Area 0 if there is no subsuming + interface." + REFERENCE + "OSPF Version 2, Appendix C.2, Area parameters" + ::= { ospfv3HostEntry 5 } + + -- OSPFv3 Interface Table + + ospfv3IfTable OBJECT-TYPE + SYNTAX SEQUENCE OF Ospfv3IfEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The OSPFv3 Interface Table describes the + interfaces from the viewpoint of OSPFv3." + REFERENCE + "OSPF for IPv6, Appendix C.3, Router Interface + Parameters" + ::= { ospfv3Objects 7 } + + ospfv3IfEntry OBJECT-TYPE + SYNTAX Ospfv3IfEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The OSPFv3 Interface Entry describes one + interface from the viewpoint of OSPFv3. + + The information in this table is persistent, + and when written, the entity SHOULD save the + change to non-volatile storage." + INDEX { ospfv3IfIndex, + ospfv3IfInstId } + ::= { ospfv3IfTable 1 } + + Ospfv3IfEntry ::= SEQUENCE { + ospfv3IfIndex + InterfaceIndex, + ospfv3IfInstId + Ospfv3IfInstIdTC, + ospfv3IfAreaId + Ospfv3AreaIdTC, + ospfv3IfType + INTEGER, + ospfv3IfAdminStatus + Status, + ospfv3IfRtrPriority + DesignatedRouterPriority, + ospfv3IfTransitDelay + Ospfv3UpToRefreshIntervalTC, + ospfv3IfRetransInterval + Ospfv3UpToRefreshIntervalTC, + ospfv3IfHelloInterval + HelloRange, + ospfv3IfRtrDeadInterval + Ospfv3DeadIntervalRangeTC, + ospfv3IfPollInterval + Unsigned32, + ospfv3IfState + INTEGER, + ospfv3IfDesignatedRouter + Ospfv3RouterIdTC, + ospfv3IfBackupDesignatedRouter + Ospfv3RouterIdTC, + ospfv3IfEvents + Counter32, + ospfv3IfRowStatus + RowStatus, + ospfv3IfDemand + TruthValue, + ospfv3IfMetricValue + Metric, + ospfv3IfLinkScopeLsaCount + Gauge32, + ospfv3IfLinkLsaCksumSum + Unsigned32, + ospfv3IfDemandNbrProbe + TruthValue, + ospfv3IfDemandNbrProbeRetransLimit + Unsigned32, + ospfv3IfDemandNbrProbeInterval + Unsigned32, + ospfv3IfTEDisabled + TruthValue, + ospfv3IfLinkLSASuppression + TruthValue + } + + ospfv3IfIndex OBJECT-TYPE + SYNTAX InterfaceIndex + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The interface index of this OSPFv3 interface. + It corresponds to the interface index of the + IPv6 interface on which OSPFv3 is configured." + ::= { ospfv3IfEntry 1 } + + ospfv3IfInstId OBJECT-TYPE + SYNTAX Ospfv3IfInstIdTC + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Enables multiple interface instances of OSPFv3 + to be run over a single link. Each interface + instance would be assigned a separate ID. This ID + has local link significance only." + ::= { ospfv3IfEntry 2 } + + ospfv3IfAreaId OBJECT-TYPE + SYNTAX Ospfv3AreaIdTC + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "A 32-bit integer uniquely identifying the area + to which the interface connects. Area ID + 0 is used for the OSPFv3 backbone." + DEFVAL { 0 } + ::= { ospfv3IfEntry 3 } + + ospfv3IfType OBJECT-TYPE + SYNTAX INTEGER { + broadcast(1), + nbma(2), + pointToPoint(3), + pointToMultipoint(5) + } + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The OSPFv3 interface type." + ::= { ospfv3IfEntry 4 } + + ospfv3IfAdminStatus OBJECT-TYPE + SYNTAX Status + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The OSPFv3 interface's administrative status. + The value formed on the interface; the interface + will be advertised as an internal route to some + area. The value 'disabled' denotes that the + interface is external to OSPFv3. + + Note that a value of 'disabled' for the object + ospfv3AdminStatus will override a value of + 'enabled' for the interface." + DEFVAL { enabled } + ::= { ospfv3IfEntry 5 } + + ospfv3IfRtrPriority OBJECT-TYPE + SYNTAX DesignatedRouterPriority + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The priority of this interface. Used in + multi-access networks, this field is used in + the designated-router election algorithm. The + value 0 signifies that the router is not + eligible to become the Designated Router on this + particular network. In the event of a tie in + this value, routers will use their Router ID as + a tie breaker." + DEFVAL { 1 } + ::= { ospfv3IfEntry 6 } + + ospfv3IfTransitDelay OBJECT-TYPE + SYNTAX Ospfv3UpToRefreshIntervalTC + UNITS "seconds" + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The estimated number of seconds it takes to transmit + a Link State Update packet over this interface. LSAs + contained in the update packet must have their age + incremented by this amount before transmission. This + value should take into account the transmission and + propagation delays of the interface." + REFERENCE + "OSPF for IPv6, Appendix C.3, Router Interface + Parameters." + DEFVAL { 1 } + ::= { ospfv3IfEntry 7 } + + ospfv3IfRetransInterval OBJECT-TYPE + SYNTAX Ospfv3UpToRefreshIntervalTC + UNITS "seconds" + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The number of seconds between link state + advertisement retransmissions for adjacencies + + belonging to this interface. This value is + also used when retransmitting database + description and Link State Request packets." + DEFVAL { 5 } + ::= { ospfv3IfEntry 8 } + + ospfv3IfHelloInterval OBJECT-TYPE + SYNTAX HelloRange + UNITS "seconds" + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The length of time, in seconds, between the + Hello packets that the router sends on the + interface. This value must be the same for all + routers attached to a common network." + DEFVAL { 10 } + ::= { ospfv3IfEntry 9 } + + ospfv3IfRtrDeadInterval OBJECT-TYPE + SYNTAX Ospfv3DeadIntervalRangeTC + UNITS "seconds" + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The number of seconds that a router's Hello + packets have not been seen before its + neighbors declare the router down on the interface. + This should be some multiple of the Hello interval. + This value must be the same for all routers attached + to a common network." + DEFVAL { 40 } + ::= { ospfv3IfEntry 10 } + + ospfv3IfPollInterval OBJECT-TYPE + SYNTAX Unsigned32 + UNITS "seconds" + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The larger time interval, in seconds, between + the Hello packets sent to an inactive, + non-broadcast multi-access neighbor." + DEFVAL { 120 } + ::= { ospfv3IfEntry 11 } + + ospfv3IfState OBJECT-TYPE + SYNTAX INTEGER { + down(1), + loopback(2), + waiting(3), + pointToPoint(4), + designatedRouter(5), + backupDesignatedRouter(6), + otherDesignatedRouter(7), + standby(8) + } + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The OSPFv3 interface state. An interface may be + in standby state if there are multiple interfaces + on the link and another interface is active. The + interface may be in Down state if the underlying + IPv6 interface is down or if the admin status is + 'disabled' either globally or for the interface." + ::= { ospfv3IfEntry 12 } + + ospfv3IfDesignatedRouter OBJECT-TYPE + SYNTAX Ospfv3RouterIdTC + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The Router ID of the Designated Router." + ::= { ospfv3IfEntry 13 } + + ospfv3IfBackupDesignatedRouter OBJECT-TYPE + SYNTAX Ospfv3RouterIdTC + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The Router ID of the Backup Designated + Router." + ::= { ospfv3IfEntry 14 } + + ospfv3IfEvents OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of times this OSPFv3 interface has + changed its state or an error has occurred. + + Discontinuities in the value of this counter + can occur at re-initialization of the management + system and at other times as indicated by the + value of ospfv3DiscontinuityTime." + ::= { ospfv3IfEntry 15 } + + ospfv3IfRowStatus OBJECT-TYPE + SYNTAX RowStatus + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "This object permits management of the table by + facilitating actions such as row creation, + construction, and destruction. + + The value of this object has no effect on + whether other objects in this conceptual row can be + modified." + ::= { ospfv3IfEntry 16 } + + ospfv3IfDemand OBJECT-TYPE + SYNTAX TruthValue + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "Indicates whether Demand OSPFv3 procedures + (Hello suppression to FULL neighbors and + setting the DoNotAge flag on propagated LSAs) + should be performed on this interface." + DEFVAL { false } + ::= { ospfv3IfEntry 17 } + + ospfv3IfMetricValue OBJECT-TYPE + SYNTAX Metric + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The metric assigned to this interface. + The default value of the metric is + 'Reference Bandwidth / ifSpeed'. The value + of the reference bandwidth can be set + in the ospfv3ReferenceBandwidth object." + ::= { ospfv3IfEntry 18 } + + ospfv3IfLinkScopeLsaCount OBJECT-TYPE + SYNTAX Gauge32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of Link-scope link state + advertisements in this link's link state + database." + ::= { ospfv3IfEntry 19 } + + ospfv3IfLinkLsaCksumSum OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The 32-bit unsigned sum of the Link-scope link state + advertisements' LS checksums contained in this + link's link state database. The sum can be used + to determine if there has been a change in a + router's link state database or to compare the + link state database of two routers." + ::= { ospfv3IfEntry 20 } + + ospfv3IfDemandNbrProbe OBJECT-TYPE + SYNTAX TruthValue + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "Indicates whether or not neighbor probing is + enabled to determine whether or not the neighbor + is inactive. Neighbor probing is disabled by + default." + DEFVAL { false } + ::= { ospfv3IfEntry 21 } + +ospfv3IfDemandNbrProbeRetransLimit OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The number of consecutive LSA retransmissions before + the neighbor is deemed inactive and the neighbor + adjacency is brought down." + DEFVAL { 10 } + ::= { ospfv3IfEntry 22} + +ospfv3IfDemandNbrProbeInterval OBJECT-TYPE + SYNTAX Unsigned32 + UNITS "seconds" + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "Defines how often the neighbor will be probed." + DEFVAL { 120 } + ::= { ospfv3IfEntry 23 } + + ospfv3IfTEDisabled OBJECT-TYPE + SYNTAX TruthValue + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "Indicates whether or not traffic engineering + is disabled on the interface when traffic + engineering is enabled in the area where the + interface is attached. The object is set + to the value true (1) to disable traffic engineering + on the interface. Traffic engineering is enabled + by default on the interface when traffic engineering + is enabled in the area where the interface is + attached." + DEFVAL { false } + ::= { ospfv3IfEntry 24 } + + ospfv3IfLinkLSASuppression OBJECT-TYPE + SYNTAX TruthValue + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "Specifies whether or not link LSA origination is + suppressed for broadcast or NBMA interface types. + The object is set to value true (1) to suppress + the origination." + REFERENCE + "OSPF for IPv6, Appendix C.3, Router Interface + Parameters" + DEFVAL { false } + ::= { ospfv3IfEntry 25 } + + -- OSPFv3 Virtual Interface Table + + ospfv3VirtIfTable OBJECT-TYPE + SYNTAX SEQUENCE OF Ospfv3VirtIfEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Information about this router's virtual + interfaces that the OSPFv3 Process is configured + to carry on." + REFERENCE + "OSPF for IPv6, Appendix C.4, Virtual Link + Parameters" + ::= { ospfv3Objects 8 } + + ospfv3VirtIfEntry OBJECT-TYPE + SYNTAX Ospfv3VirtIfEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Information about a single virtual interface. + + The information in this table is persistent, + and when written, the entity SHOULD save the + change to non-volatile storage." + INDEX { ospfv3VirtIfAreaId, + ospfv3VirtIfNeighbor } + ::= { ospfv3VirtIfTable 1 } + + Ospfv3VirtIfEntry ::= SEQUENCE { + ospfv3VirtIfAreaId + Ospfv3AreaIdTC, + ospfv3VirtIfNeighbor + Ospfv3RouterIdTC, + ospfv3VirtIfIndex + InterfaceIndex, + ospfv3VirtIfInstId + Ospfv3IfInstIdTC, + ospfv3VirtIfTransitDelay + Ospfv3UpToRefreshIntervalTC, + ospfv3VirtIfRetransInterval + Ospfv3UpToRefreshIntervalTC, + ospfv3VirtIfHelloInterval + HelloRange, + ospfv3VirtIfRtrDeadInterval + Ospfv3DeadIntervalRangeTC, + ospfv3VirtIfState + INTEGER, + ospfv3VirtIfEvents + Counter32, + ospfv3VirtIfRowStatus + RowStatus, + ospfv3VirtIfLinkScopeLsaCount + Gauge32, + ospfv3VirtIfLinkLsaCksumSum + Unsigned32 + } + + ospfv3VirtIfAreaId OBJECT-TYPE + SYNTAX Ospfv3AreaIdTC + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The transit area that the virtual link + traverses. By definition, this is not + Area 0." + ::= { ospfv3VirtIfEntry 1 } + + ospfv3VirtIfNeighbor OBJECT-TYPE + SYNTAX Ospfv3RouterIdTC + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The Router ID of the virtual neighbor." + ::= { ospfv3VirtIfEntry 2 } + + ospfv3VirtIfIndex OBJECT-TYPE + SYNTAX InterfaceIndex + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The local interface index assigned by the + OSPFv3 Process to this OSPFv3 virtual interface. + It is advertised in Hellos sent over the virtual + link and in the router's router-LSAs." + ::= { ospfv3VirtIfEntry 3 } + + ospfv3VirtIfInstId OBJECT-TYPE + SYNTAX Ospfv3IfInstIdTC + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The local Interface Instance ID assigned by the + OSPFv3 Process to this OSPFv3 virtual interface." + ::= { ospfv3VirtIfEntry 4 } + + ospfv3VirtIfTransitDelay OBJECT-TYPE + SYNTAX Ospfv3UpToRefreshIntervalTC + UNITS "seconds" + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The estimated number of seconds it takes to + transmit a Link State Update packet over this + interface." + DEFVAL { 1 } + ::= { ospfv3VirtIfEntry 5 } + + ospfv3VirtIfRetransInterval OBJECT-TYPE + SYNTAX Ospfv3UpToRefreshIntervalTC + UNITS "seconds" + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The number of seconds between link state + advertisement retransmissions for adjacencies + belonging to this interface. This value is + also used when retransmitting database + description and Link State Request packets. This + value should be well over the expected + round-trip time." + DEFVAL { 5 } + ::= { ospfv3VirtIfEntry 6 } + + ospfv3VirtIfHelloInterval OBJECT-TYPE + SYNTAX HelloRange + UNITS "seconds" + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The length of time, in seconds, between the + Hello packets that the router sends on the + interface. This value must be the same for the + virtual neighbor." + DEFVAL { 10 } + ::= { ospfv3VirtIfEntry 7 } + + ospfv3VirtIfRtrDeadInterval OBJECT-TYPE + SYNTAX Ospfv3DeadIntervalRangeTC + UNITS "seconds" + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The number of seconds that a router's Hello + packets have not been seen before its + neighbors declare the router down. This should + be some multiple of the Hello interval. This + value must be the same for the virtual + neighbor." + DEFVAL { 60 } + ::= { ospfv3VirtIfEntry 8 } + + ospfv3VirtIfState OBJECT-TYPE + SYNTAX INTEGER { + down(1), + pointToPoint(4) + } + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "OSPF virtual interface states. The same encoding + as the ospfV3IfTable is used." + ::= { ospfv3VirtIfEntry 9 } + + ospfv3VirtIfEvents OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of state changes or error events on + this virtual link. + + Discontinuities in the value of this counter + can occur at re-initialization of the management + system and at other times as indicated by the + value of ospfv3DiscontinuityTime." + ::= { ospfv3VirtIfEntry 10 } + + ospfv3VirtIfRowStatus OBJECT-TYPE + SYNTAX RowStatus + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "This object permits management of the table by + facilitating actions such as row creation, + construction, and destruction. + + The value of this object has no effect on + whether other objects in this conceptual row can be + modified." + ::= { ospfv3VirtIfEntry 11 } + + ospfv3VirtIfLinkScopeLsaCount OBJECT-TYPE + SYNTAX Gauge32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of Link-scope link state + advertisements in this virtual link's link state + database." + ::= { ospfv3VirtIfEntry 12 } + + ospfv3VirtIfLinkLsaCksumSum OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The 32-bit unsigned sum of the Link-scope link state + advertisements' LS checksums contained in this + virtual link's link state database. The sum can be used + to determine if there has been a change in a + router's link state database or to compare the + link state database of two routers." + ::= { ospfv3VirtIfEntry 13 } + + -- OSPFv3 Neighbor Table + + ospfv3NbrTable OBJECT-TYPE + SYNTAX SEQUENCE OF Ospfv3NbrEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A table describing all neighbors in the + locality of the OSPFv3 router." + REFERENCE + "OSPF Version 2, Section 10, The Neighbor Data + Structure" + ::= { ospfv3Objects 9 } + + ospfv3NbrEntry OBJECT-TYPE + SYNTAX Ospfv3NbrEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The information regarding a single neighbor." + REFERENCE + "OSPF Version 2, Section 10, The Neighbor Data + Structure" + INDEX { ospfv3NbrIfIndex, + ospfv3NbrIfInstId, + ospfv3NbrRtrId } + ::= { ospfv3NbrTable 1 } + + Ospfv3NbrEntry ::= SEQUENCE { + ospfv3NbrIfIndex + InterfaceIndex, + ospfv3NbrIfInstId + Ospfv3IfInstIdTC, + ospfv3NbrRtrId + Ospfv3RouterIdTC, + ospfv3NbrAddressType + InetAddressType, + ospfv3NbrAddress + InetAddress, + ospfv3NbrOptions + Integer32, + ospfv3NbrPriority + DesignatedRouterPriority, + ospfv3NbrState + INTEGER, + ospfv3NbrEvents + Counter32, + ospfv3NbrLsRetransQLen + Gauge32, + ospfv3NbrHelloSuppressed + TruthValue, + ospfv3NbrIfId + InterfaceIndex, + ospfv3NbrRestartHelperStatus + INTEGER, + ospfv3NbrRestartHelperAge + Ospfv3UpToRefreshIntervalTC, + ospfv3NbrRestartHelperExitReason + INTEGER + } + + ospfv3NbrIfIndex OBJECT-TYPE + SYNTAX InterfaceIndex + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The Local Link ID of the link over which the + neighbor can be reached." + ::= { ospfv3NbrEntry 1 } + + ospfv3NbrIfInstId OBJECT-TYPE + SYNTAX Ospfv3IfInstIdTC + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Interface instance over which the neighbor + can be reached. This ID has local link + significance only." + ::= { ospfv3NbrEntry 2 } + + ospfv3NbrRtrId OBJECT-TYPE + SYNTAX Ospfv3RouterIdTC + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A 32-bit unsigned integer uniquely identifying the + neighboring router in the Autonomous System." + ::= { ospfv3NbrEntry 3 } + + ospfv3NbrAddressType OBJECT-TYPE + SYNTAX InetAddressType + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The address type of ospfv3NbrAddress. Only IPv6 + addresses without zone index are expected." + ::= { ospfv3NbrEntry 4 } + + ospfv3NbrAddress OBJECT-TYPE + SYNTAX InetAddress + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The IPv6 address of the neighbor associated with + the local link." + ::= { ospfv3NbrEntry 5 } + + ospfv3NbrOptions OBJECT-TYPE + SYNTAX Integer32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "A bit mask corresponding to the neighbor's + options field." + REFERENCE + "OSPF for IPv6, Appendix A.2, The Options Field" + ::= { ospfv3NbrEntry 6 } + + ospfv3NbrPriority OBJECT-TYPE + SYNTAX DesignatedRouterPriority + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The priority of this neighbor in the designated- + router election algorithm. The value 0 signifies + that the neighbor is not eligible to become the + Designated Router on this particular network." + ::= { ospfv3NbrEntry 7 } + + ospfv3NbrState OBJECT-TYPE + SYNTAX INTEGER { + down(1), + attempt(2), + init(3), + twoWay(4), + exchangeStart(5), + exchange(6), + loading(7), + full(8) + } + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The state of the relationship with this + neighbor." + REFERENCE + "OSPF Version 2, Section 10.1, Neighbor states" + ::= { ospfv3NbrEntry 8 } + + ospfv3NbrEvents OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of times this neighbor relationship + has changed state or an error has occurred. + + Discontinuities in the value of this counter + can occur at re-initialization of the management + system and at other times as indicated by the + value of ospfv3DiscontinuityTime." + ::= { ospfv3NbrEntry 9 } + + ospfv3NbrLsRetransQLen OBJECT-TYPE + SYNTAX Gauge32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The current length of the retransmission + queue." + ::= { ospfv3NbrEntry 10 } + + ospfv3NbrHelloSuppressed OBJECT-TYPE + SYNTAX TruthValue + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Indicates whether Hellos are being suppressed + to the neighbor." + ::= { ospfv3NbrEntry 11 } + + ospfv3NbrIfId OBJECT-TYPE + SYNTAX InterfaceIndex + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The Interface ID that the neighbor advertises + in its Hello packets on this link, that is, the + neighbor's local interface index." + ::= { ospfv3NbrEntry 12 } + + ospfv3NbrRestartHelperStatus OBJECT-TYPE + SYNTAX INTEGER { notHelping(1), + helping(2) + } + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Indicates whether the router is acting + as a graceful restart helper for the neighbor." + ::= { ospfv3NbrEntry 13 } + + ospfv3NbrRestartHelperAge OBJECT-TYPE + SYNTAX Ospfv3UpToRefreshIntervalTC + UNITS "seconds" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Remaining time in current OSPF graceful restart + interval, if the router is acting as a restart + helper for the neighbor." + ::= { ospfv3NbrEntry 14 } + + ospfv3NbrRestartHelperExitReason OBJECT-TYPE + SYNTAX INTEGER { none(1), + inProgress(2), + completed(3), + timedOut(4), + topologyChanged(5) + } + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Describes the outcome of the last attempt at acting + as a graceful restart helper for the neighbor. + + none: no restart has yet been attempted. + inProgress: a restart attempt is currently underway. + completed: the last restart completed successfully. + timedOut: the last restart timed out. + topologyChanged: the last restart was aborted due to + a topology change." + ::= { ospfv3NbrEntry 15 } + + -- OSPFv3 Configured Neighbor Table + + ospfv3CfgNbrTable OBJECT-TYPE + SYNTAX SEQUENCE OF Ospfv3CfgNbrEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A table describing all configured neighbors. + + The Configured Neighbors table just gives + OSPFv3 information for sending OSPFv3 packets + to potential neighbors and is typically used + on NBMA and Point-to-Multipoint networks. + Once a Hello is received from a neighbor in + the Configured Neighbor table, an entry for + that neighbor is created in the Neighbor table + and adjacency state is maintained there. + Neighbors on multi-access or Point-to-Point + networks can use multicast addressing, so only + Neighbor table entries are created for them." + REFERENCE + "OSPF Version 2, Section 10, The Neighbor Data + Structure" + ::= { ospfv3Objects 10 } + + ospfv3CfgNbrEntry OBJECT-TYPE + SYNTAX Ospfv3CfgNbrEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The information regarding a single configured + neighbor. + + The information in this table is persistent, + and when written, the entity SHOULD save the + change to non-volatile storage." + REFERENCE + "OSPF Version 2, Section 10, The Neighbor Data + Structure" + INDEX { ospfv3CfgNbrIfIndex, + ospfv3CfgNbrIfInstId, + ospfv3CfgNbrAddressType, + ospfv3CfgNbrAddress } + ::= { ospfv3CfgNbrTable 1 } + + Ospfv3CfgNbrEntry ::= SEQUENCE { + ospfv3CfgNbrIfIndex + InterfaceIndex, + ospfv3CfgNbrIfInstId + Ospfv3IfInstIdTC, + ospfv3CfgNbrAddressType + InetAddressType, + ospfv3CfgNbrAddress + InetAddress, + ospfv3CfgNbrPriority + DesignatedRouterPriority, + ospfv3CfgNbrRowStatus + RowStatus + } + + ospfv3CfgNbrIfIndex OBJECT-TYPE + SYNTAX InterfaceIndex + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The Local Link ID of the link over which the + neighbor can be reached." + ::= { ospfv3CfgNbrEntry 1 } + + ospfv3CfgNbrIfInstId OBJECT-TYPE + SYNTAX Ospfv3IfInstIdTC + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Interface instance over which the neighbor + can be reached. This ID has local link + significance only." + ::= { ospfv3CfgNbrEntry 2 } + + ospfv3CfgNbrAddressType OBJECT-TYPE + SYNTAX InetAddressType + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The address type of ospfv3NbrAddress. Only IPv6 + addresses without zone index are expected." + ::= { ospfv3CfgNbrEntry 3 } + + ospfv3CfgNbrAddress OBJECT-TYPE + SYNTAX InetAddress + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The IPv6 address of the neighbor associated with + the local link." + ::= { ospfv3CfgNbrEntry 4 } + + ospfv3CfgNbrPriority OBJECT-TYPE + SYNTAX DesignatedRouterPriority + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The priority of this neighbor in the designated- + router election algorithm. The value 0 signifies + that the neighbor is not eligible to become the + Designated Router on this particular network." + DEFVAL { 1 } + ::= { ospfv3CfgNbrEntry 5 } + + ospfv3CfgNbrRowStatus OBJECT-TYPE + SYNTAX RowStatus + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "This object permits management of the table by + facilitating actions such as row creation, + construction, and destruction. + + The value of this object has no effect on + whether other objects in this conceptual row can be + modified." + ::= { ospfv3CfgNbrEntry 6 } + + -- OSPFv3 Virtual Neighbor Table + + ospfv3VirtNbrTable OBJECT-TYPE + SYNTAX SEQUENCE OF Ospfv3VirtNbrEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A table describing all virtual neighbors." + REFERENCE + "OSPF Version 2, Section 15, Virtual Links" + ::= { ospfv3Objects 11 } + + ospfv3VirtNbrEntry OBJECT-TYPE + SYNTAX Ospfv3VirtNbrEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Virtual neighbor information." + INDEX { ospfv3VirtNbrArea, + ospfv3VirtNbrRtrId } + ::= { ospfv3VirtNbrTable 1 } + + Ospfv3VirtNbrEntry ::= SEQUENCE { + ospfv3VirtNbrArea + Ospfv3AreaIdTC, + ospfv3VirtNbrRtrId + Ospfv3RouterIdTC, + ospfv3VirtNbrIfIndex + InterfaceIndex, + ospfv3VirtNbrIfInstId + Ospfv3IfInstIdTC, + ospfv3VirtNbrAddressType + InetAddressType, + ospfv3VirtNbrAddress + InetAddress, + ospfv3VirtNbrOptions + Integer32, + ospfv3VirtNbrState + INTEGER, + ospfv3VirtNbrEvents + Counter32, + ospfv3VirtNbrLsRetransQLen + Gauge32, + ospfv3VirtNbrHelloSuppressed + TruthValue, + ospfv3VirtNbrIfId + InterfaceIndex, + ospfv3VirtNbrRestartHelperStatus + INTEGER, + ospfv3VirtNbrRestartHelperAge + Ospfv3UpToRefreshIntervalTC, + ospfv3VirtNbrRestartHelperExitReason + INTEGER + } + + ospfv3VirtNbrArea OBJECT-TYPE + SYNTAX Ospfv3AreaIdTC + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The transit area Identifier." + ::= { ospfv3VirtNbrEntry 1 } + + ospfv3VirtNbrRtrId OBJECT-TYPE + SYNTAX Ospfv3RouterIdTC + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A 32-bit integer uniquely identifying the + neighboring router in the Autonomous System." + ::= { ospfv3VirtNbrEntry 2 } + + ospfv3VirtNbrIfIndex OBJECT-TYPE + SYNTAX InterfaceIndex + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The local Interface ID for the virtual link over + which the neighbor can be reached." + ::= { ospfv3VirtNbrEntry 3 } + + ospfv3VirtNbrIfInstId OBJECT-TYPE + SYNTAX Ospfv3IfInstIdTC + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The interface instance for the virtual link over + which the neighbor can be reached." + ::= { ospfv3VirtNbrEntry 4 } + + ospfv3VirtNbrAddressType OBJECT-TYPE + SYNTAX InetAddressType + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The address type of ospfv3VirtNbrAddress. Only IPv6 + addresses without zone index are expected." + ::= { ospfv3VirtNbrEntry 5 } + + ospfv3VirtNbrAddress OBJECT-TYPE + SYNTAX InetAddress + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The IPv6 address advertised by this virtual neighbor. + It must be a global scope address." + ::= { ospfv3VirtNbrEntry 6 } + + ospfv3VirtNbrOptions OBJECT-TYPE + SYNTAX Integer32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "A bit mask corresponding to the neighbor's options + field." + REFERENCE + "OSPF for IPv6, Appendix A.2, The Options Field" + ::= { ospfv3VirtNbrEntry 7 } + + ospfv3VirtNbrState OBJECT-TYPE + SYNTAX INTEGER { + down(1), + attempt(2), + init(3), + twoWay(4), + exchangeStart(5), + exchange(6), + loading(7), + full(8) + } + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The state of the virtual neighbor relationship." + ::= { ospfv3VirtNbrEntry 8 } + + ospfv3VirtNbrEvents OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of times this virtual link has + changed its state or an error has occurred. + + Discontinuities in the value of this counter + can occur at re-initialization of the management + system and at other times as indicated by the + value of ospfv3DiscontinuityTime." + ::= { ospfv3VirtNbrEntry 9 } + + ospfv3VirtNbrLsRetransQLen OBJECT-TYPE + SYNTAX Gauge32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The current length of the retransmission + queue." + ::= { ospfv3VirtNbrEntry 10 } + + ospfv3VirtNbrHelloSuppressed OBJECT-TYPE + SYNTAX TruthValue + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Indicates whether Hellos are being suppressed + to the neighbor." + ::= { ospfv3VirtNbrEntry 11 } + + ospfv3VirtNbrIfId OBJECT-TYPE + SYNTAX InterfaceIndex + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The Interface ID that the neighbor advertises + in its Hello packets on this virtual link, that is, + the neighbor's local Interface ID." + ::= { ospfv3VirtNbrEntry 12 } + +ospfv3VirtNbrRestartHelperStatus OBJECT-TYPE + SYNTAX INTEGER { notHelping(1), + helping(2) + } + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Indicates whether the router is acting + as a graceful restart helper for the neighbor." + ::= { ospfv3VirtNbrEntry 13 } + + ospfv3VirtNbrRestartHelperAge OBJECT-TYPE + SYNTAX Ospfv3UpToRefreshIntervalTC + UNITS "seconds" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Remaining time in the current OSPF graceful restart + interval, if the router is acting as a restart + helper for the neighbor." + ::= { ospfv3VirtNbrEntry 14 } + + ospfv3VirtNbrRestartHelperExitReason OBJECT-TYPE + SYNTAX INTEGER { none(1), + inProgress(2), + completed(3), + timedOut(4), + topologyChanged(5) + } + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "Describes the outcome of the last attempt at acting + as a graceful restart helper for the neighbor. + + none: no restart has yet been attempted. + inProgress: a restart attempt is currently underway. + completed: the last restart completed successfully. + timedOut: the last restart timed out. + topologyChanged: the last restart was aborted due to + a topology change." + ::= { ospfv3VirtNbrEntry 15 } + + -- + -- The OSPFv3 Area Aggregate Table + -- + + ospfv3AreaAggregateTable OBJECT-TYPE + SYNTAX SEQUENCE OF Ospfv3AreaAggregateEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The Area Aggregate Table acts as an adjunct + to the Area Table. It describes those address + aggregates that are configured to be propagated + from an area. Its purpose is to reduce the amount + of information that is known beyond an area's + borders. + + A range of IPv6 prefixes specified by a + prefix / prefix length pair. Note that if + ranges are configured such that one range + subsumes another range, the most specific + match is the preferred one." + ::= { ospfv3Objects 12 } + + ospfv3AreaAggregateEntry OBJECT-TYPE + SYNTAX Ospfv3AreaAggregateEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A single area aggregate entry. + + Information in this table is persistent, and + when this object is written, the entity SHOULD + save the change to non-volatile storage." + REFERENCE + "OSPF Version 2, Appendix C.2, Area parameters" + INDEX { ospfv3AreaAggregateAreaID, + ospfv3AreaAggregateAreaLsdbType, + ospfv3AreaAggregatePrefixType, + ospfv3AreaAggregatePrefix, + ospfv3AreaAggregatePrefixLength } + ::= { ospfv3AreaAggregateTable 1 } + + Ospfv3AreaAggregateEntry ::= SEQUENCE { + ospfv3AreaAggregateAreaID + Ospfv3AreaIdTC, + ospfv3AreaAggregateAreaLsdbType + INTEGER, + ospfv3AreaAggregatePrefixType + InetAddressType, + ospfv3AreaAggregatePrefix + InetAddress, + ospfv3AreaAggregatePrefixLength + InetAddressPrefixLength, + ospfv3AreaAggregateRowStatus + RowStatus, + ospfv3AreaAggregateEffect + INTEGER, + ospfv3AreaAggregateRouteTag + Unsigned32 + } + + ospfv3AreaAggregateAreaID OBJECT-TYPE + SYNTAX Ospfv3AreaIdTC + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The area the Address Aggregate is to be found + within." + REFERENCE + "OSPF Version 2, Appendix C.2, Area parameters" + ::= { ospfv3AreaAggregateEntry 1 } + + ospfv3AreaAggregateAreaLsdbType OBJECT-TYPE + SYNTAX INTEGER { + interAreaPrefixLsa(8195), -- 0x2003 + nssaExternalLsa(8199) -- 0x2007 + } + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The type of the Address Aggregate. This field + specifies the Area LSDB type that this Address + Aggregate applies to." + REFERENCE + "OSPF Version 2, Appendix A.4.1, The LSA header" + ::= { ospfv3AreaAggregateEntry 2 } + + ospfv3AreaAggregatePrefixType OBJECT-TYPE + SYNTAX InetAddressType + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The prefix type of ospfv3AreaAggregatePrefix. Only + IPv6 addresses are expected." + ::= { ospfv3AreaAggregateEntry 3 } + + ospfv3AreaAggregatePrefix OBJECT-TYPE + SYNTAX InetAddress (SIZE (0..16)) + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The IPv6 prefix." + REFERENCE + "OSPF Version 2, Appendix C.2, Area parameters" + ::= { ospfv3AreaAggregateEntry 4 } + + ospfv3AreaAggregatePrefixLength OBJECT-TYPE + SYNTAX InetAddressPrefixLength (3..128) + UNITS "bits" + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The length of the prefix (in bits). A prefix can + not be shorter than 3 bits." + REFERENCE + "OSPF Version 2, Appendix C.2, Area parameters" + ::= { ospfv3AreaAggregateEntry 5 } + + ospfv3AreaAggregateRowStatus OBJECT-TYPE + SYNTAX RowStatus + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "This object permits management of the table by + facilitating actions such as row creation, + construction, and destruction. + + The value of this object has no effect on + whether other objects in this conceptual row can be + modified." + ::= { ospfv3AreaAggregateEntry 6 } + + ospfv3AreaAggregateEffect OBJECT-TYPE + SYNTAX INTEGER { + advertiseMatching(1), + doNotAdvertiseMatching(2) + } + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "Prefixes subsumed by ranges will either trigger the + advertisement of the indicated aggregate + (advertiseMatching) or result in the prefix not + being advertised at all outside the area." + DEFVAL { advertiseMatching } + ::= { ospfv3AreaAggregateEntry 7 } + + ospfv3AreaAggregateRouteTag OBJECT-TYPE + SYNTAX Unsigned32 + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "This tag is advertised only in the summarized + As-External LSA when summarizing from NSSA-LSAs to + AS-External-LSAs." + DEFVAL { 0 } + ::= { ospfv3AreaAggregateEntry 8 } + + -- OSPFv3 Link-Scope Link State Database, for virtual interfaces + + ospfv3VirtLinkLsdbTable OBJECT-TYPE + SYNTAX SEQUENCE OF Ospfv3VirtLinkLsdbEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The OSPFv3 Process's Link-scope LSDB for virtual + interfaces. The LSDB contains the Link-scope link + state advertisements from virtual interfaces." + ::= { ospfv3Objects 13 } + + ospfv3VirtLinkLsdbEntry OBJECT-TYPE + SYNTAX Ospfv3VirtLinkLsdbEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A single Link-scope link state advertisement + for a virtual interface." + INDEX { ospfv3VirtLinkLsdbIfAreaId, + ospfv3VirtLinkLsdbIfNeighbor, + ospfv3VirtLinkLsdbType, + ospfv3VirtLinkLsdbRouterId, + ospfv3VirtLinkLsdbLsid } + ::= { ospfv3VirtLinkLsdbTable 1 } + + Ospfv3VirtLinkLsdbEntry ::= SEQUENCE { + ospfv3VirtLinkLsdbIfAreaId + Ospfv3AreaIdTC, + ospfv3VirtLinkLsdbIfNeighbor + Ospfv3RouterIdTC, + ospfv3VirtLinkLsdbType + Unsigned32, + ospfv3VirtLinkLsdbRouterId + Ospfv3RouterIdTC, + ospfv3VirtLinkLsdbLsid + Ospfv3LsIdTC, + ospfv3VirtLinkLsdbSequence + Ospfv3LsaSequenceTC, + ospfv3VirtLinkLsdbAge + Ospfv3LsaAgeTC, + ospfv3VirtLinkLsdbChecksum + Integer32, + ospfv3VirtLinkLsdbAdvertisement + OCTET STRING, + ospfv3VirtLinkLsdbTypeKnown + TruthValue + } + + ospfv3VirtLinkLsdbIfAreaId OBJECT-TYPE + SYNTAX Ospfv3AreaIdTC + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The transit area that the virtual link + traverses. By definition, this is not + Area 0." + ::= { ospfv3VirtLinkLsdbEntry 1 } + + ospfv3VirtLinkLsdbIfNeighbor OBJECT-TYPE + SYNTAX Ospfv3RouterIdTC + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The Router ID of the virtual neighbor." + ::= { ospfv3VirtLinkLsdbEntry 2 } + + ospfv3VirtLinkLsdbType OBJECT-TYPE + SYNTAX Unsigned32(0..'FFFFFFFF'h) + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The type of the link state advertisement. + Each link state type has a separate + advertisement format. Link-scope LSAs unrecognized + by the router are also stored in this database." + ::= { ospfv3VirtLinkLsdbEntry 3 } + + ospfv3VirtLinkLsdbRouterId OBJECT-TYPE + SYNTAX Ospfv3RouterIdTC + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The 32-bit number that uniquely identifies the + originating router in the Autonomous System." + REFERENCE + "OSPF Version 2, Appendix C.1, Global parameters" + ::= { ospfv3VirtLinkLsdbEntry 4 } + + ospfv3VirtLinkLsdbLsid OBJECT-TYPE + SYNTAX Ospfv3LsIdTC + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "The Link State ID is an LS type-specific field + containing a unique identifier; + it identifies the piece of the routing domain + that is being described by the advertisement. + In contrast to OSPFv2, the LSID has no + addressing semantics." + ::= { ospfv3VirtLinkLsdbEntry 5 } + + -- Note that the OSPF sequence number is a 32-bit signed + -- integer. It starts with the value '80000001'h + -- or -'7FFFFFFF'h, and increments until '7FFFFFFF'h. + -- Thus, a typical sequence number will be very negative. + + ospfv3VirtLinkLsdbSequence OBJECT-TYPE + SYNTAX Ospfv3LsaSequenceTC + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The sequence number field is a signed 32-bit + integer. It is used to detect old and duplicate + link state advertisements. The space of + sequence numbers is linearly ordered. The + larger the sequence number, the more recent the + advertisement." + REFERENCE + "OSPF Version 2, Section 12.1.6, LS sequence + number" + ::= { ospfv3VirtLinkLsdbEntry 6 } + + ospfv3VirtLinkLsdbAge OBJECT-TYPE + SYNTAX Ospfv3LsaAgeTC + UNITS "seconds" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "This field is the age of the link state + advertisement in seconds. The high-order bit + of the LS age field is considered the DoNotAge + bit for support of on-demand circuits." + REFERENCE + "OSPF Version 2, Section 12.1.1, LS age; + Extending OSPF to Support Demand Circuits, + Section 2.2, The LS age field." + ::= { ospfv3VirtLinkLsdbEntry 7 } + + ospfv3VirtLinkLsdbChecksum OBJECT-TYPE + SYNTAX Integer32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "This field is the checksum of the complete + contents of the advertisement, excepting the + age field. The age field is excepted so that + an advertisement's age can be incremented + without updating the checksum. The checksum + used is the same that is used for ISO + connectionless datagrams; it is commonly + referred to as the Fletcher checksum." + REFERENCE + "OSPF Version 2, Section 12.1.7, LS checksum" + ::= { ospfv3VirtLinkLsdbEntry 8 } + + ospfv3VirtLinkLsdbAdvertisement OBJECT-TYPE + SYNTAX OCTET STRING (SIZE (1..65535)) + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The entire link state advertisement, including + its header." + ::= { ospfv3VirtLinkLsdbEntry 9 } + + ospfv3VirtLinkLsdbTypeKnown OBJECT-TYPE + SYNTAX TruthValue + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The value true (1) indicates that the LSA type is + recognized by this router." + ::= { ospfv3VirtLinkLsdbEntry 10 } + + -- The Ospfv3 Notification Table + + -- The Ospfv3 Notification Table records fields that are + -- required for notifications. + + ospfv3NotificationEntry OBJECT IDENTIFIER + ::= { ospfv3Objects 14 } + + ospfv3ConfigErrorType OBJECT-TYPE + SYNTAX INTEGER { + badVersion(1), + areaMismatch(2), + unknownNbmaNbr(3), -- Router is DR eligible + unknownVirtualNbr(4), + helloIntervalMismatch(5), + deadIntervalMismatch(6), + optionMismatch(7), + mtuMismatch(8), + duplicateRouterId(9), + noError(10) } + MAX-ACCESS accessible-for-notify + STATUS current + DESCRIPTION + "Potential types of configuration conflicts. + Used by the ospfv3ConfigError and + ospfv3ConfigVirtError notifications." + ::= { ospfv3NotificationEntry 1 } + + ospfv3PacketType OBJECT-TYPE + SYNTAX INTEGER { + hello(1), + dbDescript(2), + lsReq(3), + lsUpdate(4), + lsAck(5), + nullPacket(6) } + MAX-ACCESS accessible-for-notify + STATUS current + DESCRIPTION + "OSPFv3 packet types." + ::= { ospfv3NotificationEntry 2 } + + ospfv3PacketSrc OBJECT-TYPE + SYNTAX InetAddressIPv6 + MAX-ACCESS accessible-for-notify + STATUS current + DESCRIPTION + "The IPv6 address of an inbound packet that cannot + be identified by a neighbor instance. + + Only IPv6 addresses without zone index are expected." + ::= { ospfv3NotificationEntry 3 } + + -- Notification Definitions + + -- The notifications need to be throttled so as to not overwhelm the + -- management agent in case of rapid changes to the OSPFv3 module. + +ospfv3VirtIfStateChange NOTIFICATION-TYPE + OBJECTS { ospfv3RouterId, -- The originator of the notification + ospfv3VirtIfState -- The new state + } + STATUS current + DESCRIPTION + "An ospfv3VirtIfStateChange notification signifies that + there has been a change in the state of an OSPFv3 virtual + interface. + + This notification should be generated when the interface + state regresses (e.g., goes from Point-to-Point to Down) + or progresses to a terminal state (i.e., Point-to-Point)." + ::= { ospfv3Notifications 1 } + +ospfv3NbrStateChange NOTIFICATION-TYPE + OBJECTS { ospfv3RouterId, -- The originator of the notification + ospfv3NbrState -- The new state + + } + STATUS current + DESCRIPTION + "An ospfv3NbrStateChange notification signifies that + there has been a change in the state of a + non-virtual OSPFv3 neighbor. This notification should be + generated when the neighbor state regresses + (e.g., goes from Attempt or Full to 1-Way or + Down) or progresses to a terminal state (e.g., + 2-Way or Full). When a neighbor transitions + from or to Full on non-broadcast multi-access + and broadcast networks, the notification should be + generated by the Designated Router. A Designated + Router transitioning to Down will be noted by + ospfIfStateChange." + ::= { ospfv3Notifications 2 } + +ospfv3VirtNbrStateChange NOTIFICATION-TYPE + OBJECTS { ospfv3RouterId, -- The originator of the notification + ospfv3VirtNbrState -- The new state + } + STATUS current + DESCRIPTION + "An ospfv3VirtNbrStateChange notification signifies + that there has been a change in the state of an OSPFv3 + virtual neighbor. This notification should be generated + when the neighbor state regresses (e.g., goes + from Attempt or Full to 1-Way or Down) or + progresses to a terminal state (e.g., Full)." + ::= { ospfv3Notifications 3 } + +ospfv3IfConfigError NOTIFICATION-TYPE + OBJECTS { ospfv3RouterId, -- The originator of the notification + ospfv3IfState, -- State of the interface + ospfv3PacketSrc, -- IPv6 address of source + ospfv3ConfigErrorType, -- Type of error + ospfv3PacketType -- Type of packet + } + STATUS current + DESCRIPTION + "An ospfv3IfConfigError notification signifies that a + packet has been received on a non-virtual + interface from a router whose configuration + parameters conflict with this router's + configuration parameters. Note that the event + optionMismatch should cause a notification only if it + prevents an adjacency from forming." + ::= { ospfv3Notifications 4 } + +ospfv3VirtIfConfigError NOTIFICATION-TYPE + OBJECTS { ospfv3RouterId, -- The originator of the notification + ospfv3VirtIfState, -- State of the interface + ospfv3ConfigErrorType, -- Type of error + ospfv3PacketType + } + STATUS current + DESCRIPTION + "An ospfv3VirtIfConfigError notification signifies that a + packet has been received on a virtual interface + from a router whose configuration parameters + conflict with this router's configuration + parameters. Note that the event optionMismatch + should cause a notification only if it prevents an + adjacency from forming." + ::= { ospfv3Notifications 5 } + +ospfv3IfRxBadPacket NOTIFICATION-TYPE + OBJECTS { ospfv3RouterId, -- The originator of the notification + ospfv3IfState, -- State of the interface + ospfv3PacketSrc, -- The source IPv6 address + ospfv3PacketType -- Type of packet + } + STATUS current + DESCRIPTION + "An ospfv3IfRxBadPacket notification signifies that an + OSPFv3 packet that cannot be parsed has been received on a + non-virtual interface." + ::= { ospfv3Notifications 6 } + +ospfv3VirtIfRxBadPacket NOTIFICATION-TYPE + OBJECTS { ospfv3RouterId, -- The originator of the notification + ospfv3VirtIfState, -- State of the interface + ospfv3PacketType -- Type of packet + } + STATUS current + DESCRIPTION + "An ospfv3VirtIfRxBadPacket notification signifies + that an OSPFv3 packet that cannot be parsed has been + received on a virtual interface." + ::= { ospfv3Notifications 7 } + +ospfv3LsdbOverflow NOTIFICATION-TYPE + OBJECTS { ospfv3RouterId, -- The originator of the notification + ospfv3ExtAreaLsdbLimit -- Limit on External LSAs + } + STATUS current + DESCRIPTION + "An ospfv3LsdbOverflow notification signifies that the + number of LSAs in the router's link state + database has exceeded ospfv3ExtAreaLsdbLimit." + ::= { ospfv3Notifications 8 } + +ospfv3LsdbApproachingOverflow NOTIFICATION-TYPE + OBJECTS { ospfv3RouterId, -- The originator of the notification + ospfv3ExtAreaLsdbLimit + } + STATUS current + DESCRIPTION + "An ospfv3LsdbApproachingOverflow notification signifies + that the number of LSAs in the router's + link state database has exceeded ninety percent of + ospfv3ExtAreaLsdbLimit." + ::= { ospfv3Notifications 9 } + +ospfv3IfStateChange NOTIFICATION-TYPE + OBJECTS { ospfv3RouterId, -- The originator of the notification + ospfv3IfState -- The new state + } + STATUS current + DESCRIPTION + "An ospfv3IfStateChange notification signifies that there + has been a change in the state of a non-virtual + OSPFv3 interface. This notification should be generated + when the interface state regresses (e.g., goes + from DR to Down) or progresses to a terminal + state (i.e., Point-to-Point, DR Other, DR, or + Backup)." + ::= { ospfv3Notifications 10 } + +ospfv3NssaTranslatorStatusChange NOTIFICATION-TYPE + OBJECTS { ospfv3RouterId, -- The originator of the notification + ospfv3AreaNssaTranslatorState -- new state + } + STATUS current + DESCRIPTION + "An ospfv3NssaTranslatorStatusChange notification + indicates that there has been a change in the router's + ability to translate OSPFv3 NSSA LSAs into OSPFv3 External + LSAs. This notification should be generated when the + Translator Status transitions from or to any defined + status on a per-area basis." + ::= { ospfv3Notifications 11 } + +ospfv3RestartStatusChange NOTIFICATION-TYPE + OBJECTS { ospfv3RouterId, -- The originator of the notification + ospfv3RestartStatus, -- new status + ospfv3RestartInterval, + ospfv3RestartExitReason + } + STATUS current + DESCRIPTION + "An ospfv3RestartStatusChange notification signifies that + there has been a change in the graceful restart + state for the router. This notification should be + generated when the router restart status + changes." + ::= { ospfv3Notifications 12 } + +ospfv3NbrRestartHelperStatusChange NOTIFICATION-TYPE + OBJECTS { ospfv3RouterId, -- The originator of the notification + ospfv3NbrRestartHelperStatus, -- new status + ospfv3NbrRestartHelperAge, + ospfv3NbrRestartHelperExitReason + } + STATUS current + DESCRIPTION + "An ospfv3NbrRestartHelperStatusChange notification + signifies that there has been a change in the + graceful restart helper state for the neighbor. + This notification should be generated when the + neighbor restart helper status transitions for a neighbor." + ::= { ospfv3Notifications 13 } + +ospfv3VirtNbrRestartHelperStatusChange NOTIFICATION-TYPE + OBJECTS { ospfv3RouterId, -- The originator of the notification + ospfv3VirtNbrRestartHelperStatus, -- new status + ospfv3VirtNbrRestartHelperAge, + ospfv3VirtNbrRestartHelperExitReason + } + STATUS current + DESCRIPTION + "An ospfv3VirtNbrRestartHelperStatusChange + notification signifies that there has been a + change in the graceful restart helper state for + the virtual neighbor. This notification should be + generated when the virtual neighbor restart helper status + transitions for a virtual neighbor." + ::= { ospfv3Notifications 14 } + + -- Conformance Information + + ospfv3Groups OBJECT IDENTIFIER ::= { ospfv3Conformance 1 } + ospfv3Compliances OBJECT IDENTIFIER ::= { ospfv3Conformance 2 } + + -- Compliance Statements + + ospfv3FullCompliance MODULE-COMPLIANCE + STATUS current + DESCRIPTION "The compliance statement" + MODULE -- this module + MANDATORY-GROUPS { + ospfv3BasicGroup, + ospfv3AreaGroup, + ospfv3IfGroup, + ospfv3VirtIfGroup, + ospfv3NbrGroup, + ospfv3CfgNbrGroup, + ospfv3VirtNbrGroup, + ospfv3AreaAggregateGroup + } + + GROUP ospfv3AsLsdbGroup + DESCRIPTION + "This group is required for OSPFv3 systems that + display their AS-scope link state database." + + GROUP ospfv3AreaLsdbGroup + DESCRIPTION + "This group is required for OSPFv3 systems that + display their Area-scope link state database." + + GROUP ospfv3LinkLsdbGroup + DESCRIPTION + "This group is required for OSPFv3 systems that + display their Link-scope link state database + for non-virtual interfaces." + + GROUP ospfv3VirtLinkLsdbGroup + DESCRIPTION + "This group is required for OSPFv3 systems that + display their Link-scope link state database + for virtual interfaces." + + GROUP ospfv3HostGroup + DESCRIPTION + "This group is required for OSPFv3 systems that + support attached hosts." + + GROUP ospfv3NotificationObjectGroup + DESCRIPTION + "This group is required for OSPFv3 systems that + support OSPFv3 notifications." + + GROUP ospfv3NotificationGroup + DESCRIPTION + "This group is required for OSPFv3 systems that + support OSPFv3 notifications." + + OBJECT ospfv3NbrAddressType + SYNTAX InetAddressType { ipv6(2) } + DESCRIPTION + "An implementation is only required to support IPv6 + address without zone index." + + OBJECT ospfv3NbrAddress + SYNTAX InetAddress (SIZE (16)) + DESCRIPTION + "An implementation is only required to support IPv6 + address without zone index." + + OBJECT ospfv3VirtNbrAddressType + SYNTAX InetAddressType { ipv6(2) } + DESCRIPTION + "An implementation is only required to support IPv6 + address without zone index." + + OBJECT ospfv3VirtNbrAddress + SYNTAX InetAddress (SIZE (16)) + DESCRIPTION + "An implementation is only required to support IPv6 + address without zone index." + ::= { ospfv3Compliances 1 } + + ospfv3ReadOnlyCompliance MODULE-COMPLIANCE + STATUS current + DESCRIPTION + "When this MIB module is implemented without + support for read-create (i.e., in read-only + mode), the implementation can claim read-only + compliance. Such a device can then be monitored, + but cannot be configured with this MIB." + + MODULE -- this module + MANDATORY-GROUPS { + ospfv3BasicGroup, + ospfv3AreaGroup, + ospfv3IfGroup, + ospfv3VirtIfGroup, + ospfv3NbrGroup, + ospfv3CfgNbrGroup, + ospfv3VirtNbrGroup, + ospfv3AreaAggregateGroup + } + + GROUP ospfv3AsLsdbGroup + DESCRIPTION + "This group is required for OSPFv3 systems that + display their AS-scope link state database." + + GROUP ospfv3AreaLsdbGroup + DESCRIPTION + "This group is required for OSPFv3 systems that + display their Area-scope link state database." + + GROUP ospfv3LinkLsdbGroup + DESCRIPTION + "This group is required for OSPFv3 systems that + display their Link-scope link state database + for non-virtual interfaces." + + GROUP ospfv3VirtLinkLsdbGroup + DESCRIPTION + "This group is required for OSPFv3 systems that + display their Link-scope link state database + for virtual interfaces." + + GROUP ospfv3HostGroup + DESCRIPTION + "This group is required for OSPFv3 systems that + support attached hosts." + + GROUP ospfv3NotificationObjectGroup + DESCRIPTION + "This group is required for OSPFv3 systems that + support OSPFv3 notifications." + + GROUP ospfv3NotificationGroup + DESCRIPTION + "This group is required for OSPFv3 systems that + support OSPFv3 notifications." + + OBJECT ospfv3RouterId + MIN-ACCESS read-only + DESCRIPTION + "Write access is not required." + + OBJECT ospfv3AdminStatus + MIN-ACCESS read-only + DESCRIPTION + "Write access is not required." + + OBJECT ospfv3ExtAreaLsdbLimit + MIN-ACCESS read-only + DESCRIPTION + "Write access is not required." + + OBJECT ospfv3ExitOverflowInterval + MIN-ACCESS read-only + DESCRIPTION + "Write access is not required." + + OBJECT ospfv3DemandExtensions + MIN-ACCESS read-only + DESCRIPTION + "Write access is not required." + + OBJECT ospfv3ReferenceBandwidth + MIN-ACCESS read-only + DESCRIPTION + "Write access is not required." + + OBJECT ospfv3RestartSupport + MIN-ACCESS read-only + DESCRIPTION + "Write access is not required." + + OBJECT ospfv3RestartInterval + MIN-ACCESS read-only + DESCRIPTION + "Write access is not required." + + OBJECT ospfv3RestartStrictLsaChecking + MIN-ACCESS read-only + DESCRIPTION + "Write access is not required." + + OBJECT ospfv3NotificationEnable + MIN-ACCESS read-only + DESCRIPTION + "Write access is not required." + + OBJECT ospfv3StubRouterAdvertisement + MIN-ACCESS read-only + DESCRIPTION + "Write access is not required." + + OBJECT ospfv3AreaImportAsExtern + MIN-ACCESS read-only + DESCRIPTION + "Write access is not required." + + OBJECT ospfv3AreaSummary + MIN-ACCESS read-only + DESCRIPTION + "Write access is not required." + + OBJECT ospfv3AreaRowStatus + MIN-ACCESS read-only + DESCRIPTION + "Write access is not required." + + OBJECT ospfv3AreaStubMetric + MIN-ACCESS read-only + DESCRIPTION + "Write access is not required." + + OBJECT ospfv3AreaNssaTranslatorRole + MIN-ACCESS read-only + DESCRIPTION + "Write access is not required." + + OBJECT ospfv3AreaNssaTranslatorStabInterval + MIN-ACCESS read-only + DESCRIPTION + "Write access is not required." + + OBJECT ospfv3AreaStubMetricType + MIN-ACCESS read-only + DESCRIPTION + "Write access is not required." + + OBJECT ospfv3AreaTEEnabled + MIN-ACCESS read-only + DESCRIPTION + "Write access is not required." + + OBJECT ospfv3HostMetric + MIN-ACCESS read-only + DESCRIPTION + "Write access is not required." + + OBJECT ospfv3HostRowStatus + MIN-ACCESS read-only + DESCRIPTION + "Write access is not required." + + OBJECT ospfv3HostAreaID + MIN-ACCESS read-only + DESCRIPTION + "Write access is not required." + + OBJECT ospfv3IfAreaId + MIN-ACCESS read-only + DESCRIPTION + "Write access is not required." + + OBJECT ospfv3IfType + MIN-ACCESS read-only + DESCRIPTION + "Write access is not required." + + OBJECT ospfv3IfAdminStatus + MIN-ACCESS read-only + DESCRIPTION + "Write access is not required." + + OBJECT ospfv3IfRtrPriority + MIN-ACCESS read-only + DESCRIPTION + "Write access is not required." + + OBJECT ospfv3IfTransitDelay + MIN-ACCESS read-only + DESCRIPTION + "Write access is not required." + + OBJECT ospfv3IfRetransInterval + MIN-ACCESS read-only + DESCRIPTION + "Write access is not required." + + OBJECT ospfv3IfHelloInterval + MIN-ACCESS read-only + DESCRIPTION + "Write access is not required." + + OBJECT ospfv3IfRtrDeadInterval + MIN-ACCESS read-only + DESCRIPTION + "Write access is not required." + + OBJECT ospfv3IfPollInterval + MIN-ACCESS read-only + DESCRIPTION + "Write access is not required." + + OBJECT ospfv3IfRowStatus + MIN-ACCESS read-only + DESCRIPTION + "Write access is not required." + + OBJECT ospfv3IfDemand + MIN-ACCESS read-only + DESCRIPTION + "Write access is not required." + + OBJECT ospfv3IfMetricValue + MIN-ACCESS read-only + DESCRIPTION + "Write access is not required." + + OBJECT ospfv3IfDemandNbrProbe + MIN-ACCESS read-only + DESCRIPTION + "Write access is not required." + + OBJECT ospfv3IfDemandNbrProbeRetransLimit + MIN-ACCESS read-only + DESCRIPTION + "Write access is not required." + + OBJECT ospfv3IfDemandNbrProbeInterval + MIN-ACCESS read-only + DESCRIPTION + "Write access is not required." + + OBJECT ospfv3IfTEDisabled + MIN-ACCESS read-only + DESCRIPTION + "Write access is not required." + + OBJECT ospfv3IfLinkLSASuppression + MIN-ACCESS read-only + DESCRIPTION + "Write access is not required." + + OBJECT ospfv3VirtIfTransitDelay + MIN-ACCESS read-only + DESCRIPTION + "Write access is not required." + + OBJECT ospfv3VirtIfRetransInterval + MIN-ACCESS read-only + DESCRIPTION + "Write access is not required." + + OBJECT ospfv3VirtIfHelloInterval + MIN-ACCESS read-only + DESCRIPTION + "Write access is not required." + + OBJECT ospfv3VirtIfRtrDeadInterval + MIN-ACCESS read-only + DESCRIPTION + "Write access is not required." + + OBJECT ospfv3VirtIfRowStatus + MIN-ACCESS read-only + DESCRIPTION + "Write access is not required." + + OBJECT ospfv3CfgNbrPriority + MIN-ACCESS read-only + DESCRIPTION + "Write access is not required." + + OBJECT ospfv3CfgNbrRowStatus + MIN-ACCESS read-only + DESCRIPTION + "Write access is not required." + + OBJECT ospfv3AreaAggregateRowStatus + MIN-ACCESS read-only + DESCRIPTION + "Write access is not required." + + OBJECT ospfv3AreaAggregateEffect + MIN-ACCESS read-only + DESCRIPTION + "Write access is not required." + + OBJECT ospfv3AreaAggregateRouteTag + MIN-ACCESS read-only + DESCRIPTION + "Write access is not required." + ::= { ospfv3Compliances 2 } + + -- units of conformance + + ospfv3BasicGroup OBJECT-GROUP + OBJECTS { + ospfv3RouterId, + ospfv3AdminStatus, + ospfv3VersionNumber, + ospfv3AreaBdrRtrStatus, + ospfv3ASBdrRtrStatus, + ospfv3AsScopeLsaCount, + ospfv3AsScopeLsaCksumSum, + ospfv3OriginateNewLsas, + ospfv3RxNewLsas, + ospfv3ExtLsaCount, + ospfv3ExtAreaLsdbLimit, + ospfv3ExitOverflowInterval, + ospfv3DemandExtensions, + ospfv3ReferenceBandwidth, + ospfv3RestartSupport, + ospfv3RestartInterval, + ospfv3RestartStrictLsaChecking, + ospfv3RestartStatus, + ospfv3RestartAge, + ospfv3RestartExitReason, + ospfv3NotificationEnable, + ospfv3StubRouterSupport, + ospfv3StubRouterAdvertisement, + ospfv3DiscontinuityTime, + ospfv3RestartTime + } + STATUS current + DESCRIPTION + "These objects are used for managing/monitoring + OSPFv3 global parameters." + ::= { ospfv3Groups 1 } + + ospfv3AreaGroup OBJECT-GROUP + OBJECTS { + ospfv3AreaImportAsExtern, + ospfv3AreaSpfRuns, + ospfv3AreaBdrRtrCount, + ospfv3AreaAsBdrRtrCount, + ospfv3AreaScopeLsaCount, + ospfv3AreaScopeLsaCksumSum, + ospfv3AreaSummary, + ospfv3AreaRowStatus, + ospfv3AreaStubMetric, + ospfv3AreaNssaTranslatorRole, + ospfv3AreaNssaTranslatorState, + ospfv3AreaNssaTranslatorStabInterval, + ospfv3AreaNssaTranslatorEvents, + ospfv3AreaStubMetricType, + ospfv3AreaTEEnabled + } + STATUS current + DESCRIPTION + "These objects are used for OSPFv3 systems + supporting areas." + ::= { ospfv3Groups 2 } + + ospfv3AsLsdbGroup OBJECT-GROUP + OBJECTS { + ospfv3AsLsdbSequence, + ospfv3AsLsdbAge, + ospfv3AsLsdbChecksum, + ospfv3AsLsdbAdvertisement, + ospfv3AsLsdbTypeKnown + } + STATUS current + DESCRIPTION + "These objects are used for OSPFv3 systems + that display their AS-scope link state database." + ::= { ospfv3Groups 3 } + + ospfv3AreaLsdbGroup OBJECT-GROUP + OBJECTS { + ospfv3AreaLsdbSequence, + ospfv3AreaLsdbAge, + ospfv3AreaLsdbChecksum, + ospfv3AreaLsdbAdvertisement, + ospfv3AreaLsdbTypeKnown + } + STATUS current + DESCRIPTION + "These objects are used for OSPFv3 systems + that display their Area-scope link state database." + ::= { ospfv3Groups 4 } + + ospfv3LinkLsdbGroup OBJECT-GROUP + OBJECTS { + ospfv3LinkLsdbSequence, + ospfv3LinkLsdbAge, + ospfv3LinkLsdbChecksum, + ospfv3LinkLsdbAdvertisement, + ospfv3LinkLsdbTypeKnown + } + STATUS current + DESCRIPTION + "These objects are used for OSPFv3 systems + that display their Link-scope link state database + for non-virtual interfaces." + ::= { ospfv3Groups 5 } + + ospfv3HostGroup OBJECT-GROUP + OBJECTS { + ospfv3HostMetric, + ospfv3HostRowStatus, + ospfv3HostAreaID + } + STATUS current + DESCRIPTION + "These objects are used for OSPFv3 systems + that support attached hosts." + ::= { ospfv3Groups 6 } + + ospfv3IfGroup OBJECT-GROUP + OBJECTS { + ospfv3IfAreaId, + ospfv3IfType, + ospfv3IfAdminStatus, + ospfv3IfRtrPriority, + ospfv3IfTransitDelay, + ospfv3IfRetransInterval, + ospfv3IfHelloInterval, + ospfv3IfRtrDeadInterval, + ospfv3IfPollInterval, + ospfv3IfState, + ospfv3IfDesignatedRouter, + ospfv3IfBackupDesignatedRouter, + ospfv3IfEvents, + ospfv3IfRowStatus, + ospfv3IfDemand, + ospfv3IfMetricValue, + ospfv3IfLinkScopeLsaCount, + ospfv3IfLinkLsaCksumSum, + ospfv3IfDemandNbrProbe, + ospfv3IfDemandNbrProbeRetransLimit, + ospfv3IfDemandNbrProbeInterval, + ospfv3IfTEDisabled, + ospfv3IfLinkLSASuppression + } + STATUS current + DESCRIPTION + "These interface objects are used for + managing/monitoring OSPFv3 interfaces." + ::= { ospfv3Groups 7 } + + ospfv3VirtIfGroup OBJECT-GROUP + OBJECTS { + ospfv3VirtIfIndex, + ospfv3VirtIfInstId, + ospfv3VirtIfTransitDelay, + ospfv3VirtIfRetransInterval, + ospfv3VirtIfHelloInterval, + ospfv3VirtIfRtrDeadInterval, + ospfv3VirtIfState, + ospfv3VirtIfEvents, + ospfv3VirtIfRowStatus, + ospfv3VirtIfLinkScopeLsaCount, + ospfv3VirtIfLinkLsaCksumSum + } + STATUS current + DESCRIPTION + "These virtual interface objects are used for + managing/monitoring OSPFv3 virtual interfaces." + ::= { ospfv3Groups 8 } + + ospfv3NbrGroup OBJECT-GROUP + OBJECTS { + ospfv3NbrAddressType, + ospfv3NbrAddress, + ospfv3NbrOptions, + ospfv3NbrPriority, + ospfv3NbrState, + ospfv3NbrEvents, + ospfv3NbrLsRetransQLen, + ospfv3NbrHelloSuppressed, + ospfv3NbrIfId, + ospfv3NbrRestartHelperStatus, + ospfv3NbrRestartHelperAge, + ospfv3NbrRestartHelperExitReason + } + STATUS current + DESCRIPTION + "These neighbor objects are used for + managing/monitoring OSPFv3 neighbors." + ::= { ospfv3Groups 9 } + + ospfv3CfgNbrGroup OBJECT-GROUP + OBJECTS { + ospfv3CfgNbrPriority, + ospfv3CfgNbrRowStatus + } + STATUS current + DESCRIPTION + "These configured neighbor objects are used for + managing/monitoring OSPFv3-configured neighbors." + ::= { ospfv3Groups 10 } + + ospfv3VirtNbrGroup OBJECT-GROUP + OBJECTS { + ospfv3VirtNbrIfIndex, + ospfv3VirtNbrIfInstId, + ospfv3VirtNbrAddressType, + ospfv3VirtNbrAddress, + ospfv3VirtNbrOptions, + ospfv3VirtNbrState, + ospfv3VirtNbrEvents, + ospfv3VirtNbrLsRetransQLen, + ospfv3VirtNbrHelloSuppressed, + ospfv3VirtNbrIfId, + ospfv3VirtNbrRestartHelperStatus, + ospfv3VirtNbrRestartHelperAge, + ospfv3VirtNbrRestartHelperExitReason + } + STATUS current + DESCRIPTION + "These virtual neighbor objects are used for + managing/monitoring OSPFv3 virtual neighbors." + ::= { ospfv3Groups 11 } + + ospfv3AreaAggregateGroup OBJECT-GROUP + OBJECTS { + ospfv3AreaAggregateRowStatus, + ospfv3AreaAggregateEffect, + ospfv3AreaAggregateRouteTag + } + STATUS current + DESCRIPTION + "These area aggregate objects are required for + aggregating OSPFv3 prefixes for summarization + across areas." + ::= { ospfv3Groups 12 } + + ospfv3VirtLinkLsdbGroup OBJECT-GROUP + OBJECTS { + ospfv3VirtLinkLsdbSequence, + ospfv3VirtLinkLsdbAge, + ospfv3VirtLinkLsdbChecksum, + ospfv3VirtLinkLsdbAdvertisement, + ospfv3VirtLinkLsdbTypeKnown + } + STATUS current + DESCRIPTION + "These objects are used for OSPFv3 systems + that display their Link-scope link state database + for virtual interfaces." + ::= { ospfv3Groups 13 } + + ospfv3NotificationObjectGroup OBJECT-GROUP + OBJECTS { + ospfv3ConfigErrorType, + ospfv3PacketType, + ospfv3PacketSrc + } + STATUS current + DESCRIPTION + "These objects are used to record notification + parameters." + ::= { ospfv3Groups 14 } + + ospfv3NotificationGroup NOTIFICATION-GROUP + NOTIFICATIONS { + ospfv3VirtIfStateChange, + ospfv3NbrStateChange, + ospfv3VirtNbrStateChange, + ospfv3IfConfigError, + ospfv3VirtIfConfigError, + ospfv3IfRxBadPacket, + ospfv3VirtIfRxBadPacket, + ospfv3LsdbOverflow, + ospfv3LsdbApproachingOverflow, + ospfv3IfStateChange, + ospfv3NssaTranslatorStatusChange, + ospfv3RestartStatusChange, + ospfv3NbrRestartHelperStatusChange, + ospfv3VirtNbrRestartHelperStatusChange + } + STATUS current + DESCRIPTION + "This group is used for OSPFv3 notifications." + ::= { ospfv3Groups 15 } + + END diff --git a/ospf6d/README b/ospf6d/README index 883486fa0..f5a004646 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 c3a63fe60..7e94cef44 100644 --- a/ospf6d/ospf6_abr.c +++ b/ospf6d/ospf6_abr.c @@ -106,14 +106,14 @@ void ospf6_abr_disable_area (struct ospf6_area *area) { struct ospf6_area *oa; - struct ospf6_route *ro; + struct ospf6_route *ro, *nro; struct ospf6_lsa *old; struct listnode *node, *nnode; /* Withdraw all summary prefixes previously originated */ - for (ro = ospf6_route_head (area->summary_prefix); ro; - ro = ospf6_route_next (ro)) + for (ro = ospf6_route_head (area->summary_prefix); ro; ro = nro) { + nro = ospf6_route_next (ro); old = ospf6_lsdb_lookup (ro->path.origin.type, ro->path.origin.id, area->ospf6->router_id, area->lsdb); if (old) @@ -122,9 +122,9 @@ ospf6_abr_disable_area (struct ospf6_area *area) } /* Withdraw all summary router-routes previously originated */ - for (ro = ospf6_route_head (area->summary_router); ro; - ro = ospf6_route_next (ro)) + for (ro = ospf6_route_head (area->summary_router); ro; ro = nro) { + nro = ospf6_route_next (ro); old = ospf6_lsdb_lookup (ro->path.origin.type, ro->path.origin.id, area->ospf6->router_id, area->lsdb); if (old) @@ -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 816f59645..e5e2660ba 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 9934e6b9c..1861fe7a1 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)) @@ -639,6 +666,8 @@ DEFUN (show_ipv6_ospf6_spf_tree, struct ospf6_route *route; struct prefix prefix; + OSPF6_CMD_CHECK_RUNNING (); + ospf6_linkstate_prefix (ospf6->router_id, htonl (0), &prefix); for (ALL_LIST_ELEMENTS_RO (ospf6->area_list, node, oa)) @@ -674,6 +703,8 @@ DEFUN (show_ipv6_ospf6_area_spf_tree, struct ospf6_route *route; struct prefix prefix; + OSPF6_CMD_CHECK_RUNNING (); + ospf6_linkstate_prefix (ospf6->router_id, htonl (0), &prefix); if (inet_pton (AF_INET, argv[0], &area_id) != 1) @@ -720,6 +751,8 @@ DEFUN (show_ipv6_ospf6_simulate_spf_tree_root, struct ospf6_route_table *spf_table; unsigned char tmp_debug_ospf6_spf = 0; + OSPF6_CMD_CHECK_RUNNING (); + inet_pton (AF_INET, argv[0], &router_id); ospf6_linkstate_prefix (router_id, htonl (0), &prefix); @@ -766,10 +799,6 @@ ospf6_area_init (void) install_element (VIEW_NODE, &show_ipv6_ospf6_area_spf_tree_cmd); install_element (VIEW_NODE, &show_ipv6_ospf6_simulate_spf_tree_root_cmd); - install_element (ENABLE_NODE, &show_ipv6_ospf6_spf_tree_cmd); - install_element (ENABLE_NODE, &show_ipv6_ospf6_area_spf_tree_cmd); - install_element (ENABLE_NODE, &show_ipv6_ospf6_simulate_spf_tree_root_cmd); - install_element (OSPF6_NODE, &area_range_cmd); install_element (OSPF6_NODE, &area_range_advertise_cmd); install_element (OSPF6_NODE, &no_area_range_cmd); diff --git a/ospf6d/ospf6_area.h b/ospf6d/ospf6_area.h index c7c5ee358..655967a9a 100644 --- a/ospf6d/ospf6_area.h +++ b/ospf6d/ospf6_area.h @@ -57,6 +57,7 @@ struct ospf6_area struct thread *thread_spf_calculation; struct thread *thread_route_calculation; + u_int32_t spf_calculation; /* SPF calculation count */ struct thread *thread_router_lsa; struct thread *thread_intra_prefix_lsa; @@ -104,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 ae0a286d5..a442506c7 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)); @@ -98,7 +93,10 @@ ospf6_as_external_lsa_originate (struct ospf6_route *route) UNSET_FLAG (as_external_lsa->bits_metric, OSPF6_ASBR_BIT_F); /* external route tag */ - UNSET_FLAG (as_external_lsa->bits_metric, OSPF6_ASBR_BIT_T); + if (info->tag) + SET_FLAG (as_external_lsa->bits_metric, OSPF6_ASBR_BIT_T); + else + UNSET_FLAG (as_external_lsa->bits_metric, OSPF6_ASBR_BIT_T); /* Set metric */ OSPF6_ASBR_METRIC_SET (as_external_lsa, route->path.cost); @@ -128,7 +126,10 @@ ospf6_as_external_lsa_originate (struct ospf6_route *route) /* External Route Tag */ if (CHECK_FLAG (as_external_lsa->bits_metric, OSPF6_ASBR_BIT_T)) { - /* xxx */ + route_tag_t network_order = htonl(info->tag); + + memcpy (p, &network_order, sizeof(network_order)); + p += sizeof(network_order); } /* Fill LSA Header */ @@ -151,7 +152,30 @@ ospf6_as_external_lsa_originate (struct ospf6_route *route) ospf6_lsa_originate_process (lsa, ospf6); } - +static route_tag_t +ospf6_as_external_lsa_get_tag (struct ospf6_lsa *lsa) +{ + struct ospf6_as_external_lsa *external; + ptrdiff_t tag_offset; + route_tag_t network_order; + + if (!lsa) + return 0; + + external = (struct ospf6_as_external_lsa *) + OSPF6_LSA_HEADER_END (lsa->header); + + if (!CHECK_FLAG (external->bits_metric, OSPF6_ASBR_BIT_T)) + return 0; + + tag_offset = sizeof(*external) + OSPF6_PREFIX_SPACE(external->prefix.prefix_length); + if (CHECK_FLAG (external->bits_metric, OSPF6_ASBR_BIT_F)) + tag_offset += sizeof(struct in6_addr); + + memcpy(&network_order, (caddr_t)external + tag_offset, sizeof(network_order)); + return ntohl(network_order); +} + void ospf6_asbr_lsa_add (struct ospf6_lsa *lsa) { @@ -174,13 +198,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 || @@ -221,6 +252,8 @@ ospf6_asbr_lsa_add (struct ospf6_lsa *lsa) route->path.cost_e2 = 0; } + route->path.tag = ospf6_as_external_lsa_get_tag (lsa); + for (i = 0; i < OSPF6_MULTI_PATH_LIMIT; i++) ospf6_nexthop_copy (&route->nexthop[i], &asbr_entry->nexthop[i]); @@ -238,7 +271,7 @@ ospf6_asbr_lsa_remove (struct ospf6_lsa *lsa) { struct ospf6_as_external_lsa *external; struct prefix prefix; - struct ospf6_route *route; + struct ospf6_route *route, *nroute; char buf[64]; external = (struct ospf6_as_external_lsa *) @@ -272,8 +305,9 @@ ospf6_asbr_lsa_remove (struct ospf6_lsa *lsa) for (ospf6_route_lock (route); route && ospf6_route_is_prefix (&prefix, route); - route = ospf6_route_next (route)) + route = nroute) { + nroute = ospf6_route_next (route); if (route->type != OSPF6_DEST_TYPE_NETWORK) continue; if (route->path.origin.type != lsa->header->type) @@ -290,6 +324,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 +369,7 @@ ospf6_asbr_lsentry_remove (struct ospf6_route *asbr_entry) } - + /* redistribute function */ static void @@ -402,11 +438,13 @@ ospf6_asbr_redistribute_unset (int type) ospf6_asbr_redistribute_remove (info->type, route->nexthop[0].ifindex, &route->prefix); } + + ospf6_asbr_routemap_unset (type); } void -ospf6_asbr_redistribute_add (int type, int ifindex, struct prefix *prefix, - u_int nexthop_num, struct in6_addr *nexthop) +ospf6_asbr_redistribute_add (int type, ifindex_t ifindex, struct prefix *prefix, + u_int nexthop_num, struct in6_addr *nexthop, route_tag_t tag) { int ret; struct ospf6_route troute; @@ -448,6 +486,7 @@ ospf6_asbr_redistribute_add (int type, int ifindex, struct prefix *prefix, memset (&tinfo, 0, sizeof (tinfo)); troute.route_option = &tinfo; tinfo.ifindex = ifindex; + tinfo.tag = tag; ret = route_map_apply (ospf6->rmap[type].map, prefix, RMAP_OSPF6, &troute); @@ -474,6 +513,12 @@ ospf6_asbr_redistribute_add (int type, int ifindex, struct prefix *prefix, if (! IN6_IS_ADDR_UNSPECIFIED (&tinfo.forwarding)) memcpy (&info->forwarding, &tinfo.forwarding, sizeof (struct in6_addr)); + info->tag = tinfo.tag; + } + else + { + /* If there is no route-map, simply update the tag */ + info->tag = tag; } info->type = type; @@ -519,6 +564,12 @@ ospf6_asbr_redistribute_add (int type, int ifindex, struct prefix *prefix, if (! IN6_IS_ADDR_UNSPECIFIED (&tinfo.forwarding)) memcpy (&info->forwarding, &tinfo.forwarding, sizeof (struct in6_addr)); + info->tag = tinfo.tag; + } + else + { + /* If there is no route-map, simply set the tag */ + info->tag = tag; } info->type = type; @@ -551,7 +602,8 @@ ospf6_asbr_redistribute_add (int type, int ifindex, struct prefix *prefix, } void -ospf6_asbr_redistribute_remove (int type, int ifindex, struct prefix *prefix) +ospf6_asbr_redistribute_remove (int type, ifindex_t ifindex, + struct prefix *prefix) { struct ospf6_route *match; struct ospf6_external_info *info = NULL; @@ -629,7 +681,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,11 +721,19 @@ DEFUN (no_ospf6_redistribute, return CMD_WARNING; ospf6_asbr_redistribute_unset (type); - ospf6_asbr_routemap_unset (type); return CMD_SUCCESS; } +ALIAS (no_ospf6_redistribute, + no_ospf6_redistribute_route_map_cmd, + "no redistribute " QUAGGA_REDIST_STR_OSPF6D " route-map WORD", + NO_STR + "Redistribute\n" + QUAGGA_REDIST_HELP_STR_OSPF6D + "Route map reference\n" + "Route map name\n") + int ospf6_redistribute_config_write (struct vty *vty) { @@ -739,7 +798,7 @@ ospf6_redistribute_show_config (struct vty *vty) } - + /* Routemap Functions */ static route_map_result_t ospf6_routemap_rule_match_address_prefixlist (void *rule, @@ -829,6 +888,30 @@ ospf6_routemap_rule_match_interface_cmd = ospf6_routemap_rule_match_interface_free }; +/* Match function for matching route tags */ +static route_map_result_t +ospf6_routemap_rule_match_tag (void *rule, struct prefix *prefix, + route_map_object_t type, void *object) +{ + route_tag_t *tag = rule; + struct ospf6_route *route = object; + struct ospf6_external_info *info = route->route_option; + + if (type == RMAP_OSPF6 && info->tag == *tag) + return RMAP_MATCH; + + return RMAP_NOMATCH; +} + +static struct route_map_rule_cmd +ospf6_routemap_rule_match_tag_cmd = +{ + "tag", + ospf6_routemap_rule_match_tag, + route_map_rule_tag_compile, + route_map_rule_tag_free, +}; + static route_map_result_t ospf6_routemap_rule_set_metric_type (void *rule, struct prefix *prefix, route_map_object_t type, void *object) @@ -890,7 +973,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); } @@ -954,6 +1037,30 @@ ospf6_routemap_rule_set_forwarding_cmd = ospf6_routemap_rule_set_forwarding_free, }; +static route_map_result_t +ospf6_routemap_rule_set_tag (void *rule, struct prefix *prefix, + route_map_object_t type, void *object) +{ + route_tag_t *tag = rule; + struct ospf6_route *route = object; + struct ospf6_external_info *info = route->route_option; + + if (type != RMAP_OSPF6) + return RMAP_OKAY; + + info->tag = *tag; + return RMAP_OKAY; +} + +static struct route_map_rule_cmd +ospf6_routemap_rule_set_tag_cmd = +{ + "tag", + ospf6_routemap_rule_set_tag, + route_map_rule_tag_compile, + route_map_rule_tag_free, +}; + static int route_map_command_status (struct vty *vty, int ret) { @@ -963,13 +1070,13 @@ route_map_command_status (struct vty *vty, int ret) switch (ret) { case RMAP_RULE_MISSING: - vty_out (vty, "Can't find rule.%s", VNL); + vty_out (vty, "OSPF6 Can't find rule.%s", VNL); break; case RMAP_COMPILE_ERROR: - vty_out (vty, "Argument is malformed.%s", VNL); + vty_out (vty, "OSPF6 Argument is malformed.%s", VNL); break; default: - vty_out (vty, "route-map add set failed.%s", VNL); + vty_out (vty, "OSPF6 route-map add set failed.%s", VNL); break; } return CMD_WARNING; @@ -1022,8 +1129,8 @@ DEFUN (ospf6_routemap_match_interface, DEFUN (ospf6_routemap_no_match_interface, ospf6_routemap_no_match_interface_cmd, "no match interface", - MATCH_STR NO_STR + MATCH_STR "Match first hop interface of route\n") { int ret = route_map_delete_match ((struct route_map_index *) vty->index, @@ -1034,11 +1141,45 @@ DEFUN (ospf6_routemap_no_match_interface, ALIAS (ospf6_routemap_no_match_interface, ospf6_routemap_no_match_interface_val_cmd, "no match interface WORD", - MATCH_STR NO_STR + MATCH_STR "Match first hop interface of route\n" "Interface name\n") +/* add "match tag" */ +DEFUN (ospf6_routemap_match_tag, + ospf6_routemap_match_tag_cmd, + "match tag <1-4294967295>", + MATCH_STR + "Tag value for routing protocol\n" + "Tag value\n") +{ + int ret = route_map_add_match ((struct route_map_index *) vty->index, + "tag", argv[0]); + return route_map_command_status (vty, ret); +} + +/* delete "match tag" */ +DEFUN (ospf6_routemap_no_match_tag, + ospf6_routemap_no_match_tag_cmd, + "no match tag", + NO_STR + MATCH_STR + "Tag value for routing protocol\n") +{ + int ret = route_map_delete_match ((struct route_map_index *) vty->index, + "tag", argc ? argv[0] : NULL); + return route_map_command_status (vty, ret); +} + +ALIAS (ospf6_routemap_no_match_tag, + ospf6_routemap_no_match_tag_val_cmd, + "no match tag <1-4294967295>", + NO_STR + MATCH_STR + "Tag value for routing protocol\n" + "Tag value\n") + /* add "set metric-type" */ DEFUN (ospf6_routemap_set_metric_type, ospf6_routemap_set_metric_type_cmd, @@ -1084,17 +1225,30 @@ DEFUN (set_metric, /* delete "set metric" */ DEFUN (no_set_metric, no_set_metric_cmd, - "no set metric <0-4294967295>", + "no set metric", NO_STR - "Set value\n" - "Metric\n" - "METRIC value\n") + SET_STR + "Metric value for destination routing protocol\n") { - int ret = route_map_delete_set ((struct route_map_index *) vty->index, - "metric", argv[0]); + int ret = 0; + + if (argc == 0) + ret = route_map_delete_set ((struct route_map_index *) vty->index, + "metric", NULL); + else + ret = route_map_delete_set ((struct route_map_index *) vty->index, + "metric", argv[0]); return route_map_command_status (vty, ret); } +ALIAS (no_set_metric, + no_set_metric_val_cmd, + "no set metric <0-4294967295>", + NO_STR + SET_STR + "Metric value for destination routing protocol\n" + "Metric value\n") + /* add "set forwarding-address" */ DEFUN (ospf6_routemap_set_forwarding, ospf6_routemap_set_forwarding_cmd, @@ -1122,6 +1276,40 @@ DEFUN (ospf6_routemap_no_set_forwarding, return route_map_command_status (vty, ret); } +/* add "set tag" */ +DEFUN (ospf6_routemap_set_tag, + ospf6_routemap_set_tag_cmd, + "set tag <1-4294967295>", + "Set value\n" + "Tag value for routing protocol\n" + "Tag value\n") +{ + int ret = route_map_add_set ((struct route_map_index *) vty->index, + "tag", argv[0]); + return route_map_command_status (vty, ret); +} + +/* delete "set tag" */ +DEFUN (ospf6_routemap_no_set_tag, + ospf6_routemap_no_set_tag_cmd, + "no set tag", + NO_STR + "Set value\n" + "Tag value for routing protocol\n") +{ + int ret = route_map_delete_set ((struct route_map_index *) vty->index, + "tag", argc ? argv[0] : NULL); + return route_map_command_status (vty, ret); +} + +ALIAS (ospf6_routemap_no_set_tag, + ospf6_routemap_no_set_tag_val_cmd, + "no set tag <1-4294967295>", + NO_STR + "Set value\n" + "Tag value for routing protocol\n" + "Tag value\n") + static void ospf6_routemap_init (void) { @@ -1132,10 +1320,12 @@ ospf6_routemap_init (void) route_map_install_match (&ospf6_routemap_rule_match_address_prefixlist_cmd); route_map_install_match (&ospf6_routemap_rule_match_interface_cmd); + route_map_install_match (&ospf6_routemap_rule_match_tag_cmd); route_map_install_set (&ospf6_routemap_rule_set_metric_type_cmd); route_map_install_set (&ospf6_routemap_rule_set_metric_cmd); route_map_install_set (&ospf6_routemap_rule_set_forwarding_cmd); + route_map_install_set (&ospf6_routemap_rule_set_tag_cmd); /* Match address prefix-list */ install_element (RMAP_NODE, &ospf6_routemap_match_address_prefixlist_cmd); @@ -1146,6 +1336,11 @@ ospf6_routemap_init (void) install_element (RMAP_NODE, &ospf6_routemap_no_match_interface_cmd); install_element (RMAP_NODE, &ospf6_routemap_no_match_interface_val_cmd); + /* Match tag */ + install_element (RMAP_NODE, &ospf6_routemap_match_tag_cmd); + install_element (RMAP_NODE, &ospf6_routemap_no_match_tag_cmd); + install_element (RMAP_NODE, &ospf6_routemap_no_match_tag_val_cmd); + /* ASE Metric Type (e.g. Type-1/Type-2) */ install_element (RMAP_NODE, &ospf6_routemap_set_metric_type_cmd); install_element (RMAP_NODE, &ospf6_routemap_no_set_metric_type_cmd); @@ -1153,20 +1348,58 @@ ospf6_routemap_init (void) /* ASE Metric */ install_element (RMAP_NODE, &set_metric_cmd); install_element (RMAP_NODE, &no_set_metric_cmd); + install_element (RMAP_NODE, &no_set_metric_val_cmd); - /* ASE Metric */ + /* Forwarding address */ install_element (RMAP_NODE, &ospf6_routemap_set_forwarding_cmd); install_element (RMAP_NODE, &ospf6_routemap_no_set_forwarding_cmd); + + /* Tag */ + install_element (RMAP_NODE, &ospf6_routemap_set_tag_cmd); + install_element (RMAP_NODE, &ospf6_routemap_no_set_tag_cmd); + install_element (RMAP_NODE, &ospf6_routemap_no_set_tag_val_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 +1424,22 @@ 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); + } + + /* Tag */ + if (CHECK_FLAG (external->bits_metric, OSPF6_ASBR_BIT_T)) + { + vty_out (vty, " Tag: %u%s", + ospf6_as_external_lsa_get_tag (lsa), VNL); } return 0; @@ -1244,6 +1480,8 @@ DEFUN (show_ipv6_ospf6_redistribute, { struct ospf6_route *route; + OSPF6_CMD_CHECK_RUNNING (); + ospf6_redistribute_show_config (vty); for (route = ospf6_route_head (ospf6->external_table); route; @@ -1257,7 +1495,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 @@ -1268,11 +1508,25 @@ ospf6_asbr_init (void) ospf6_install_lsa_handler (&as_external_handler); install_element (VIEW_NODE, &show_ipv6_ospf6_redistribute_cmd); - install_element (ENABLE_NODE, &show_ipv6_ospf6_redistribute_cmd); install_element (OSPF6_NODE, &ospf6_redistribute_cmd); install_element (OSPF6_NODE, &ospf6_redistribute_routemap_cmd); install_element (OSPF6_NODE, &no_ospf6_redistribute_cmd); + install_element (OSPF6_NODE, &no_ospf6_redistribute_route_map_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 diff --git a/ospf6d/ospf6_asbr.h b/ospf6d/ospf6_asbr.h index 72e491432..14113b06b 100644 --- a/ospf6d/ospf6_asbr.h +++ b/ospf6d/ospf6_asbr.h @@ -47,9 +47,10 @@ struct ospf6_external_info u_int32_t id; struct in6_addr forwarding; - /* u_int32_t tag; */ - unsigned int ifindex; + route_tag_t tag; + + ifindex_t ifindex; }; /* AS-External-LSA */ @@ -79,16 +80,18 @@ extern void ospf6_asbr_lsentry_add (struct ospf6_route *asbr_entry); extern void ospf6_asbr_lsentry_remove (struct ospf6_route *asbr_entry); extern int ospf6_asbr_is_asbr (struct ospf6 *o); -extern void ospf6_asbr_redistribute_add (int type, int ifindex, +extern void ospf6_asbr_redistribute_add (int type, ifindex_t ifindex, struct prefix *prefix, u_int nexthop_num, - struct in6_addr *nexthop); -extern void ospf6_asbr_redistribute_remove (int type, int ifindex, + struct in6_addr *nexthop, + route_tag_t tag); +extern void ospf6_asbr_redistribute_remove (int type, ifindex_t ifindex, struct prefix *prefix); 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 670c5d1d1..815db7b75 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, @@ -733,7 +759,6 @@ ospf6_receive_lsa (struct ospf6_neighbor *from, { struct ospf6_lsa *new = NULL, *old = NULL, *rem = NULL; int ismore_recent; - unsigned short cksum; int is_debug = 0; ismore_recent = 1; @@ -751,8 +776,7 @@ ospf6_receive_lsa (struct ospf6_neighbor *from, } /* (1) LSA Checksum */ - cksum = ntohs (new->header->checksum); - if (ntohs (ospf6_lsa_checksum (new->header)) != cksum) + if (! ospf6_lsa_checksum_valid (new->header)) { if (is_debug) zlog_debug ("Wrong LSA Checksum, discard"); @@ -799,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); @@ -839,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 / 1000)) { if (is_debug) zlog_debug ("LSA can't be updated within MinLSArrival, discard"); @@ -851,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 @@ -860,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); @@ -946,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 */ @@ -971,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 71aa6859c..fa6509f40 100644 --- a/ospf6d/ospf6_interface.c +++ b/ospf6d/ospf6_interface.c @@ -40,6 +40,7 @@ #include "ospf6_neighbor.h" #include "ospf6_intra.h" #include "ospf6_spf.h" +#include "ospf6_snmp.h" #include "ospf6d.h" unsigned char conf_debug_ospf6_interface = 0; @@ -58,7 +59,7 @@ const char *ospf6_interface_state_str[] = }; struct ospf6_interface * -ospf6_interface_lookup_by_ifindex (int ifindex) +ospf6_interface_lookup_by_ifindex (ifindex_t ifindex) { struct ospf6_interface *oi; struct interface *ifp; @@ -73,14 +74,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 +95,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 +188,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 +210,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 +221,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 +268,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,31 +336,7 @@ ospf6_interface_if_add (struct interface *ifp) } /* interface start */ - if (oi->area) - thread_add_event (master, interface_up, oi, 0); -} - -void -ospf6_interface_if_del (struct interface *ifp) -{ - struct ospf6_interface *oi; - - oi = (struct ospf6_interface *) ifp->info; - if (oi == NULL) - return; - - /* interface stop */ - if (oi->area) - thread_execute (master, interface_down, oi, 0); - - listnode_delete (oi->area->if_list, oi); - oi->area = (struct ospf6_area *) NULL; - - /* cut link */ - oi->interface = NULL; - ifp->info = NULL; - - ospf6_interface_delete (oi); + ospf6_interface_state_update(oi->interface); } void @@ -297,8 +349,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 +381,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); @@ -394,12 +453,14 @@ ospf6_interface_state_change (u_char next_state, struct ospf6_interface *oi) ospf6_interface_state_str[prev_state], ospf6_interface_state_str[next_state]); } + oi->state_change++; if ((prev_state == OSPF6_INTERFACE_DR || prev_state == OSPF6_INTERFACE_BDR) && (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 || @@ -420,9 +481,20 @@ ospf6_interface_state_change (u_char next_state, struct ospf6_interface *oi) OSPF6_INTRA_PREFIX_LSA_SCHEDULE_TRANSIT (oi); OSPF6_INTRA_PREFIX_LSA_SCHEDULE_STUB (oi->area); } + +#ifdef HAVE_SNMP + /* Terminal state or regression */ + if ((next_state == OSPF6_INTERFACE_POINTTOPOINT) || + (next_state == OSPF6_INTERFACE_DROTHER) || + (next_state == OSPF6_INTERFACE_BDR) || + (next_state == OSPF6_INTERFACE_DR) || + (next_state < prev_state)) + ospf6TrapIfStateChange (oi); +#endif + } - + /* DR Election, RFC2328 section 9.4 */ #define IS_ELIGIBLE(n) \ @@ -579,7 +651,7 @@ dr_election (struct ospf6_interface *oi) return next_state; } - + /* Interface State Machine */ int interface_up (struct thread *thread) @@ -594,7 +666,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]", @@ -602,6 +674,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) { @@ -611,19 +696,40 @@ 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); + if (ospf6_sso (oi->interface->ifindex, &allspfrouters6, IPV6_JOIN_GROUP) < 0) + { + if (oi->sso_try_cnt++ < OSPF6_INTERFACE_SSO_RETRY_MAX) + { + zlog_info("Scheduling %s for sso retry, trial count: %d", + oi->interface->name, oi->sso_try_cnt); + thread_add_timer (master, interface_up, oi, + OSPF6_INTERFACE_SSO_RETRY_INT); + } + return 0; + } + oi->sso_try_cnt = 0; /* Reset on success */ /* Update interface route */ ospf6_interface_connected_route_update (oi->interface); /* 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 @@ -705,6 +811,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); @@ -716,10 +825,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) @@ -746,7 +859,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); @@ -789,7 +902,7 @@ ospf6_interface_show (struct vty *vty, struct interface *ifp) "disabled" : "enabled", VNL); inet_ntop (AF_INET, &oi->area->area_id, strbuf, sizeof (strbuf)); - vty_out (vty, " Area ID %s, Cost %hu%s", strbuf, oi->cost, + vty_out (vty, " Area ID %s, Cost %u%s", strbuf, oi->cost, VNL); } else @@ -1141,20 +1254,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; } @@ -1280,7 +1465,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; @@ -1504,6 +1692,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) { @@ -1525,7 +1793,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); @@ -1563,6 +1831,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; @@ -1589,20 +1862,13 @@ ospf6_interface_init (void) install_element (VIEW_NODE, &show_ipv6_ospf6_interface_ifname_prefix_cmd); install_element (VIEW_NODE, &show_ipv6_ospf6_interface_ifname_prefix_detail_cmd); install_element (VIEW_NODE, &show_ipv6_ospf6_interface_ifname_prefix_match_cmd); - install_element (ENABLE_NODE, &show_ipv6_ospf6_interface_cmd); - install_element (ENABLE_NODE, &show_ipv6_ospf6_interface_prefix_cmd); - install_element (ENABLE_NODE, &show_ipv6_ospf6_interface_prefix_detail_cmd); - install_element (ENABLE_NODE, &show_ipv6_ospf6_interface_prefix_match_cmd); - install_element (ENABLE_NODE, &show_ipv6_ospf6_interface_ifname_cmd); - install_element (ENABLE_NODE, &show_ipv6_ospf6_interface_ifname_prefix_cmd); - install_element (ENABLE_NODE, &show_ipv6_ospf6_interface_ifname_prefix_detail_cmd); - install_element (ENABLE_NODE, &show_ipv6_ospf6_interface_ifname_prefix_match_cmd); install_element (CONFIG_NODE, &interface_cmd); install_default (INTERFACE_NODE); 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); @@ -1620,6 +1886,73 @@ 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); +} + +/* Clear the specified interface structure */ +static void +ospf6_interface_clear (struct vty *vty, struct interface *ifp) +{ + struct ospf6_interface *oi; + + if (!if_is_operative (ifp)) + return; + + if (ifp->info == NULL) + return; + + oi = (struct ospf6_interface *) ifp->info; + + if (IS_OSPF6_DEBUG_INTERFACE) + zlog_debug ("Interface %s: clear by reset", ifp->name); + + /* Reset the interface */ + thread_add_event (master, interface_down, oi, 0); + thread_add_event (master, interface_up, oi, 0); +} + +/* Clear interface */ +DEFUN (clear_ipv6_ospf6_interface, + clear_ipv6_ospf6_interface_cmd, + "clear ipv6 ospf6 interface [IFNAME]", + CLEAR_STR + IP6_STR + OSPF6_STR + INTERFACE_STR + IFNAME_STR + ) +{ + struct interface *ifp; + struct listnode *node; + + if (argc == 0) /* Clear all the ospfv3 interfaces. */ + { + for (ALL_LIST_ELEMENTS_RO (iflist, node, ifp)) + ospf6_interface_clear (vty, ifp); + } + else /* Interface name is specified. */ + { + if ((ifp = if_lookup_by_name (argv[0])) == NULL) + { + vty_out (vty, "No such Interface: %s%s", argv[0], VNL); + return CMD_WARNING; + } + ospf6_interface_clear (vty, ifp); + } + + return CMD_SUCCESS; +} + +void +install_element_ospf6_clear_interface (void) +{ + install_element (ENABLE_NODE, &clear_ipv6_ospf6_interface_cmd); } DEFUN (debug_ospf6_interface, diff --git a/ospf6d/ospf6_interface.h b/ospf6d/ospf6_interface.h index 2d1ff34db..2fbb83c4b 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; @@ -64,6 +67,8 @@ struct ospf6_interface u_int16_t dead_interval; u_int32_t rxmt_interval; + u_int32_t state_change; + /* Cost */ u_int32_t cost; @@ -73,6 +78,9 @@ struct ospf6_interface /* Interface State */ u_char state; + /* Interface socket setting trial counter, resets on success */ + u_char sso_try_cnt; + /* OSPF6 Interface flag */ char flag; @@ -123,6 +131,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 @@ -132,8 +141,12 @@ 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 */ +#define OSPF6_INTERFACE_SSO_RETRY_INT 1 +#define OSPF6_INTERFACE_SSO_RETRY_MAX 5 + - /* Function Prototypes */ extern struct ospf6_interface *ospf6_interface_lookup_by_ifindex (int); @@ -144,7 +157,6 @@ extern void ospf6_interface_enable (struct ospf6_interface *); extern void ospf6_interface_disable (struct ospf6_interface *); extern void ospf6_interface_if_add (struct interface *); -extern void ospf6_interface_if_del (struct interface *); extern void ospf6_interface_state_update (struct interface *); extern void ospf6_interface_connected_route_update (struct interface *); @@ -157,6 +169,8 @@ extern int neighbor_change (struct thread *); extern void ospf6_interface_init (void); +extern void install_element_ospf6_clear_interface (void); + extern int config_write_ospf6_debug_interface (struct vty *vty); extern void install_element_ospf6_debug_interface (void); diff --git a/ospf6d/ospf6_intra.c b/ospf6d/ospf6_intra.c index 9bc603b30..591dab326 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) { @@ -175,9 +234,9 @@ ospf6_router_lsa_originate (struct thread *thread) /* Multiple Router-LSA instance according to size limit setting */ if ( (oa->router_lsa_size_limit != 0) - && ((caddr_t) lsdesc + sizeof (struct ospf6_router_lsdesc) - - /* XXX warning: comparison between signed and unsigned */ - (caddr_t) buffer > oa->router_lsa_size_limit)) + && ((size_t)((char *)lsdesc - buffer) + + sizeof (struct ospf6_router_lsdesc) + > oa->router_lsa_size_limit)) { if ((caddr_t) lsdesc == (caddr_t) router_lsa + sizeof (struct ospf6_router_lsa)) @@ -187,35 +246,11 @@ ospf6_router_lsa_originate (struct thread *thread) return 0; } - /* 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); - - /* Reset setting for consecutive origination */ - memset ((caddr_t) router_lsa + sizeof (struct ospf6_router_lsa), - 0, (caddr_t) lsdesc - (caddr_t) router_lsa); - lsdesc = (struct ospf6_router_lsdesc *) - ((caddr_t) router_lsa + sizeof (struct ospf6_router_lsa)); link_state_id ++; } /* 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 +268,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 +296,10 @@ ospf6_router_lsa_originate (struct thread *thread) lsdesc++; } + else + { + assert (0); /* Unknown interface type */ + } /* Virtual links */ /* xxx */ @@ -268,35 +307,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 +346,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 +522,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 +729,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 +1194,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 +1232,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 +1250,19 @@ 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)) + { + 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 option set", + __func__, buf); + } + continue; + } + route = ospf6_route_create (); memset (&route->prefix, 0, sizeof (struct prefix)); @@ -1095,9 +1281,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; i < OSPF6_MULTI_PATH_LIMIT && + ospf6_nexthop_is_set (&ls_entry->nexthop[i]); i++) + ospf6_nexthop_copy (&route->nexthop[i], &ls_entry->nexthop[i]); + } if (IS_OSPF6_DEBUG_EXAMIN (INTRA_PREFIX)) { @@ -1119,7 +1314,7 @@ ospf6_intra_prefix_lsa_remove (struct ospf6_lsa *lsa) struct ospf6_area *oa; struct ospf6_intra_prefix_lsa *intra_prefix_lsa; struct prefix prefix; - struct ospf6_route *route; + struct ospf6_route *route, *nroute; int prefix_num; struct ospf6_prefix *op; char *start, *current, *end; @@ -1157,8 +1352,9 @@ ospf6_intra_prefix_lsa_remove (struct ospf6_lsa *lsa) for (ospf6_route_lock (route); route && ospf6_route_is_prefix (&prefix, route); - route = ospf6_route_next (route)) + route = nroute) { + nroute = ospf6_route_next (route); if (route->type != OSPF6_DEST_TYPE_NETWORK) continue; if (route->path.area_id != oa->area_id) @@ -1188,7 +1384,7 @@ ospf6_intra_prefix_lsa_remove (struct ospf6_lsa *lsa) void ospf6_intra_route_calculation (struct ospf6_area *oa) { - struct ospf6_route *route; + struct ospf6_route *route, *nroute; u_int16_t type; struct ospf6_lsa *lsa; void (*hook_add) (struct ospf6_route *) = NULL; @@ -1215,8 +1411,9 @@ ospf6_intra_route_calculation (struct ospf6_area *oa) oa->route_table->hook_remove = hook_remove; for (route = ospf6_route_head (oa->route_table); route; - route = ospf6_route_next (route)) + route = nroute) { + nroute = ospf6_route_next (route); if (CHECK_FLAG (route->flag, OSPF6_ROUTE_REMOVE) && CHECK_FLAG (route->flag, OSPF6_ROUTE_ADD)) { @@ -1275,7 +1472,8 @@ ospf6_brouter_debug_print (struct ospf6_route *brouter) zlog_info ("Brouter: %s via area %s", brouter_name, area_name); zlog_info (" memory: prev: %p this: %p next: %p parent rnode: %p", - brouter->prev, brouter, brouter->next, brouter->rnode); + (void *)brouter->prev, (void *)brouter, (void *)brouter->next, + (void *)brouter->rnode); zlog_info (" type: %d prefix: %s installed: %s changed: %s", brouter->type, destination, installed, changed); zlog_info (" lock: %d flags: %s%s%s%s", brouter->lock, @@ -1295,7 +1493,7 @@ ospf6_brouter_debug_print (struct ospf6_route *brouter) void ospf6_intra_brouter_calculation (struct ospf6_area *oa) { - struct ospf6_route *brouter, *copy; + struct ospf6_route *brouter, *nbrouter, *copy; void (*hook_add) (struct ospf6_route *) = NULL; void (*hook_remove) (struct ospf6_route *) = NULL; u_int32_t brouter_id; @@ -1323,7 +1521,7 @@ ospf6_intra_brouter_calculation (struct ospf6_area *oa) IS_OSPF6_DEBUG_ROUTE (MEMORY)) { zlog_info ("%p: mark as removing: area %s brouter %s", - brouter, oa->name, brouter_name); + (void *)brouter, oa->name, brouter_name); ospf6_brouter_debug_print (brouter); } } @@ -1342,6 +1540,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; @@ -1351,7 +1553,7 @@ ospf6_intra_brouter_calculation (struct ospf6_area *oa) IS_OSPF6_DEBUG_ROUTE (MEMORY)) { zlog_info ("%p: transfer: area %s brouter %s", - brouter, oa->name, brouter_name); + (void *)brouter, oa->name, brouter_name); ospf6_brouter_debug_print (brouter); } } @@ -1360,8 +1562,9 @@ ospf6_intra_brouter_calculation (struct ospf6_area *oa) oa->ospf6->brouter_table->hook_remove = hook_remove; for (brouter = ospf6_route_head (oa->ospf6->brouter_table); brouter; - brouter = ospf6_route_next (brouter)) + brouter = nbrouter) { + nbrouter = ospf6_route_next (brouter); brouter_id = ADV_ROUTER_IN_PREFIX (&brouter->prefix); inet_ntop (AF_INET, &brouter_id, brouter_name, sizeof (brouter_name)); @@ -1419,28 +1622,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 3810174ea..c9660b6a5 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 e65752d8c..3f008d3d9 100644 --- a/ospf6d/ospf6_lsa.c +++ b/ospf6d/ospf6_lsa.c @@ -29,6 +29,7 @@ #include "command.h" #include "memory.h" #include "thread.h" +#include "checksum.h" #include "ospf6_proto.h" #include "ospf6_lsa.h" @@ -74,7 +75,9 @@ struct ospf6_lsa_handler unknown_handler = { OSPF6_LSTYPE_UNKNOWN, "Unknown", + "Unk", ospf6_unknown_lsa_show, + NULL, OSPF6_LSA_DEBUG, }; @@ -117,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) { @@ -138,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 */ @@ -217,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; @@ -242,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); } @@ -257,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); } @@ -266,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; @@ -274,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) @@ -296,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 */ @@ -347,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); @@ -366,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 @@ -426,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", + (void *)lsa->expire, (void *)lsa->refresh, VNL); vty_out (vty, "%s", VNL); return; } @@ -437,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); @@ -444,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); @@ -453,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) @@ -557,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; } @@ -583,7 +658,7 @@ ospf6_lsa_unlock (struct ospf6_lsa *lsa) ospf6_lsa_delete (lsa); } - + /* ospf6 lsa expiry */ int ospf6_lsa_expire (struct thread *thread) @@ -607,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); @@ -652,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); @@ -663,56 +738,44 @@ 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; } - -/* enhanced Fletcher checksum algorithm, RFC1008 7.2 */ -#define MODX 4102 -#define LSA_CHECKSUM_OFFSET 15 +/* Fletcher Checksum -- Refer to RFC1008. */ + +/* All the offsets are zero-based. The offsets in the RFC1008 are + one-based. */ unsigned short ospf6_lsa_checksum (struct ospf6_lsa_header *lsa_header) { - u_char *sp, *ep, *p, *q; - int c0 = 0, c1 = 0; - int x, y; - u_int16_t length; + u_char *buffer = (u_char *) &lsa_header->type; + int type_offset = buffer - (u_char *) &lsa_header->age; /* should be 2 */ - lsa_header->checksum = 0; - length = ntohs (lsa_header->length) - 2; - sp = (u_char *) &lsa_header->type; + /* Skip the AGE field */ + u_int16_t len = ntohs(lsa_header->length) - type_offset; - for (ep = sp + length; sp < ep; sp = q) - { - q = sp + MODX; - if (q > ep) - q = ep; - for (p = sp; p < q; p++) - { - c0 += *p; - c1 += c0; - } - c0 %= 255; - c1 %= 255; - } + /* Checksum offset starts from "type" field, not the beginning of the + lsa_header struct. The offset is 14, rather than 16. */ + int checksum_offset = (u_char *) &lsa_header->checksum - buffer; + + return (unsigned short)fletcher_checksum(buffer, len, checksum_offset); +} - /* r = (c1 << 8) + c0; */ - x = ((length - LSA_CHECKSUM_OFFSET) * c0 - c1) % 255; - if (x <= 0) - x += 255; - y = 510 - c0 - x; - if (y > 255) - y -= 255; +int +ospf6_lsa_checksum_valid (struct ospf6_lsa_header *lsa_header) +{ + u_char *buffer = (u_char *) &lsa_header->type; + int type_offset = buffer - (u_char *) &lsa_header->age; /* should be 2 */ - lsa_header->checksum = htons ((x << 8) + y); + /* Skip the AGE field */ + u_int16_t len = ntohs(lsa_header->length) - type_offset; - return (lsa_header->checksum); + return (fletcher_checksum(buffer, len, FLETCHER_CHECKSUM_VALIDATE) == 0); } void @@ -727,7 +790,7 @@ ospf6_lsa_terminate (void) { vector_free (ospf6_lsa_handler_vector); } - + static char * ospf6_lsa_handler_name (struct ospf6_lsa_handler *h) { @@ -735,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); @@ -744,8 +807,8 @@ ospf6_lsa_handler_name (struct ospf6_lsa_handler *h) for (i = 0; i < MIN (size, sizeof (buf)); i++) { - if (! islower (h->name[i])) - buf[i] = tolower (h->name[i]); + if (! islower ((unsigned char)h->name[i])) + buf[i] = tolower ((unsigned char)h->name[i]); else buf[i] = h->name[i]; } @@ -755,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" @@ -764,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; @@ -809,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); @@ -820,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 @@ -832,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; @@ -864,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); @@ -872,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 @@ -1006,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 7d93f5cba..a85ca66dc 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); @@ -237,6 +242,7 @@ extern int ospf6_lsa_expire (struct thread *); extern int ospf6_lsa_refresh (struct thread *); extern unsigned short ospf6_lsa_checksum (struct ospf6_lsa_header *); +extern int ospf6_lsa_checksum_valid (struct ospf6_lsa_header *); extern int ospf6_lsa_prohibited_duration (u_int16_t type, u_int32_t id, u_int32_t adv_router, void *scope); diff --git a/ospf6d/ospf6_lsdb.c b/ospf6d/ospf6_lsdb.c index 280bdf957..b5a4587c2 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 71297daec..425f61537 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 d3ef0a6a8..1afe84a73 100644 --- a/ospf6d/ospf6_main.c +++ b/ospf6d/ospf6_main.c @@ -35,12 +35,15 @@ #include "privs.h" #include "sigevent.h" #include "zclient.h" +#include "vrf.h" #include "ospf6d.h" #include "ospf6_top.h" #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 +128,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,17 +137,21 @@ 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 (); - if_terminate (); + vrf_terminate (); vty_terminate (); cmd_terminate (); @@ -308,20 +315,17 @@ main (int argc, char *argv[], char *envp[]) LOG_DAEMON); zprivs_init (&ospf6d_privs); /* initialize zebra libraries */ - signal_init (master, Q_SIGC(ospf6_signals), ospf6_signals); + signal_init (master, array_size(ospf6_signals), ospf6_signals); cmd_init (1); vty_init (master); memory_init (); - if_init (); + vrf_init (); access_list_init (); prefix_list_init (); /* 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 01d61263e..d382f038a 100644 --- a/ospf6d/ospf6_message.c +++ b/ospf6d/ospf6_message.c @@ -58,8 +58,7 @@ static const struct message ospf6_message_type_str [] = { OSPF6_MESSAGE_TYPE_LSUPDATE, "LSUpdate" }, { OSPF6_MESSAGE_TYPE_LSACK, "LSAck" }, }; -static const size_t ospf6_message_type_str_max = - sizeof (ospf6_message_type_str) / sizeof (ospf6_message_type_str[0]); +static const size_t ospf6_message_type_str_max = array_size(ospf6_message_type_str); /* Minimum (besides the standard OSPF packet header) lengths for OSPF packets of particular types, offset is the "type" field. */ @@ -518,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)); @@ -540,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); @@ -736,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)); @@ -748,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); @@ -1283,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 @@ -1352,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 @@ -1529,7 +1515,7 @@ ospf6_receive (struct thread *thread) unsigned int len; char srcname[64], dstname[64]; struct in6_addr src, dst; - unsigned int ifindex; + ifindex_t ifindex; struct iovec iovector[2]; struct ospf6_interface *oi; struct ospf6_header *oh; @@ -1557,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; @@ -1644,7 +1630,7 @@ static void ospf6_send (struct in6_addr *src, struct in6_addr *dst, struct ospf6_interface *oi, struct ospf6_header *oh) { - int len; + unsigned int len; char srcname[64], dstname[64]; struct iovec iovector[2]; @@ -1735,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); @@ -1786,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; @@ -1810,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) @@ -1838,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)); @@ -1849,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; } @@ -1872,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; } @@ -1901,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; @@ -1923,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)); @@ -1939,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; } @@ -1948,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; } @@ -1965,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); @@ -1982,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 */ @@ -2006,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; } @@ -2068,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); @@ -2089,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) { @@ -2141,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; @@ -2167,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)); @@ -2184,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; } @@ -2201,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; @@ -2227,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)); @@ -2244,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) { @@ -2264,7 +2326,7 @@ ospf6_lsack_send_interface (struct thread *thread) return 0; } - + /* Commands */ DEFUN (debug_ospf6_message, debug_ospf6_message_cmd, @@ -2338,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 f6c3aeac7..fbad62a6c 100644 --- a/ospf6d/ospf6_neighbor.c +++ b/ospf6d/ospf6_neighbor.c @@ -38,6 +38,7 @@ #include "ospf6_neighbor.h" #include "ospf6_intra.h" #include "ospf6_flood.h" +#include "ospf6_snmp.h" #include "ospf6d.h" unsigned char conf_debug_ospf6_neighbor = 0; @@ -46,6 +47,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) { @@ -89,6 +115,7 @@ ospf6_neighbor_create (u_int32_t router_id, struct ospf6_interface *oi) buf, oi->interface->name); on->ospf6_if = oi; on->state = OSPF6_NEIGHBOR_DOWN; + on->state_change = 0; quagga_gettime (QUAGGA_CLK_MONOTONIC, &on->last_changed); on->router_id = router_id; @@ -97,7 +124,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); @@ -120,7 +146,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); @@ -129,7 +154,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); @@ -144,7 +168,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; @@ -154,16 +178,29 @@ ospf6_neighbor_state_change (u_char next_state, struct ospf6_neighbor *on) if (prev_state == next_state) return; + on->state_change++; quagga_gettime (QUAGGA_CLK_MONOTONIC, &on->last_changed); /* 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); @@ -180,6 +217,15 @@ ospf6_neighbor_state_change (u_char next_state, struct ospf6_neighbor *on) (next_state != OSPF6_NEIGHBOR_EXCHANGE && next_state != OSPF6_NEIGHBOR_LOADING)) ospf6_maxage_remove (on->ospf6_if->area->ospf6); + +#ifdef HAVE_SNMP + /* Terminal state or regression */ + if ((next_state == OSPF6_NEIGHBOR_FULL) || + (next_state == OSPF6_NEIGHBOR_TWOWAY) || + (next_state < prev_state)) + ospf6TrapNbrStateChange (on); +#endif + } /* RFC2328 section 10.4 */ @@ -215,7 +261,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; } @@ -238,11 +285,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); @@ -319,7 +368,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; } @@ -347,13 +397,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) { @@ -370,7 +452,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; } @@ -389,7 +472,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); @@ -402,7 +486,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; @@ -431,7 +516,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); @@ -446,6 +532,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); @@ -467,7 +555,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); @@ -482,6 +571,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); @@ -503,7 +594,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); @@ -538,7 +630,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); @@ -548,7 +641,7 @@ inactivity_timer (struct thread *thread) } - + /* vty functions */ /* show neighbor structure */ static void @@ -716,10 +809,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); @@ -843,8 +936,6 @@ ospf6_neighbor_init (void) { install_element (VIEW_NODE, &show_ipv6_ospf6_neighbor_cmd); install_element (VIEW_NODE, &show_ipv6_ospf6_neighbor_detail_cmd); - install_element (ENABLE_NODE, &show_ipv6_ospf6_neighbor_cmd); - install_element (ENABLE_NODE, &show_ipv6_ospf6_neighbor_detail_cmd); } DEFUN (debug_ospf6_neighbor, diff --git a/ospf6d/ospf6_neighbor.h b/ospf6d/ospf6_neighbor.h index b3bd173fc..54b7ffe0d 100644 --- a/ospf6d/ospf6_neighbor.h +++ b/ospf6d/ospf6_neighbor.h @@ -46,13 +46,14 @@ struct ospf6_neighbor u_char state; /* timestamp of last changing state */ + u_int32_t state_change; struct timeval last_changed; /* Neighbor Router ID */ u_int32_t router_id; /* Neighbor Interface ID */ - u_int32_t ifindex; + ifindex_t ifindex; /* Router Priority of this neighbor */ u_char priority; @@ -85,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; @@ -105,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); @@ -129,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 e5a1436c6..53d6c3594 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; @@ -121,8 +112,8 @@ ospf6_serv_sock (void) } /* ospf6 set socket option */ -void -ospf6_sso (u_int ifindex, struct in6_addr *group, int option) +int +ospf6_sso (ifindex_t ifindex, struct in6_addr *group, int option) { struct ipv6_mreq mreq6; int ret; @@ -134,8 +125,12 @@ ospf6_sso (u_int ifindex, struct in6_addr *group, int option) ret = setsockopt (ospf6_sock, IPPROTO_IPV6, option, &mreq6, sizeof (mreq6)); if (ret < 0) - zlog_err ("Network: setsockopt (%d) on ifindex %d failed: %s", - option, ifindex, safe_strerror (errno)); + { + zlog_err ("Network: setsockopt (%d) on ifindex %d failed: %s", + option, ifindex, safe_strerror (errno)); + } + + return ret; } static int @@ -159,7 +154,7 @@ iov_totallen (struct iovec *iov) int ospf6_sendmsg (struct in6_addr *src, struct in6_addr *dst, - unsigned int *ifindex, struct iovec *message) + ifindex_t *ifindex, struct iovec *message) { int retval; struct msghdr smsghdr; @@ -205,7 +200,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)) @@ -217,7 +212,7 @@ ospf6_sendmsg (struct in6_addr *src, struct in6_addr *dst, int ospf6_recvmsg (struct in6_addr *src, struct in6_addr *dst, - unsigned int *ifindex, struct iovec *message) + ifindex_t *ifindex, struct iovec *message) { int retval; struct msghdr rmsghdr; diff --git a/ospf6d/ospf6_network.h b/ospf6d/ospf6_network.h index 0526b3e1c..4fa283951 100644 --- a/ospf6d/ospf6_network.h +++ b/ospf6d/ospf6_network.h @@ -22,25 +22,19 @@ #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); +extern int ospf6_sso (ifindex_t ifindex, struct in6_addr *group, int option); extern int ospf6_sendmsg (struct in6_addr *, struct in6_addr *, - unsigned int *, struct iovec *); + ifindex_t *, struct iovec *); extern int ospf6_recvmsg (struct in6_addr *, struct in6_addr *, - unsigned int *, struct iovec *); + ifindex_t *, struct iovec *); #endif /* OSPF6_NETWORK_H */ diff --git a/ospf6d/ospf6_proto.h b/ospf6d/ospf6_proto.h index 646250047..af60eb922 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 398acfa80..3cfbab535 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, @@ -374,7 +374,7 @@ ospf6_route_add (struct ospf6_route *route, if (IS_OSPF6_DEBUG_ROUTE (MEMORY)) zlog_debug ("%s %p: route add %p: %s", ospf6_route_table_name (table), - table, route, buf); + (void *)table, (void *)route, buf); else if (IS_OSPF6_DEBUG_ROUTE (TABLE)) zlog_debug ("%s: route add: %s", ospf6_route_table_name (table), buf); @@ -408,7 +408,8 @@ ospf6_route_add (struct ospf6_route *route, { if (IS_OSPF6_DEBUG_ROUTE (MEMORY)) zlog_debug ("%s %p: route add %p: needless update of %p", - ospf6_route_table_name (table), table, route, old); + ospf6_route_table_name (table), + (void *)table, (void *)route, (void *)old); else if (IS_OSPF6_DEBUG_ROUTE (TABLE)) zlog_debug ("%s: route add: needless update", ospf6_route_table_name (table)); @@ -422,7 +423,8 @@ ospf6_route_add (struct ospf6_route *route, if (IS_OSPF6_DEBUG_ROUTE (MEMORY)) zlog_debug ("%s %p: route add %p: update of %p", - ospf6_route_table_name (table), table, route, old); + ospf6_route_table_name (table), + (void *)table, (void *)route, (void *)old); else if (IS_OSPF6_DEBUG_ROUTE (TABLE)) zlog_debug ("%s: route add: update", ospf6_route_table_name (table)); @@ -463,7 +465,8 @@ ospf6_route_add (struct ospf6_route *route, { if (IS_OSPF6_DEBUG_ROUTE (MEMORY)) zlog_debug ("%s %p: route add %p: another path: prev %p, next %p", - ospf6_route_table_name (table), table, route, prev, next); + ospf6_route_table_name (table), + (void *)table, (void *)route, (void *)prev, (void *)next); else if (IS_OSPF6_DEBUG_ROUTE (TABLE)) zlog_debug ("%s: route add: another path found", ospf6_route_table_name (table)); @@ -488,7 +491,8 @@ ospf6_route_add (struct ospf6_route *route, SET_FLAG (route->flag, OSPF6_ROUTE_BEST); if (IS_OSPF6_DEBUG_ROUTE (MEMORY)) zlog_info ("%s %p: route add %p: replacing previous best: %p", - ospf6_route_table_name (table), table, route, next); + ospf6_route_table_name (table), + (void *)table, (void *)route, (void *)next); } route->installed = now; @@ -510,7 +514,7 @@ ospf6_route_add (struct ospf6_route *route, /* Else, this is the brand new route regarding to the prefix */ if (IS_OSPF6_DEBUG_ROUTE (MEMORY)) zlog_debug ("%s %p: route add %p: brand new route", - ospf6_route_table_name (table), table, route); + ospf6_route_table_name (table), (void *)table, (void *)route); else if (IS_OSPF6_DEBUG_ROUTE (TABLE)) zlog_debug ("%s: route add: brand new route", ospf6_route_table_name (table)); @@ -589,7 +593,8 @@ ospf6_route_remove (struct ospf6_route *route, if (IS_OSPF6_DEBUG_ROUTE (MEMORY)) zlog_debug ("%s %p: route remove %p: %s", - ospf6_route_table_name (table), table, route, buf); + ospf6_route_table_name (table), + (void *)table, (void *)route, buf); else if (IS_OSPF6_DEBUG_ROUTE (TABLE)) zlog_debug ("%s: route remove: %s", ospf6_route_table_name (table), buf); @@ -615,7 +620,7 @@ ospf6_route_remove (struct ospf6_route *route, if (node->info == route) { - if (route->next && ospf6_route_is_same (route->next, route)) + if (route->next && route->next->rnode == node) { node->info = route->next; SET_FLAG (route->next->flag, OSPF6_ROUTE_BEST); @@ -661,8 +666,8 @@ ospf6_route_head (struct ospf6_route_table *table) if (IS_OSPF6_DEBUG_ROUTE (MEMORY)) zlog_info ("%s %p: route head: %p<-[%p]->%p", - ospf6_route_table_name (table), table, - route->prev, route, route->next); + ospf6_route_table_name (table), (void *)table, + (void *)route->prev, (void *)route, (void *)route->next); return route; } @@ -674,8 +679,8 @@ ospf6_route_next (struct ospf6_route *route) if (IS_OSPF6_DEBUG_ROUTE (MEMORY)) zlog_info ("%s %p: route next: %p<-[%p]->%p", - ospf6_route_table_name (route->table), route->table, - route->prev, route, route->next); + ospf6_route_table_name (route->table), (void *)route->table, + (void *)route->prev, (void *)route, (void *)route->next); ospf6_route_unlock (route); if (next) @@ -780,14 +785,15 @@ 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) { int i; char destination[64], nexthop[64]; - char duration[16], ifname[IFNAMSIZ]; + char duration[16]; + const char *ifname; struct timeval now, res; quagga_gettime (QUAGGA_CLK_MONOTONIC, &now); @@ -807,8 +813,7 @@ ospf6_route_show (struct vty *vty, struct ospf6_route *route) /* nexthop */ inet_ntop (AF_INET6, &route->nexthop[0].address, nexthop, sizeof (nexthop)); - if (! if_indextoname (route->nexthop[0].ifindex, ifname)) - snprintf (ifname, sizeof (ifname), "%d", route->nexthop[0].ifindex); + ifname = ifindex2ifname (route->nexthop[0].ifindex); vty_out (vty, "%c%1s %2s %-30s %-25s %6.*s %s%s", (ospf6_route_is_best (route) ? '*' : ' '), @@ -816,14 +821,13 @@ ospf6_route_show (struct vty *vty, struct ospf6_route *route) OSPF6_PATH_TYPE_SUBSTR (route->path.type), destination, nexthop, IFNAMSIZ, ifname, duration, VNL); - for (i = 1; ospf6_nexthop_is_set (&route->nexthop[i]) && - i < OSPF6_MULTI_PATH_LIMIT; i++) + for (i = 1; i < OSPF6_MULTI_PATH_LIMIT && + ospf6_nexthop_is_set (&route->nexthop[i]); i++) { /* nexthop */ inet_ntop (AF_INET6, &route->nexthop[i].address, nexthop, sizeof (nexthop)); - if (! if_indextoname (route->nexthop[i].ifindex, ifname)) - snprintf (ifname, sizeof (ifname), "%d", route->nexthop[i].ifindex); + ifname = ifindex2ifname (route->nexthop[i].ifindex); vty_out (vty, "%c%1s %2s %-30s %-25s %6.*s %s%s", ' ', "", "", "", nexthop, IFNAMSIZ, ifname, "", VNL); @@ -833,7 +837,8 @@ ospf6_route_show (struct vty *vty, struct ospf6_route *route) void ospf6_route_show_detail (struct vty *vty, struct ospf6_route *route) { - char destination[64], nexthop[64], ifname[IFNAMSIZ]; + const char *ifname; + char destination[64], nexthop[64]; char area_id[16], id[16], adv_router[16], capa[16], options[16]; struct timeval now, res; char duration[16]; @@ -874,7 +879,7 @@ ospf6_route_show_detail (struct vty *vty, struct ospf6_route *route) (CHECK_FLAG (route->flag, OSPF6_ROUTE_CHANGE) ? "C" : "-"), VNL); vty_out (vty, "Memory: prev: %p this: %p next: %p%s", - route->prev, route, route->next, VNL); + (void *)route->prev, (void *)route, (void *)route->next, VNL); /* Path section */ @@ -913,14 +918,13 @@ ospf6_route_show_detail (struct vty *vty, struct ospf6_route *route) /* Nexthops */ vty_out (vty, "Nexthop:%s", VNL); - for (i = 0; ospf6_nexthop_is_set (&route->nexthop[i]) && - i < OSPF6_MULTI_PATH_LIMIT; i++) + for (i = 0; i < OSPF6_MULTI_PATH_LIMIT && + ospf6_nexthop_is_set (&route->nexthop[i]); i++) { /* nexthop */ inet_ntop (AF_INET6, &route->nexthop[i].address, nexthop, sizeof (nexthop)); - if (! if_indextoname (route->nexthop[i].ifindex, ifname)) - snprintf (ifname, sizeof (ifname), "%d", route->nexthop[i].ifindex); + ifname = ifindex2ifname (route->nexthop[i].ifindex); vty_out (vty, " %s %.*s%s", nexthop, IFNAMSIZ, ifname, VNL); } vty_out (vty, "%s", VNL); diff --git a/ospf6d/ospf6_route.h b/ospf6d/ospf6_route.h index b384824cb..027a648a3 100644 --- a/ospf6d/ospf6_route.h +++ b/ospf6d/ospf6_route.h @@ -41,7 +41,7 @@ extern unsigned char conf_debug_ospf6_route; struct ospf6_nexthop { /* Interface index */ - unsigned int ifindex; + ifindex_t ifindex; /* IP address, if any */ struct in6_addr address; @@ -97,6 +97,7 @@ struct ospf6_path u_int8_t metric_type; u_int32_t cost; u_int32_t cost_e2; + u_int32_t tag; }; #define OSPF6_PATH_TYPE_NONE 0 @@ -122,6 +123,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; @@ -236,14 +241,14 @@ extern const char *ospf6_path_type_substr[OSPF6_PATH_TYPE_MAX]; #define ospf6_route_is_best(r) (CHECK_FLAG ((r)->flag, OSPF6_ROUTE_BEST)) #define ospf6_linkstate_prefix_adv_router(x) \ - (*(u_int32_t *)(&(x)->u.prefix6.s6_addr[0])) + ((x)->u.lp.id.s_addr) #define ospf6_linkstate_prefix_id(x) \ - (*(u_int32_t *)(&(x)->u.prefix6.s6_addr[4])) + ((x)->u.lp.adv_router.s_addr) #define ADV_ROUTER_IN_PREFIX(x) \ - (*(u_int32_t *)(&(x)->u.prefix6.s6_addr[0])) + ((x)->u.lp.id.s_addr) #define ID_IN_PREFIX(x) \ - (*(u_int32_t *)(&(x)->u.prefix6.s6_addr[4])) + ((x)->u.lp.adv_router.s_addr) /* Function prototype */ extern void ospf6_linkstate_prefix (u_int32_t adv_router, u_int32_t id, diff --git a/ospf6d/ospf6_snmp.c b/ospf6d/ospf6_snmp.c index 5ac7846d2..266031e7f 100644 --- a/ospf6d/ospf6_snmp.c +++ b/ospf6d/ospf6_snmp.c @@ -23,14 +23,8 @@ #ifdef HAVE_SNMP -#ifdef HAVE_NETSNMP #include #include -#else -#include -#include -#include -#endif #include "log.h" #include "vty.h" @@ -46,11 +40,13 @@ #include "ospf6_interface.h" #include "ospf6_message.h" #include "ospf6_neighbor.h" +#include "ospf6_abr.h" +#include "ospf6_asbr.h" #include "ospf6d.h" #include "ospf6_snmp.h" /* OSPFv3-MIB */ -#define OSPFv3MIB 1,3,6,1,3,102 +#define OSPFv3MIB 1,3,6,1,2,1,191 /* OSPFv3 MIB General Group values. */ #define OSPFv3ROUTERID 1 @@ -64,19 +60,22 @@ #define OSPFv3RXNEWLSAS 9 #define OSPFv3EXTLSACOUNT 10 #define OSPFv3EXTAREALSDBLIMIT 11 -#define OSPFv3MULTICASTEXTENSIONS 12 -#define OSPFv3EXITOVERFLOWINTERVAL 13 -#define OSPFv3DEMANDEXTENSIONS 14 -#define OSPFv3TRAFFICENGINEERINGSUPPORT 15 -#define OSPFv3REFERENCEBANDWIDTH 16 -#define OSPFv3RESTARTSUPPORT 17 -#define OSPFv3RESTARTINTERVAL 18 -#define OSPFv3RESTARTSTATUS 19 -#define OSPFv3RESTARTAGE 20 -#define OSPFv3RESTARTEXITREASON 21 - -/* OSPFv3 MIB Area Table values. */ -#define OSPFv3AREAID 1 +#define OSPFv3EXITOVERFLOWINTERVAL 12 +#define OSPFv3DEMANDEXTENSIONS 13 +#define OSPFv3REFERENCEBANDWIDTH 14 +#define OSPFv3RESTARTSUPPORT 15 +#define OSPFv3RESTARTINTERVAL 16 +#define OSPFv3RESTARTSTRICTLSACHECKING 17 +#define OSPFv3RESTARTSTATUS 18 +#define OSPFv3RESTARTAGE 19 +#define OSPFv3RESTARTEXITREASON 20 +#define OSPFv3NOTIFICATIONENABLE 21 +#define OSPFv3STUBROUTERSUPPORT 22 +#define OSPFv3STUBROUTERADVERTISEMENT 23 +#define OSPFv3DISCONTINUITYTIME 24 +#define OSPFv3RESTARTTIME 25 + +/* OSPFv3 MIB Area Table values: ospfv3AreaTable */ #define OSPFv3IMPORTASEXTERN 2 #define OSPFv3AREASPFRUNS 3 #define OSPFv3AREABDRRTRCOUNT 4 @@ -84,24 +83,110 @@ #define OSPFv3AREASCOPELSACOUNT 6 #define OSPFv3AREASCOPELSACKSUMSUM 7 #define OSPFv3AREASUMMARY 8 -#define OSPFv3AREASTATUS 9 -#define OSPFv3STUBMETRIC 10 +#define OSPFv3AREAROWSTATUS 9 +#define OSPFv3AREASTUBMETRIC 10 #define OSPFv3AREANSSATRANSLATORROLE 11 #define OSPFv3AREANSSATRANSLATORSTATE 12 -#define OSPFv3AREANSSATRANSLATORSTABILITYINTERVAL 13 +#define OSPFv3AREANSSATRANSLATORSTABINTERVAL 13 #define OSPFv3AREANSSATRANSLATOREVENTS 14 #define OSPFv3AREASTUBMETRICTYPE 15 +#define OSPFv3AREATEENABLED 16 + +/* OSPFv3 MIB * Lsdb Table values: ospfv3*LsdbTable */ +#define OSPFv3WWLSDBSEQUENCE 1 +#define OSPFv3WWLSDBAGE 2 +#define OSPFv3WWLSDBCHECKSUM 3 +#define OSPFv3WWLSDBADVERTISEMENT 4 +#define OSPFv3WWLSDBTYPEKNOWN 5 + +/* Three first bits are to identify column */ +#define OSPFv3WWCOLUMN 0x7 +/* Then we use other bits to identify table */ +#define OSPFv3WWASTABLE (1 << 3) +#define OSPFv3WWAREATABLE (1 << 4) +#define OSPFv3WWLINKTABLE (1 << 5) +#define OSPFv3WWVIRTLINKTABLE (1 << 6) + +/* OSPFv3 MIB Host Table values: ospfv3HostTable */ +#define OSPFv3HOSTMETRIC 3 +#define OSPFv3HOSTROWSTATUS 4 +#define OSPFv3HOSTAREAID 5 + +/* OSPFv3 MIB Interface Table values: ospfv3IfTable */ +#define OSPFv3IFAREAID 3 +#define OSPFv3IFTYPE 4 +#define OSPFv3IFADMINSTATUS 5 +#define OSPFv3IFRTRPRIORITY 6 +#define OSPFv3IFTRANSITDELAY 7 +#define OSPFv3IFRETRANSINTERVAL 8 +#define OSPFv3IFHELLOINTERVAL 9 +#define OSPFv3IFRTRDEADINTERVAL 10 +#define OSPFv3IFPOLLINTERVAL 11 +#define OSPFv3IFSTATE 12 +#define OSPFv3IFDESIGNATEDROUTER 13 +#define OSPFv3IFBACKUPDESIGNATEDROUTER 14 +#define OSPFv3IFEVENTS 15 +#define OSPFv3IFROWSTATUS 16 +#define OSPFv3IFDEMAND 17 +#define OSPFv3IFMETRICVALUE 18 +#define OSPFv3IFLINKSCOPELSACOUNT 19 +#define OSPFv3IFLINKLSACKSUMSUM 20 +#define OSPFv3IFDEMANDNBRPROBE 21 +#define OSPFv3IFDEMANDNBRPROBERETRANSLIMIT 22 +#define OSPFv3IFDEMANDNBRPROBEINTERVAL 23 +#define OSPFv3IFTEDISABLED 24 +#define OSPFv3IFLINKLSASUPPRESSION 25 + +/* OSPFv3 MIB Virtual Interface Table values: ospfv3VirtIfTable */ +#define OSPFv3VIRTIFINDEX 3 +#define OSPFv3VIRTIFINSTID 4 +#define OSPFv3VIRTIFTRANSITDELAY 5 +#define OSPFv3VIRTIFRETRANSINTERVAL 6 +#define OSPFv3VIRTIFHELLOINTERVAL 7 +#define OSPFv3VIRTIFRTRDEADINTERVAL 8 +#define OSPFv3VIRTIFSTATE 9 +#define OSPFv3VIRTIFEVENTS 10 +#define OSPFv3VIRTIFROWSTATUS 11 +#define OSPFv3VIRTIFLINKSCOPELSACOUNT 12 +#define OSPFv3VIRTIFLINKLSACKSUMSUM 13 + +/* OSPFv3 MIB Neighbors Table values: ospfv3NbrTable */ +#define OSPFv3NBRADDRESSTYPE 4 +#define OSPFv3NBRADDRESS 5 +#define OSPFv3NBROPTIONS 6 +#define OSPFv3NBRPRIORITY 7 +#define OSPFv3NBRSTATE 8 +#define OSPFv3NBREVENTS 9 +#define OSPFv3NBRLSRETRANSQLEN 10 +#define OSPFv3NBRHELLOSUPPRESSED 11 +#define OSPFv3NBRIFID 12 +#define OSPFv3NBRRESTARTHELPERSTATUS 13 +#define OSPFv3NBRRESTARTHELPERAGE 14 +#define OSPFv3NBRRESTARTHELPEREXITREASON 15 + +/* OSPFv3 MIB Configured Neighbors Table values: ospfv3CfgNbrTable */ +#define OSPFv3CFGNBRPRIORITY 5 +#define OSPFv3CFGNBRROWSTATUS 6 -/* OSPFv3 MIB Area Lsdb Table values. */ -#define OSPFv3AREALSDBAREAID 1 -#define OSPFv3AREALSDBTYPE 2 -#define OSPFv3AREALSDBROUTERID 3 -#define OSPFv3AREALSDBLSID 4 -#define OSPFv3AREALSDBSEQUENCE 5 -#define OSPFv3AREALSDBAGE 6 -#define OSPFv3AREALSDBCHECKSUM 7 -#define OSPFv3AREALSDBADVERTISEMENT 8 -#define OSPFv3AREALSDBTYPEKNOWN 9 +/* OSPFv3 MIB Virtual Neighbors Table values: ospfv3VirtNbrTable */ +#define OSPFv3VIRTNBRIFINDEX 3 +#define OSPFv3VIRTNBRIFINSTID 4 +#define OSPFv3VIRTNBRADDRESSTYPE 5 +#define OSPFv3VIRTNBRADDRESS 6 +#define OSPFv3VIRTNBROPTIONS 7 +#define OSPFv3VIRTNBRSTATE 8 +#define OSPFv3VIRTNBREVENTS 9 +#define OSPFv3VIRTNBRLSRETRANSQLEN 10 +#define OSPFv3VIRTNBRHELLOSUPPRESSED 11 +#define OSPFv3VIRTNBRIFID 12 +#define OSPFv3VIRTNBRRESTARTHELPERSTATUS 13 +#define OSPFv3VIRTNBRRESTARTHELPERAGE 14 +#define OSPFv3VIRTNBRRESTARTHELPEREXITREASON 15 + +/* OSPFv3 MIB Area Aggregate Table values: ospfv3AreaAggregateTable */ +#define OSPFv3AREAAGGREGATEROWSTATUS 6 +#define OSPFv3AREAAGGREGATEEFFECT 7 +#define OSPFv3AREAAGGREGATEROUTETAG 8 /* SYNTAX Status from OSPF-MIB. */ #define OSPF_STATUS_ENABLED 1 @@ -111,6 +196,7 @@ #define COUNTER ASN_COUNTER #define INTEGER ASN_INTEGER #define GAUGE ASN_GAUGE +#define UNSIGNED ASN_UNSIGNED #define TIMETICKS ASN_TIMETICKS #define IPADDRESS ASN_IPADDRESS #define STRING ASN_OCTET_STR @@ -118,28 +204,26 @@ /* For return values e.g. SNMP_INTEGER macro */ SNMP_LOCAL_VARIABLES -static struct in_addr tmp; -#define INT32_INADDR(x) \ - (tmp.s_addr = (x), tmp) - /* OSPFv3-MIB instances. */ oid ospfv3_oid [] = { OSPFv3MIB }; - -/* empty ID 0.0.0.0 e.g. empty router-id */ -static struct in_addr ospf6_empty_id = {0}; +oid ospfv3_trap_oid [] = { OSPFv3MIB, 0 }; /* Hook functions. */ static u_char *ospfv3GeneralGroup (struct variable *, oid *, size_t *, int, size_t *, WriteMethod **); static u_char *ospfv3AreaEntry (struct variable *, oid *, size_t *, int, size_t *, WriteMethod **); -static u_char *ospfv3AreaLsdbEntry (struct variable *, oid *, size_t *, - int, size_t *, WriteMethod **); +static u_char *ospfv3WwLsdbEntry (struct variable *, oid *, size_t *, + int, size_t *, WriteMethod **); +static u_char *ospfv3NbrEntry (struct variable *, oid *, size_t *, + int, size_t *, WriteMethod **); +static u_char *ospfv3IfEntry (struct variable *, oid *, size_t *, + int, size_t *, WriteMethod **); struct variable ospfv3_variables[] = { /* OSPF general variables */ - {OSPFv3ROUTERID, IPADDRESS, RWRITE, ospfv3GeneralGroup, + {OSPFv3ROUTERID, UNSIGNED, RWRITE, ospfv3GeneralGroup, 3, {1, 1, 1}}, {OSPFv3ADMINSTAT, INTEGER, RWRITE, ospfv3GeneralGroup, 3, {1, 1, 2}}, @@ -151,7 +235,7 @@ struct variable ospfv3_variables[] = 3, {1, 1, 5}}, {OSPFv3ASSCOPELSACOUNT, GAUGE, RONLY, ospfv3GeneralGroup, 3, {1, 1, 6}}, - {OSPFv3ASSCOPELSACHECKSUMSUM, INTEGER, RONLY, ospfv3GeneralGroup, + {OSPFv3ASSCOPELSACHECKSUMSUM,UNSIGNED, RONLY, ospfv3GeneralGroup, 3, {1, 1, 7}}, {OSPFv3ORIGINATENEWLSAS, COUNTER, RONLY, ospfv3GeneralGroup, 3, {1, 1, 8}}, @@ -161,30 +245,36 @@ struct variable ospfv3_variables[] = 3, {1, 1, 10}}, {OSPFv3EXTAREALSDBLIMIT, INTEGER, RWRITE, ospfv3GeneralGroup, 3, {1, 1, 11}}, - {OSPFv3MULTICASTEXTENSIONS, INTEGER, RWRITE, ospfv3GeneralGroup, + {OSPFv3EXITOVERFLOWINTERVAL, UNSIGNED, RWRITE, ospfv3GeneralGroup, 3, {1, 1, 12}}, - {OSPFv3EXITOVERFLOWINTERVAL, INTEGER, RWRITE, ospfv3GeneralGroup, - 3, {1, 1, 13}}, {OSPFv3DEMANDEXTENSIONS, INTEGER, RWRITE, ospfv3GeneralGroup, + 3, {1, 1, 13}}, + {OSPFv3REFERENCEBANDWIDTH, UNSIGNED, RWRITE, ospfv3GeneralGroup, 3, {1, 1, 14}}, - {OSPFv3TRAFFICENGINEERINGSUPPORT, INTEGER, RWRITE, ospfv3GeneralGroup, + {OSPFv3RESTARTSUPPORT, INTEGER, RWRITE, ospfv3GeneralGroup, 3, {1, 1, 15}}, - {OSPFv3REFERENCEBANDWIDTH, INTEGER, RWRITE, ospfv3GeneralGroup, + {OSPFv3RESTARTINTERVAL, UNSIGNED, RWRITE, ospfv3GeneralGroup, 3, {1, 1, 16}}, - {OSPFv3RESTARTSUPPORT, INTEGER, RWRITE, ospfv3GeneralGroup, + {OSPFv3RESTARTSTRICTLSACHECKING, INTEGER, RWRITE, ospfv3GeneralGroup, 3, {1, 1, 17}}, - {OSPFv3RESTARTINTERVAL, INTEGER, RWRITE, ospfv3GeneralGroup, - 3, {1, 1, 18}}, {OSPFv3RESTARTSTATUS, INTEGER, RONLY, ospfv3GeneralGroup, + 3, {1, 1, 18}}, + {OSPFv3RESTARTAGE, UNSIGNED, RONLY, ospfv3GeneralGroup, 3, {1, 1, 19}}, - {OSPFv3RESTARTAGE, INTEGER, RONLY, ospfv3GeneralGroup, - 3, {1, 1, 20}}, {OSPFv3RESTARTEXITREASON, INTEGER, RONLY, ospfv3GeneralGroup, + 3, {1, 1, 20}}, + {OSPFv3NOTIFICATIONENABLE, INTEGER, RWRITE, ospfv3GeneralGroup, 3, {1, 1, 21}}, + {OSPFv3STUBROUTERSUPPORT, INTEGER, RONLY, ospfv3GeneralGroup, + 3, {1, 1, 22}}, + {OSPFv3STUBROUTERADVERTISEMENT, INTEGER, RWRITE, ospfv3GeneralGroup, + 3, {1, 1, 23}}, + {OSPFv3DISCONTINUITYTIME, TIMETICKS, RONLY, ospfv3GeneralGroup, + 3, {1, 1, 24}}, + {OSPFv3RESTARTTIME, TIMETICKS, RONLY, ospfv3GeneralGroup, + 3, {1, 1, 25}}, /* OSPFv3 Area Data Structure */ - {OSPFv3AREAID, IPADDRESS, RONLY, ospfv3AreaEntry, - 4, {1, 2, 1, 1}}, {OSPFv3IMPORTASEXTERN, INTEGER, RWRITE, ospfv3AreaEntry, 4, {1, 2, 1, 2}}, {OSPFv3AREASPFRUNS, COUNTER, RONLY, ospfv3AreaEntry, @@ -195,50 +285,146 @@ struct variable ospfv3_variables[] = 4, {1, 2, 1, 5}}, {OSPFv3AREASCOPELSACOUNT, GAUGE, RONLY, ospfv3AreaEntry, 4, {1, 2, 1, 6}}, - {OSPFv3AREASCOPELSACKSUMSUM, INTEGER, RONLY, ospfv3AreaEntry, + {OSPFv3AREASCOPELSACKSUMSUM, UNSIGNED, RONLY, ospfv3AreaEntry, 4, {1, 2, 1, 7}}, {OSPFv3AREASUMMARY, INTEGER, RWRITE, ospfv3AreaEntry, 4, {1, 2, 1, 8}}, - {OSPFv3AREASTATUS, INTEGER, RWRITE, ospfv3AreaEntry, + {OSPFv3AREAROWSTATUS, INTEGER, RWRITE, ospfv3AreaEntry, 4, {1, 2, 1, 9}}, - {OSPFv3STUBMETRIC, INTEGER, RWRITE, ospfv3AreaEntry, + {OSPFv3AREASTUBMETRIC, INTEGER, RWRITE, ospfv3AreaEntry, 4, {1, 2, 1, 10}}, {OSPFv3AREANSSATRANSLATORROLE, INTEGER, RWRITE, ospfv3AreaEntry, 4, {1, 2, 1, 11}}, {OSPFv3AREANSSATRANSLATORSTATE, INTEGER, RONLY, ospfv3AreaEntry, 4, {1, 2, 1, 12}}, - {OSPFv3AREANSSATRANSLATORSTABILITYINTERVAL, INTEGER, RWRITE, ospfv3AreaEntry, + {OSPFv3AREANSSATRANSLATORSTABINTERVAL, UNSIGNED, RWRITE, ospfv3AreaEntry, 4, {1, 2, 1, 13}}, {OSPFv3AREANSSATRANSLATOREVENTS, COUNTER, RONLY, ospfv3AreaEntry, 4, {1, 2, 1, 14}}, {OSPFv3AREASTUBMETRICTYPE, INTEGER, RWRITE, ospfv3AreaEntry, 4, {1, 2, 1, 15}}, + {OSPFv3AREATEENABLED, INTEGER, RWRITE, ospfv3AreaEntry, + 4, {1, 2, 1, 16}}, - {OSPFv3AREALSDBAREAID, IPADDRESS, RONLY, ospfv3AreaLsdbEntry, - 4, {1, 4, 1, 1}}, - {OSPFv3AREALSDBTYPE, GAUGE, RONLY, ospfv3AreaLsdbEntry, - 4, {1, 4, 1, 2}}, - {OSPFv3AREALSDBROUTERID, IPADDRESS, RONLY, ospfv3AreaLsdbEntry, - 4, {1, 4, 1, 3}}, - {OSPFv3AREALSDBLSID, IPADDRESS, RONLY, ospfv3AreaLsdbEntry, - 4, {1, 4, 1, 4}}, - {OSPFv3AREALSDBSEQUENCE, INTEGER, RONLY, ospfv3AreaLsdbEntry, + /* OSPFv3 AS LSDB */ + {OSPFv3WWLSDBSEQUENCE | OSPFv3WWASTABLE, INTEGER, RONLY, ospfv3WwLsdbEntry, + 4, {1, 3, 1, 4}}, + {OSPFv3WWLSDBAGE | OSPFv3WWASTABLE, UNSIGNED, RONLY, ospfv3WwLsdbEntry, + 4, {1, 3, 1, 5}}, + {OSPFv3WWLSDBCHECKSUM | OSPFv3WWASTABLE, INTEGER, RONLY, ospfv3WwLsdbEntry, + 4, {1, 3, 1, 6}}, + {OSPFv3WWLSDBADVERTISEMENT | OSPFv3WWASTABLE, STRING, RONLY, ospfv3WwLsdbEntry, + 4, {1, 3, 1, 7}}, + {OSPFv3WWLSDBTYPEKNOWN | OSPFv3WWASTABLE, INTEGER, RONLY, ospfv3WwLsdbEntry, + 4, {1, 3, 1, 8}}, + + /* OSPFv3 Area LSDB */ + {OSPFv3WWLSDBSEQUENCE | OSPFv3WWAREATABLE, INTEGER, RONLY, ospfv3WwLsdbEntry, 4, {1, 4, 1, 5}}, - {OSPFv3AREALSDBAGE, INTEGER, RONLY, ospfv3AreaLsdbEntry, + {OSPFv3WWLSDBAGE | OSPFv3WWAREATABLE, UNSIGNED, RONLY, ospfv3WwLsdbEntry, 4, {1, 4, 1, 6}}, - {OSPFv3AREALSDBCHECKSUM, INTEGER, RONLY, ospfv3AreaLsdbEntry, + {OSPFv3WWLSDBCHECKSUM | OSPFv3WWAREATABLE, INTEGER, RONLY, ospfv3WwLsdbEntry, 4, {1, 4, 1, 7}}, - {OSPFv3AREALSDBADVERTISEMENT, STRING, RONLY, ospfv3AreaLsdbEntry, + {OSPFv3WWLSDBADVERTISEMENT | OSPFv3WWAREATABLE, STRING, RONLY, ospfv3WwLsdbEntry, 4, {1, 4, 1, 8}}, - {OSPFv3AREALSDBTYPEKNOWN, INTEGER, RONLY, ospfv3AreaLsdbEntry, + {OSPFv3WWLSDBTYPEKNOWN | OSPFv3WWAREATABLE, INTEGER, RONLY, ospfv3WwLsdbEntry, 4, {1, 4, 1, 9}}, + /* OSPFv3 Link LSDB */ + {OSPFv3WWLSDBSEQUENCE | OSPFv3WWLINKTABLE, INTEGER, RONLY, ospfv3WwLsdbEntry, + 4, {1, 5, 1, 6}}, + {OSPFv3WWLSDBAGE | OSPFv3WWLINKTABLE, UNSIGNED, RONLY, ospfv3WwLsdbEntry, + 4, {1, 5, 1, 7}}, + {OSPFv3WWLSDBCHECKSUM | OSPFv3WWLINKTABLE, INTEGER, RONLY, ospfv3WwLsdbEntry, + 4, {1, 5, 1, 8}}, + {OSPFv3WWLSDBADVERTISEMENT | OSPFv3WWLINKTABLE, STRING, RONLY, ospfv3WwLsdbEntry, + 4, {1, 5, 1, 9}}, + {OSPFv3WWLSDBTYPEKNOWN | OSPFv3WWLINKTABLE, INTEGER, RONLY, ospfv3WwLsdbEntry, + 4, {1, 5, 1, 10}}, + + /* OSPFv3 interfaces */ + {OSPFv3IFAREAID, UNSIGNED, RONLY, ospfv3IfEntry, + 4, {1, 7, 1, 3}}, + {OSPFv3IFTYPE, INTEGER, RONLY, ospfv3IfEntry, + 4, {1, 7, 1, 4}}, + {OSPFv3IFADMINSTATUS, INTEGER, RONLY, ospfv3IfEntry, + 4, {1, 7, 1, 5}}, + {OSPFv3IFRTRPRIORITY, INTEGER, RONLY, ospfv3IfEntry, + 4, {1, 7, 1, 6}}, + {OSPFv3IFTRANSITDELAY, UNSIGNED, RONLY, ospfv3IfEntry, + 4, {1, 7, 1, 7}}, + {OSPFv3IFRETRANSINTERVAL, UNSIGNED, RONLY, ospfv3IfEntry, + 4, {1, 7, 1, 8}}, + {OSPFv3IFHELLOINTERVAL, INTEGER, RONLY, ospfv3IfEntry, + 4, {1, 7, 1, 9}}, + {OSPFv3IFRTRDEADINTERVAL, UNSIGNED, RONLY, ospfv3IfEntry, + 4, {1, 7, 1, 10}}, + {OSPFv3IFPOLLINTERVAL, UNSIGNED, RONLY, ospfv3IfEntry, + 4, {1, 7, 1, 11}}, + {OSPFv3IFSTATE, INTEGER, RONLY, ospfv3IfEntry, + 4, {1, 7, 1, 12}}, + {OSPFv3IFDESIGNATEDROUTER, UNSIGNED, RONLY, ospfv3IfEntry, + 4, {1, 7, 1, 13}}, + {OSPFv3IFBACKUPDESIGNATEDROUTER, UNSIGNED, RONLY, ospfv3IfEntry, + 4, {1, 7, 1, 14}}, + {OSPFv3IFEVENTS, COUNTER, RONLY, ospfv3IfEntry, + 4, {1, 7, 1, 15}}, + {OSPFv3IFROWSTATUS, INTEGER, RONLY, ospfv3IfEntry, + 4, {1, 7, 1, 16}}, + {OSPFv3IFDEMAND, INTEGER, RONLY, ospfv3IfEntry, + 4, {1, 7, 1, 17}}, + {OSPFv3IFMETRICVALUE, INTEGER, RONLY, ospfv3IfEntry, + 4, {1, 7, 1, 18}}, + {OSPFv3IFLINKSCOPELSACOUNT, GAUGE, RONLY, ospfv3IfEntry, + 4, {1, 7, 1, 19}}, + {OSPFv3IFLINKLSACKSUMSUM, UNSIGNED, RONLY, ospfv3IfEntry, + 4, {1, 7, 1, 20}}, + {OSPFv3IFDEMANDNBRPROBE, INTEGER, RONLY, ospfv3IfEntry, + 4, {1, 7, 1, 21}}, + {OSPFv3IFDEMANDNBRPROBERETRANSLIMIT, UNSIGNED, RONLY, ospfv3IfEntry, + 4, {1, 7, 1, 22}}, + {OSPFv3IFDEMANDNBRPROBEINTERVAL, UNSIGNED, RONLY, ospfv3IfEntry, + 4, {1, 7, 1, 23}}, + {OSPFv3IFTEDISABLED, INTEGER, RONLY, ospfv3IfEntry, + 4, {1, 7, 1, 24}}, + {OSPFv3IFLINKLSASUPPRESSION, INTEGER, RONLY, ospfv3IfEntry, + 4, {1, 7, 1, 25}}, + + /* OSPFv3 neighbors */ + {OSPFv3NBRADDRESSTYPE, INTEGER, RONLY, ospfv3NbrEntry, + 4, {1, 9, 1, 4}}, + {OSPFv3NBRADDRESS, STRING, RONLY, ospfv3NbrEntry, + 4, {1, 9, 1, 5}}, + {OSPFv3NBROPTIONS, INTEGER, RONLY, ospfv3NbrEntry, + 4, {1, 9, 1, 6}}, + {OSPFv3NBRPRIORITY, INTEGER, RONLY, ospfv3NbrEntry, + 4, {1, 9, 1, 7}}, + {OSPFv3NBRSTATE, INTEGER, RONLY, ospfv3NbrEntry, + 4, {1, 9, 1, 8}}, + {OSPFv3NBREVENTS, COUNTER, RONLY, ospfv3NbrEntry, + 4, {1, 9, 1, 9}}, + {OSPFv3NBRLSRETRANSQLEN, GAUGE, RONLY, ospfv3NbrEntry, + 4, {1, 9, 1, 10}}, + {OSPFv3NBRHELLOSUPPRESSED, INTEGER, RONLY, ospfv3NbrEntry, + 4, {1, 9, 1, 11}}, + {OSPFv3NBRIFID, INTEGER, RONLY, ospfv3NbrEntry, + 4, {1, 9, 1, 12}}, + {OSPFv3NBRRESTARTHELPERSTATUS, INTEGER, RONLY, ospfv3NbrEntry, + 4, {1, 9, 1, 13}}, + {OSPFv3NBRRESTARTHELPERAGE, UNSIGNED, RONLY, ospfv3NbrEntry, + 4, {1, 9, 1, 14}}, + {OSPFv3NBRRESTARTHELPEREXITREASON, INTEGER, RONLY, ospfv3NbrEntry, + 4, {1, 9, 1, 15}}, }; static u_char * ospfv3GeneralGroup (struct variable *v, oid *name, size_t *length, int exact, size_t *var_len, WriteMethod **write_method) { + u_int16_t sum; + u_int32_t count; + struct ospf6_lsa *lsa = NULL; + /* Check whether the instance identifier is valid */ if (smux_header_generic (v, name, length, exact, var_len, write_method) == MATCH_FAILED) @@ -247,54 +433,78 @@ ospfv3GeneralGroup (struct variable *v, oid *name, size_t *length, /* Return the current value of the variable */ switch (v->magic) { - case OSPFv3ROUTERID: /* 1*/ + case OSPFv3ROUTERID: /* Router-ID of this OSPF instance. */ if (ospf6) - return SNMP_IPADDRESS (INT32_INADDR (ospf6->router_id)); - else - return SNMP_IPADDRESS (ospf6_empty_id); - break; - case OSPFv3ADMINSTAT: /* 2*/ - break; - case OSPFv3VERSIONNUMBER: /* 3*/ - break; - case OSPFv3AREABDRRTRSTATUS: /* 4*/ - break; - case OSPFv3ASBDRRTRSTATUS: /* 5*/ - break; - case OSPFv3ASSCOPELSACOUNT: /* 6*/ - break; - case OSPFv3ASSCOPELSACHECKSUMSUM: /* 7*/ - break; - case OSPFv3ORIGINATENEWLSAS: /* 8*/ - break; - case OSPFv3RXNEWLSAS: /* 9*/ - break; - case OSPFv3EXTLSACOUNT: /*10*/ - break; - case OSPFv3EXTAREALSDBLIMIT: /*11*/ - break; - case OSPFv3MULTICASTEXTENSIONS: /*12*/ - break; - case OSPFv3EXITOVERFLOWINTERVAL: /*13*/ - break; - case OSPFv3DEMANDEXTENSIONS: /*14*/ - break; - case OSPFv3TRAFFICENGINEERINGSUPPORT: /*15*/ - break; - case OSPFv3REFERENCEBANDWIDTH: /*16*/ - break; - case OSPFv3RESTARTSUPPORT: /*17*/ - break; - case OSPFv3RESTARTINTERVAL: /*18*/ - break; - case OSPFv3RESTARTSTATUS: /*19*/ - break; - case OSPFv3RESTARTAGE: /*20*/ - break; - case OSPFv3RESTARTEXITREASON: /*21*/ - break; - default: + return SNMP_INTEGER (ntohl (ospf6->router_id)); + return SNMP_INTEGER (0); + case OSPFv3ADMINSTAT: + if (ospf6) + return SNMP_INTEGER (CHECK_FLAG (ospf6->flag, OSPF6_DISABLED)? + OSPF_STATUS_DISABLED:OSPF_STATUS_ENABLED); + return SNMP_INTEGER (OSPF_STATUS_DISABLED); + case OSPFv3VERSIONNUMBER: + return SNMP_INTEGER (3); + case OSPFv3AREABDRRTRSTATUS: + if (ospf6) + return SNMP_INTEGER (ospf6_is_router_abr (ospf6)?SNMP_TRUE:SNMP_FALSE); + return SNMP_INTEGER (SNMP_FALSE); + case OSPFv3ASBDRRTRSTATUS: + if (ospf6) + return SNMP_INTEGER (ospf6_asbr_is_asbr (ospf6)?SNMP_TRUE:SNMP_FALSE); + return SNMP_INTEGER (SNMP_FALSE); + case OSPFv3ASSCOPELSACOUNT: + if (ospf6) + return SNMP_INTEGER (ospf6->lsdb->count); + return SNMP_INTEGER (0); + case OSPFv3ASSCOPELSACHECKSUMSUM: + if (ospf6) + { + for (sum = 0, lsa = ospf6_lsdb_head (ospf6->lsdb); + lsa; + lsa = ospf6_lsdb_next (lsa)) + sum += ntohs (lsa->header->checksum); + return SNMP_INTEGER (sum); + } + return SNMP_INTEGER (0); + case OSPFv3ORIGINATENEWLSAS: + return SNMP_INTEGER (0); /* Don't know where to get this value... */ + case OSPFv3RXNEWLSAS: + return SNMP_INTEGER (0); /* Don't know where to get this value... */ + case OSPFv3EXTLSACOUNT: + if (ospf6) + { + for (count = 0, lsa = ospf6_lsdb_type_head (htons (OSPF6_LSTYPE_AS_EXTERNAL), + ospf6->lsdb); + lsa; + lsa = ospf6_lsdb_type_next (htons (OSPF6_LSTYPE_AS_EXTERNAL), + lsa)) + count += 1; + return SNMP_INTEGER (count); + } + return SNMP_INTEGER (0); + case OSPFv3EXTAREALSDBLIMIT: + return SNMP_INTEGER (-1); + case OSPFv3EXITOVERFLOWINTERVAL: + return SNMP_INTEGER (0); /* Not supported */ + case OSPFv3DEMANDEXTENSIONS: + return SNMP_INTEGER (0); /* Not supported */ + case OSPFv3REFERENCEBANDWIDTH: + if (ospf6) + return SNMP_INTEGER (ospf6->ref_bandwidth); + /* Otherwise, like for "not implemented". */ + case OSPFv3RESTARTSUPPORT: + case OSPFv3RESTARTINTERVAL: + case OSPFv3RESTARTSTRICTLSACHECKING: + case OSPFv3RESTARTSTATUS: + case OSPFv3RESTARTAGE: + case OSPFv3RESTARTEXITREASON: + case OSPFv3NOTIFICATIONENABLE: + case OSPFv3STUBROUTERSUPPORT: + case OSPFv3STUBROUTERADVERTISEMENT: + case OSPFv3DISCONTINUITYTIME: + case OSPFv3RESTARTTIME: + /* TODO: Not implemented */ return NULL; } return NULL; @@ -305,23 +515,32 @@ ospfv3AreaEntry (struct variable *v, oid *name, size_t *length, int exact, size_t *var_len, WriteMethod **write_method) { struct ospf6_area *oa, *area = NULL; + struct ospf6_lsa *lsa = NULL; u_int32_t area_id = 0; + u_int32_t count; + u_int16_t sum; struct listnode *node; unsigned int len; + char a[16]; + struct ospf6_route *ro; if (ospf6 == NULL) return NULL; + if (smux_header_table(v, name, length, exact, var_len, write_method) + == MATCH_FAILED) + return NULL; + len = *length - v->namelen; - len = (len >= sizeof (u_int32_t) ? sizeof (u_int32_t) : 0); - if (exact && len != sizeof (u_int32_t)) + len = (len >= 1 ? 1 : 0); + if (exact && len != 1) return NULL; if (len) - oid2in_addr (name + v->namelen, len, (struct in_addr *) &area_id); + area_id = htonl (name[v->namelen]); + inet_ntop (AF_INET, &area_id, a, sizeof (a)); zlog_debug ("SNMP access by area: %s, exact=%d len=%d length=%lu", - inet_ntoa (* (struct in_addr *) &area_id), - exact, len, (u_long)*length); + a, exact, len, (u_long)*length); for (ALL_LIST_ELEMENTS_RO (ospf6->area_list, node, oa)) { @@ -339,49 +558,87 @@ ospfv3AreaEntry (struct variable *v, oid *name, size_t *length, if (area == NULL) return NULL; - *length = v->namelen + sizeof (u_int32_t); - oid_copy_addr (name + v->namelen, (struct in_addr *) &area->area_id, - sizeof (u_int32_t)); + *length = v->namelen + 1; + name[v->namelen] = ntohl (area->area_id); + inet_ntop (AF_INET, &area->area_id, a, sizeof (a)); zlog_debug ("SNMP found area: %s, exact=%d len=%d length=%lu", - inet_ntoa (* (struct in_addr *) &area->area_id), - exact, len, (u_long)*length); + a, exact, len, (u_long)*length); switch (v->magic) { - case OSPFv3AREAID: /* 1*/ - return SNMP_IPADDRESS (INT32_INADDR (area->area_id)); - break; - case OSPFv3IMPORTASEXTERN: /* 2*/ - return SNMP_INTEGER (ospf6->external_table->count); - break; - default: + case OSPFv3IMPORTASEXTERN: + /* No NSSA support */ + return SNMP_INTEGER (IS_AREA_STUB(area)?2:1); + case OSPFv3AREASPFRUNS: + return SNMP_INTEGER (area->spf_calculation); + case OSPFv3AREABDRRTRCOUNT: + case OSPFv3AREAASBDRRTRCOUNT: + count = 0; + for (ro = ospf6_route_head (ospf6->brouter_table); ro; + ro = ospf6_route_next (ro)) + { + if (ntohl (ro->path.area_id) != ntohl (area->area_id)) continue; + if (v->magic == OSPFv3AREABDRRTRCOUNT && + CHECK_FLAG (ro->path.router_bits, OSPF6_ROUTER_BIT_B)) + count++; + if (v->magic == OSPFv3AREAASBDRRTRCOUNT && + CHECK_FLAG (ro->path.router_bits, OSPF6_ROUTER_BIT_E)) + count++; + } + return SNMP_INTEGER (count); + case OSPFv3AREASCOPELSACOUNT: + return SNMP_INTEGER (area->lsdb->count); + case OSPFv3AREASCOPELSACKSUMSUM: + for (sum = 0, lsa = ospf6_lsdb_head (area->lsdb); + lsa; + lsa = ospf6_lsdb_next (lsa)) + sum += ntohs (lsa->header->checksum); + return SNMP_INTEGER (sum); + case OSPFv3AREASUMMARY: + return SNMP_INTEGER (2); /* sendAreaSummary */ + case OSPFv3AREAROWSTATUS: + return SNMP_INTEGER (1); /* Active */ + case OSPFv3AREASTUBMETRIC: + case OSPFv3AREANSSATRANSLATORROLE: + case OSPFv3AREANSSATRANSLATORSTATE: + case OSPFv3AREANSSATRANSLATORSTABINTERVAL: + case OSPFv3AREANSSATRANSLATOREVENTS: + case OSPFv3AREASTUBMETRICTYPE: + case OSPFv3AREATEENABLED: + /* Not implemented. */ return NULL; - break; } return NULL; } +static int +if_icmp_func (struct interface *ifp1, struct interface *ifp2) +{ + return (ifp1->ifindex - ifp2->ifindex); +} + static u_char * -ospfv3AreaLsdbEntry (struct variable *v, oid *name, size_t *length, +ospfv3WwLsdbEntry (struct variable *v, oid *name, size_t *length, int exact, size_t *var_len, WriteMethod **write_method) { struct ospf6_lsa *lsa = NULL; - struct in_addr area_id; + ifindex_t ifindex, area_id, id, instid, adv_router; u_int16_t type; - struct in_addr id; - struct in_addr adv_router; int len; oid *offset; int offsetlen; - char a[16], b[16], c[16]; - struct ospf6_area *oa; + struct ospf6_area *oa = NULL; struct listnode *node; + struct interface *iif; + struct ospf6_interface *oi = NULL; + struct list *ifslist; + + if (smux_header_table(v, name, length, exact, var_len, write_method) + == MATCH_FAILED) + return NULL; - memset (&area_id, 0, sizeof (struct in_addr)); - type = 0; - memset (&id, 0, sizeof (struct in_addr)); - memset (&adv_router, 0, sizeof (struct in_addr)); + instid = ifindex = area_id = type = id = adv_router = 0; /* Check OSPFv3 instance. */ if (ospf6 == NULL) @@ -391,137 +648,528 @@ ospfv3AreaLsdbEntry (struct variable *v, oid *name, size_t *length, offset = name + v->namelen; offsetlen = *length - v->namelen; -#define OSPFV3_AREA_LSDB_ENTRY_EXACT_OFFSET \ - (IN_ADDR_SIZE + 1 + IN_ADDR_SIZE + IN_ADDR_SIZE) - - if (exact && offsetlen != OSPFV3_AREA_LSDB_ENTRY_EXACT_OFFSET) + if (exact && (v->magic & OSPFv3WWASTABLE) && offsetlen != 3) + return NULL; + if (exact && (v->magic & OSPFv3WWAREATABLE) && offsetlen != 4) + return NULL; + if (exact && (v->magic & OSPFv3WWLINKTABLE) && offsetlen != 5) return NULL; - /* Parse area-id */ - len = (offsetlen < IN_ADDR_SIZE ? offsetlen : IN_ADDR_SIZE); - if (len) - oid2in_addr (offset, len, &area_id); - offset += len; - offsetlen -= len; + if (v->magic & OSPFv3WWLINKTABLE) + { + /* Parse ifindex */ + len = (offsetlen < 1 ? 0 : 1); + if (len) + ifindex = *offset; + offset += len; + offsetlen -= len; + + /* Parse instance ID */ + len = (offsetlen < 1 ? 0 : 1); + if (len) + instid = *offset; + offset += len; + offsetlen -= len; + } + else if (v->magic & OSPFv3WWAREATABLE) + { + /* Parse area-id */ + len = (offsetlen < 1 ? 0 : 1); + if (len) + area_id = htonl (*offset); + offset += len; + offsetlen -= len; + } /* Parse type */ - len = (offsetlen < 1 ? offsetlen : 1); + len = (offsetlen < 1 ? 0 : 1); if (len) type = htons (*offset); offset += len; offsetlen -= len; /* Parse Router-ID */ - len = (offsetlen < IN_ADDR_SIZE ? offsetlen : IN_ADDR_SIZE); + len = (offsetlen < 1 ? 0 : 1); if (len) - oid2in_addr (offset, len, &adv_router); + adv_router = htonl (*offset); offset += len; offsetlen -= len; /* Parse LS-ID */ - len = (offsetlen < IN_ADDR_SIZE ? offsetlen : IN_ADDR_SIZE); + len = (offsetlen < 1 ? 0 : 1); if (len) - oid2in_addr (offset, len, &id); + id = htonl (*offset); offset += len; offsetlen -= len; - inet_ntop (AF_INET, &area_id, a, sizeof (a)); - inet_ntop (AF_INET, &adv_router, b, sizeof (b)); - inet_ntop (AF_INET, &id, c, sizeof (c)); - zlog_debug ("SNMP access by lsdb: area=%s exact=%d length=%lu magic=%d" - " type=%#x adv_router=%s id=%s", - a, exact, (u_long)*length, v->magic, ntohs (type), b, c); - if (exact) { - oa = ospf6_area_lookup (area_id.s_addr, ospf6); - lsa = ospf6_lsdb_lookup (type, id.s_addr, adv_router.s_addr, oa->lsdb); + if (v->magic & OSPFv3WWASTABLE) + { + lsa = ospf6_lsdb_lookup (type, id, adv_router, ospf6->lsdb); + } + else if (v->magic & OSPFv3WWAREATABLE) + { + oa = ospf6_area_lookup (area_id, ospf6); + if (!oa) return NULL; + lsa = ospf6_lsdb_lookup (type, id, adv_router, oa->lsdb); + } + else if (v->magic & OSPFv3WWLINKTABLE) + { + oi = ospf6_interface_lookup_by_ifindex (ifindex); + if (!oi || oi->instance_id != instid) return NULL; + lsa = ospf6_lsdb_lookup (type, id, adv_router, oi->lsdb); + } } else { - for (ALL_LIST_ELEMENTS_RO (ospf6->area_list, node, oa)) + if (v->magic & OSPFv3WWASTABLE) + { + if (ospf6->lsdb->count) + lsa = ospf6_lsdb_lookup_next (type, id, adv_router, + ospf6->lsdb); + } + else if (v->magic & OSPFv3WWAREATABLE) + for (ALL_LIST_ELEMENTS_RO (ospf6->area_list, node, oa)) + { + if (oa->area_id < area_id) + continue; + + if (oa->lsdb->count) + lsa = ospf6_lsdb_lookup_next (type, id, adv_router, + oa->lsdb); + if (lsa) break; + type = 0; + id = 0; + adv_router = 0; + } + else if (v->magic & OSPFv3WWLINKTABLE) { - if (lsa) - continue; - if (ntohl (oa->area_id) < ntohl (area_id.s_addr)) - continue; - - lsa = ospf6_lsdb_lookup_next (type, id.s_addr, adv_router.s_addr, - oa->lsdb); - if (! lsa) + /* We build a sorted list of interfaces */ + ifslist = list_new (); + if (!ifslist) return NULL; + ifslist->cmp = (int (*)(void *, void *))if_icmp_func; + for (ALL_LIST_ELEMENTS_RO (iflist, node, iif)) + listnode_add_sort (ifslist, iif); + + for (ALL_LIST_ELEMENTS_RO (ifslist, node, iif)) { + if (!iif->ifindex) continue; + oi = ospf6_interface_lookup_by_ifindex (iif->ifindex); + if (!oi) continue; + if (iif->ifindex < ifindex) continue; + if (oi->instance_id < instid) continue; + + if (oi->lsdb->count) + lsa = ospf6_lsdb_lookup_next (type, id, adv_router, + oi->lsdb); + if (lsa) break; type = 0; - memset (&id, 0, sizeof (struct in_addr)); - memset (&adv_router, 0, sizeof (struct in_addr)); + id = 0; + adv_router = 0; + oi = NULL; } + + list_delete_all_node (ifslist); } } if (! lsa) - { - zlog_debug ("SNMP respond: No LSA to return"); return NULL; - } - oa = OSPF6_AREA (lsa->lsdb->data); - - zlog_debug ("SNMP respond: area: %s lsa: %s", oa->name, lsa->name); - /* Add Index (AreaId, Type, RouterId, Lsid) */ - *length = v->namelen + OSPFV3_AREA_LSDB_ENTRY_EXACT_OFFSET; - offset = name + v->namelen; - oid_copy_addr (offset, (struct in_addr *) &oa->area_id, IN_ADDR_SIZE); - offset += IN_ADDR_SIZE; + /* Add indexes */ + if (v->magic & OSPFv3WWASTABLE) + { + *length = v->namelen + 3; + offset = name + v->namelen; + } + else if (v->magic & OSPFv3WWAREATABLE) + { + *length = v->namelen + 4; + offset = name + v->namelen; + *offset = ntohl (oa->area_id); + offset++; + } + else if (v->magic & OSPFv3WWLINKTABLE) + { + *length = v->namelen + 5; + offset = name + v->namelen; + *offset = oi->interface->ifindex; + offset++; + *offset = oi->instance_id; + offset++; + } *offset = ntohs (lsa->header->type); offset++; - oid_copy_addr (offset, (struct in_addr *) &lsa->header->adv_router, - IN_ADDR_SIZE); - offset += IN_ADDR_SIZE; - oid_copy_addr (offset, (struct in_addr *) &lsa->header->id, IN_ADDR_SIZE); - offset += IN_ADDR_SIZE; + *offset = ntohl (lsa->header->adv_router); + offset++; + *offset = ntohl (lsa->header->id); + offset++; /* Return the current value of the variable */ - switch (v->magic) + switch (v->magic & OSPFv3WWCOLUMN) { - case OSPFv3AREALSDBAREAID: /* 1 */ - area_id.s_addr = OSPF6_AREA (lsa->lsdb->data)->area_id; - return SNMP_IPADDRESS (area_id); - break; - case OSPFv3AREALSDBTYPE: /* 2 */ - return SNMP_INTEGER (ntohs (lsa->header->type)); - break; - case OSPFv3AREALSDBROUTERID: /* 3 */ - adv_router.s_addr = lsa->header->adv_router; - return SNMP_IPADDRESS (adv_router); - break; - case OSPFv3AREALSDBLSID: /* 4 */ - id.s_addr = lsa->header->id; - return SNMP_IPADDRESS (id); + case OSPFv3WWLSDBSEQUENCE: + return SNMP_INTEGER (ntohl (lsa->header->seqnum)); break; - case OSPFv3AREALSDBSEQUENCE: /* 5 */ - return SNMP_INTEGER (lsa->header->seqnum); - break; - case OSPFv3AREALSDBAGE: /* 6 */ + case OSPFv3WWLSDBAGE: ospf6_lsa_age_current (lsa); - return SNMP_INTEGER (lsa->header->age); + return SNMP_INTEGER (ntohs (lsa->header->age)); break; - case OSPFv3AREALSDBCHECKSUM: /* 7 */ - return SNMP_INTEGER (lsa->header->checksum); + case OSPFv3WWLSDBCHECKSUM: + return SNMP_INTEGER (ntohs (lsa->header->checksum)); break; - case OSPFv3AREALSDBADVERTISEMENT: /* 8 */ + case OSPFv3WWLSDBADVERTISEMENT: *var_len = ntohs (lsa->header->length); return (u_char *) lsa->header; break; - case OSPFv3AREALSDBTYPEKNOWN: /* 9 */ + case OSPFv3WWLSDBTYPEKNOWN: return SNMP_INTEGER (OSPF6_LSA_IS_KNOWN (lsa->header->type) ? SNMP_TRUE : SNMP_FALSE); break; - default: - return NULL; + } + return NULL; +} + +static u_char * +ospfv3IfEntry (struct variable *v, oid *name, size_t *length, + int exact, size_t *var_len, WriteMethod **write_method) +{ + ifindex_t ifindex = 0; + unsigned int instid = 0; + struct ospf6_interface *oi = NULL; + struct ospf6_lsa *lsa = NULL; + struct interface *iif; + struct listnode *i; + struct list *ifslist; + oid *offset; + int offsetlen, len; + u_int32_t sum; + + if (smux_header_table (v, name, length, exact, var_len, write_method) + == MATCH_FAILED) + return NULL; + + /* Check OSPFv3 instance. */ + if (ospf6 == NULL) + return NULL; + + /* Get variable length. */ + offset = name + v->namelen; + offsetlen = *length - v->namelen; + + if (exact && offsetlen != 2) + return NULL; + + /* Parse if index */ + len = (offsetlen < 1 ? 0 : 1); + if (len) + ifindex = *offset; + offset += len; + offsetlen -= len; + + /* Parse instance ID */ + len = (offsetlen < 1 ? 0 : 1); + if (len) + instid = *offset; + offset += len; + offsetlen -= len; + + if (exact) + { + oi = ospf6_interface_lookup_by_ifindex (ifindex); + if (!oi || oi->instance_id != instid) return NULL; + } + else + { + /* We build a sorted list of interfaces */ + ifslist = list_new (); + if (!ifslist) return NULL; + ifslist->cmp = (int (*)(void *, void *))if_icmp_func; + for (ALL_LIST_ELEMENTS_RO (iflist, i, iif)) + listnode_add_sort (ifslist, iif); + + for (ALL_LIST_ELEMENTS_RO (ifslist, i, iif)) + { + if (!iif->ifindex) continue; + oi = ospf6_interface_lookup_by_ifindex (iif->ifindex); + if (!oi) continue; + if (iif->ifindex > ifindex || + (iif->ifindex == ifindex && + (oi->instance_id > instid))) + break; + oi = NULL; + } + + list_delete_all_node (ifslist); + } + + if (!oi) return NULL; + + /* Add Index (IfIndex, IfInstId) */ + *length = v->namelen + 2; + offset = name + v->namelen; + *offset = oi->interface->ifindex; + offset++; + *offset = oi->instance_id; + offset++; + + /* Return the current value of the variable */ + switch (v->magic) + { + case OSPFv3IFAREAID: + if (oi->area) + return SNMP_INTEGER (ntohl (oi->area->area_id)); + break; + case OSPFv3IFTYPE: + if (if_is_broadcast (oi->interface)) + return SNMP_INTEGER (1); + else if (if_is_pointopoint (oi->interface)) + return SNMP_INTEGER (3); + else break; /* Unknown, don't put anything */ + case OSPFv3IFADMINSTATUS: + if (oi->area) + return SNMP_INTEGER (OSPF_STATUS_ENABLED); + return SNMP_INTEGER (OSPF_STATUS_DISABLED); + case OSPFv3IFRTRPRIORITY: + return SNMP_INTEGER (oi->priority); + case OSPFv3IFTRANSITDELAY: + return SNMP_INTEGER (oi->transdelay); + case OSPFv3IFRETRANSINTERVAL: + return SNMP_INTEGER (oi->rxmt_interval); + case OSPFv3IFHELLOINTERVAL: + return SNMP_INTEGER (oi->hello_interval); + case OSPFv3IFRTRDEADINTERVAL: + return SNMP_INTEGER (oi->dead_interval); + case OSPFv3IFPOLLINTERVAL: + /* No support for NBMA */ break; + case OSPFv3IFSTATE: + return SNMP_INTEGER (oi->state); + case OSPFv3IFDESIGNATEDROUTER: + return SNMP_INTEGER (ntohl (oi->drouter)); + case OSPFv3IFBACKUPDESIGNATEDROUTER: + return SNMP_INTEGER (ntohl (oi->bdrouter)); + case OSPFv3IFEVENTS: + return SNMP_INTEGER (oi->state_change); + case OSPFv3IFROWSTATUS: + return SNMP_INTEGER (1); + case OSPFv3IFDEMAND: + return SNMP_INTEGER (SNMP_FALSE); + case OSPFv3IFMETRICVALUE: + return SNMP_INTEGER (oi->cost); + case OSPFv3IFLINKSCOPELSACOUNT: + return SNMP_INTEGER (oi->lsdb->count); + case OSPFv3IFLINKLSACKSUMSUM: + for (sum = 0, lsa = ospf6_lsdb_head (oi->lsdb); + lsa; + lsa = ospf6_lsdb_next (lsa)) + sum += ntohs (lsa->header->checksum); + return SNMP_INTEGER (sum); + case OSPFv3IFDEMANDNBRPROBE: + case OSPFv3IFDEMANDNBRPROBERETRANSLIMIT: + case OSPFv3IFDEMANDNBRPROBEINTERVAL: + case OSPFv3IFTEDISABLED: + case OSPFv3IFLINKLSASUPPRESSION: + /* Not implemented. Only works if all the last ones are not + implemented! */ + return NULL; + } + + /* Try an internal getnext. Some columns are missing in this table. */ + if (!exact && (name[*length-1] < MAX_SUBID)) + return ospfv3IfEntry(v, name, length, + exact, var_len, write_method); + return NULL; +} + +static u_char * +ospfv3NbrEntry (struct variable *v, oid *name, size_t *length, + int exact, size_t *var_len, WriteMethod **write_method) +{ + ifindex_t ifindex = 0; + unsigned int instid, rtrid; + struct ospf6_interface *oi = NULL; + struct ospf6_neighbor *on = NULL; + struct interface *iif; + struct listnode *i, *j; + struct list *ifslist; + oid *offset; + int offsetlen, len; + + if (smux_header_table (v, name, length, exact, var_len, write_method) + == MATCH_FAILED) + return NULL; + + instid = rtrid = 0; + + /* Check OSPFv3 instance. */ + if (ospf6 == NULL) + return NULL; + + /* Get variable length. */ + offset = name + v->namelen; + offsetlen = *length - v->namelen; + + if (exact && offsetlen != 3) + return NULL; + + /* Parse if index */ + len = (offsetlen < 1 ? 0 : 1); + if (len) + ifindex = *offset; + offset += len; + offsetlen -= len; + + /* Parse instance ID */ + len = (offsetlen < 1 ? 0 : 1); + if (len) + instid = *offset; + offset += len; + offsetlen -= len; + + /* Parse router ID */ + len = (offsetlen < 1 ? 0 : 1); + if (len) + rtrid = htonl (*offset); + offset += len; + offsetlen -= len; + + if (exact) + { + oi = ospf6_interface_lookup_by_ifindex (ifindex); + if (!oi || oi->instance_id != instid) return NULL; + on = ospf6_neighbor_lookup (rtrid, oi); } + else + { + /* We build a sorted list of interfaces */ + ifslist = list_new (); + if (!ifslist) return NULL; + ifslist->cmp = (int (*)(void *, void *))if_icmp_func; + for (ALL_LIST_ELEMENTS_RO (iflist, i, iif)) + listnode_add_sort (ifslist, iif); + + for (ALL_LIST_ELEMENTS_RO (ifslist, i, iif)) + { + if (!iif->ifindex) continue; + oi = ospf6_interface_lookup_by_ifindex (iif->ifindex); + if (!oi) continue; + for (ALL_LIST_ELEMENTS_RO (oi->neighbor_list, j, on)) { + if (iif->ifindex > ifindex || + (iif->ifindex == ifindex && + (oi->instance_id > instid || + (oi->instance_id == instid && + ntohl (on->router_id) > ntohl (rtrid))))) + break; + } + if (on) break; + oi = NULL; + on = NULL; + } + + list_delete_all_node (ifslist); + } + + if (!oi || !on) return NULL; + + /* Add Index (IfIndex, IfInstId, RtrId) */ + *length = v->namelen + 3; + offset = name + v->namelen; + *offset = oi->interface->ifindex; + offset++; + *offset = oi->instance_id; + offset++; + *offset = ntohl (on->router_id); + offset++; + + /* Return the current value of the variable */ + switch (v->magic) + { + case OSPFv3NBRADDRESSTYPE: + return SNMP_INTEGER (2); /* IPv6 only */ + case OSPFv3NBRADDRESS: + *var_len = sizeof (struct in6_addr); + return (u_char *) &on->linklocal_addr; + case OSPFv3NBROPTIONS: + return SNMP_INTEGER (on->options[2]); + case OSPFv3NBRPRIORITY: + return SNMP_INTEGER (on->priority); + case OSPFv3NBRSTATE: + return SNMP_INTEGER (on->state); + case OSPFv3NBREVENTS: + return SNMP_INTEGER (on->state_change); + case OSPFv3NBRLSRETRANSQLEN: + return SNMP_INTEGER (on->retrans_list->count); + case OSPFv3NBRHELLOSUPPRESSED: + return SNMP_INTEGER (SNMP_FALSE); + case OSPFv3NBRIFID: + return SNMP_INTEGER (on->ifindex); + case OSPFv3NBRRESTARTHELPERSTATUS: + case OSPFv3NBRRESTARTHELPERAGE: + case OSPFv3NBRRESTARTHELPEREXITREASON: + /* Not implemented. Only works if all the last ones are not + implemented! */ + return NULL; + } + return NULL; } +/* OSPF Traps. */ +#define NBRSTATECHANGE 2 +#define IFSTATECHANGE 10 + +static struct trap_object ospf6NbrTrapList[] = +{ + {-3, {1, 1, OSPFv3ROUTERID}}, + {4, {1, 9, 1, OSPFv3NBRADDRESSTYPE}}, + {4, {1, 9, 1, OSPFv3NBRADDRESS}}, + {4, {1, 9, 1, OSPFv3NBRSTATE}} +}; + +static struct trap_object ospf6IfTrapList[] = +{ + {-3, {1, 1, OSPFv3ROUTERID}}, + {4, {1, 7, 1, OSPFv3IFSTATE}}, + {4, {1, 7, 1, OSPFv3IFADMINSTATUS}}, + {4, {1, 7, 1, OSPFv3IFAREAID}} +}; + +void +ospf6TrapNbrStateChange (struct ospf6_neighbor *on) +{ + oid index[3]; + + index[0] = on->ospf6_if->interface->ifindex; + index[1] = on->ospf6_if->instance_id; + index[2] = ntohl (on->router_id); + + smux_trap (ospfv3_variables, sizeof ospfv3_variables / sizeof (struct variable), + ospfv3_trap_oid, sizeof ospfv3_trap_oid / sizeof (oid), + ospfv3_oid, sizeof ospfv3_oid / sizeof (oid), + index, 3, + ospf6NbrTrapList, + sizeof ospf6NbrTrapList / sizeof (struct trap_object), + NBRSTATECHANGE); +} + +void +ospf6TrapIfStateChange (struct ospf6_interface *oi) +{ + oid index[2]; + + index[0] = oi->interface->ifindex; + index[1] = oi->instance_id; + + smux_trap (ospfv3_variables, sizeof ospfv3_variables / sizeof (struct variable), + ospfv3_trap_oid, sizeof ospfv3_trap_oid / sizeof (oid), + ospfv3_oid, sizeof ospfv3_oid / sizeof (oid), + index, 2, + ospf6IfTrapList, + sizeof ospf6IfTrapList / sizeof (struct trap_object), + IFSTATECHANGE); +} /* Register OSPFv3-MIB. */ void diff --git a/ospf6d/ospf6_snmp.h b/ospf6d/ospf6_snmp.h index 5c67893c4..fa1b0c37a 100644 --- a/ospf6d/ospf6_snmp.h +++ b/ospf6d/ospf6_snmp.h @@ -22,6 +22,8 @@ #ifndef OSPF6_SNMP_H #define OSPF6_SNMP_H +extern void ospf6TrapNbrStateChange (struct ospf6_neighbor *); +extern void ospf6TrapIfStateChange (struct ospf6_interface *); extern void ospf6_snmp_init (struct thread_master *); #endif /*OSPF6_SNMP_H*/ diff --git a/ospf6d/ospf6_spf.c b/ospf6d/ospf6_spf.c index a4a5b7218..b6dbd0d9f 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; @@ -224,7 +225,8 @@ static void ospf6_nexthop_calc (struct ospf6_vertex *w, struct ospf6_vertex *v, caddr_t lsdesc) { - int i, ifindex; + int i; + ifindex_t ifindex; struct ospf6_interface *oi; u_int16_t type; u_int32_t adv_router; @@ -234,7 +236,10 @@ ospf6_nexthop_calc (struct ospf6_vertex *w, struct ospf6_vertex *v, assert (VERTEX_IS_TYPE (ROUTER, w)); ifindex = (VERTEX_IS_TYPE (NETWORK, v) ? v->nexthop[0].ifindex : - ROUTER_LSDESC_GET_IFID (lsdesc)); + /* v is the local router & the interface_id is a local ifindex */ + (ifindex_t) ROUTER_LSDESC_GET_IFID (lsdesc)); + assert (ifindex >= 0); + oi = ospf6_interface_lookup_by_ifindex (ifindex); if (oi == NULL) { @@ -302,8 +307,8 @@ ospf6_spf_install (struct ospf6_vertex *v, if (IS_OSPF6_DEBUG_SPF (PROCESS)) zlog_debug (" another path found, merge"); - for (i = 0; ospf6_nexthop_is_set (&v->nexthop[i]) && - i < OSPF6_MULTI_PATH_LIMIT; i++) + for (i = 0; i < OSPF6_MULTI_PATH_LIMIT && + ospf6_nexthop_is_set (&v->nexthop[i]); i++) { for (j = 0; j < OSPF6_MULTI_PATH_LIMIT; j++) { @@ -351,8 +356,8 @@ ospf6_spf_install (struct ospf6_vertex *v, route->path.options[1] = v->options[1]; route->path.options[2] = v->options[2]; - for (i = 0; ospf6_nexthop_is_set (&v->nexthop[i]) && - i < OSPF6_MULTI_PATH_LIMIT; i++) + for (i = 0; i < OSPF6_MULTI_PATH_LIMIT && + ospf6_nexthop_is_set (&v->nexthop[i]); i++) ospf6_nexthop_copy (&route->nexthop[i], &v->nexthop[i]); if (v->parent) @@ -366,17 +371,48 @@ ospf6_spf_install (struct ospf6_vertex *v, void ospf6_spf_table_finish (struct ospf6_route_table *result_table) { - struct ospf6_route *route; + struct ospf6_route *route, *nroute; struct ospf6_vertex *v; for (route = ospf6_route_head (result_table); route; - route = ospf6_route_next (route)) + route = nroute) { + nroute = ospf6_route_next (route); v = (struct ospf6_vertex *) route->route_option; ospf6_vertex_delete (v); ospf6_route_remove (route, 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 < array_size(ospf6_spf_reason_str); 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 +460,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) : @@ -459,8 +500,8 @@ ospf6_spf_calculation (u_int32_t router_id, ospf6_nexthop_calc (w, v, lsdesc); else { - for (i = 0; ospf6_nexthop_is_set (&v->nexthop[i]) && - i < OSPF6_MULTI_PATH_LIMIT; i++) + for (i = 0; i < OSPF6_MULTI_PATH_LIMIT && + ospf6_nexthop_is_set (&v->nexthop[i]); i++) ospf6_nexthop_copy (&w->nexthop[i], &v->nexthop[i]); } @@ -473,6 +514,8 @@ ospf6_spf_calculation (u_int32_t router_id, } pqueue_delete (candidate_list); + + oa->spf_calculation++; } static void @@ -504,39 +547,147 @@ 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); - 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->ts_spf_duration = runtime; - ospf6_intra_route_calculation (oa); - ospf6_intra_brouter_calculation (oa); + 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: %lld sec %lld usec", + (long long)runtime.tv_sec, (long long)runtime.tv_usec); + + zlog_info("SPF processing: # Areas: %d, SPF runtime: %lld sec %lld usec, " + "Reason: %s\n", areas_processed, + (long long)runtime.tv_sec, (long long)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", + (void *)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 @@ -664,6 +815,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) { @@ -676,6 +880,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) { @@ -696,6 +913,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 c7069c258..b3481dc37 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 df856b4cf..7ec430961 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,10 @@ ospf6_create (void) o->external_id_table = route_table_init (); + o->ref_bandwidth = OSPF6_REFERENCE_BANDWIDTH; + + o->distance_table = route_table_init (); + return o; } @@ -155,6 +165,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); @@ -166,10 +178,14 @@ ospf6_delete (struct ospf6 *o) ospf6_route_table_delete (o->external_table); route_table_finish (o->external_id_table); + ospf6_distance_reset (o); + route_table_finish (o->distance_table); + XFREE (MTYPE_OSPF6_TOP, o); } static void +__attribute__((unused)) ospf6_enable (struct ospf6 *o) { struct listnode *node, *nnode; @@ -196,9 +212,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 +233,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 +245,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 +256,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 +286,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 +299,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 +314,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 +356,485 @@ 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_distance, + ospf6_distance_cmd, + "distance <1-255>", + NO_STR + "Define an administrative distance\n" + "OSPF6 Administrative distance\n") +{ + struct ospf6 *o; + + o = (struct ospf6 *) vty->index; + + o->distance_all = atoi (argv[0]); + + return CMD_SUCCESS; +} + +DEFUN (no_ospf6_distance, + no_ospf6_distance_cmd, + "no distance <1-255>", + NO_STR + "Define an administrative distance\n" + "OSPF6 Administrative distance\n") +{ + struct ospf6 *o; + + o = (struct ospf6 *) vty->index; + + o->distance_all = 0; + + return CMD_SUCCESS; +} + +DEFUN (no_ospf6_distance_ospf6, + no_ospf6_distance_ospf6_cmd, + "no distance ospf6", + NO_STR + "Define an administrative distance\n" + "OSPF6 Administrative distance\n" + "OSPF6 Distance\n") +{ + struct ospf6 *o; + + o = (struct ospf6 *) vty->index; + + o->distance_intra = 0; + o->distance_inter = 0; + o->distance_external = 0; + + return CMD_SUCCESS; +} + +DEFUN (ospf6_distance_ospf6_intra, + ospf6_distance_ospf6_intra_cmd, + "distance ospf6 intra-area <1-255>", + "Define an administrative distance\n" + "OSPF6 Administrative distance\n" + "Intra-area routes\n" + "Distance for intra-area routes\n") +{ + struct ospf6 *o; + + o = (struct ospf6 *) vty->index; + + o->distance_intra = atoi (argv[0]); + + return CMD_SUCCESS; +} + +DEFUN (ospf6_distance_ospf6_intra_inter, + ospf6_distance_ospf6_intra_inter_cmd, + "distance ospf6 intra-area <1-255> inter-area <1-255>", + "Define an administrative distance\n" + "OSPF6 Administrative distance\n" + "Intra-area routes\n" + "Distance for intra-area routes\n" + "Inter-area routes\n" + "Distance for inter-area routes\n") +{ + struct ospf6 *o; + + o = (struct ospf6 *) vty->index; + + o->distance_intra = atoi (argv[0]); + o->distance_inter = atoi (argv[1]); + + return CMD_SUCCESS; +} + +DEFUN (ospf6_distance_ospf6_intra_external, + ospf6_distance_ospf6_intra_external_cmd, + "distance ospf6 intra-area <1-255> external <1-255>", + "Define an administrative distance\n" + "OSPF6 Administrative distance\n" + "Intra-area routes\n" + "Distance for intra-area routes\n" + "External routes\n" + "Distance for external routes\n") +{ + struct ospf6 *o; + + o = (struct ospf6 *) vty->index; + + o->distance_intra = atoi (argv[0]); + o->distance_external = atoi (argv[1]); + + return CMD_SUCCESS; +} + +DEFUN (ospf6_distance_ospf6_intra_inter_external, + ospf6_distance_ospf6_intra_inter_external_cmd, + "distance ospf6 intra-area <1-255> inter-area <1-255> external <1-255>", + "Define an administrative distance\n" + "OSPF6 Administrative 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 ospf6 *o; + + o = (struct ospf6 *) vty->index; + + o->distance_intra = atoi (argv[0]); + o->distance_inter = atoi (argv[1]); + o->distance_external = atoi (argv[2]); + + return CMD_SUCCESS; +} + +DEFUN (ospf6_distance_ospf6_intra_external_inter, + ospf6_distance_ospf6_intra_external_inter_cmd, + "distance ospf6 intra-area <1-255> external <1-255> inter-area <1-255>", + "Define an administrative distance\n" + "OSPF6 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 ospf6 *o; + + o = (struct ospf6 *) vty->index; + + o->distance_intra = atoi (argv[0]); + o->distance_external = atoi (argv[1]); + o->distance_inter = atoi (argv[2]); + + return CMD_SUCCESS; +} + +DEFUN (ospf6_distance_ospf6_inter, + ospf6_distance_ospf6_inter_cmd, + "distance ospf6 inter-area <1-255>", + "Define an administrative distance\n" + "OSPF6 Administrative distance\n" + "Inter-area routes\n" + "Distance for inter-area routes\n") +{ + struct ospf6 *o; + + o = (struct ospf6 *) vty->index; + + o->distance_inter = atoi (argv[0]); + + return CMD_SUCCESS; +} + +DEFUN (ospf6_distance_ospf6_inter_intra, + ospf6_distance_ospf6_inter_intra_cmd, + "distance ospf6 inter-area <1-255> intra-area <1-255>", + "Define an administrative distance\n" + "OSPF6 Administrative distance\n" + "Inter-area routes\n" + "Distance for inter-area routes\n" + "Intra-area routes\n" + "Distance for intra-area routes\n") +{ + struct ospf6 *o; + + o = (struct ospf6 *) vty->index; + + o->distance_inter = atoi (argv[0]); + o->distance_intra = atoi (argv[1]); + + return CMD_SUCCESS; +} + +DEFUN (ospf6_distance_ospf6_inter_external, + ospf6_distance_ospf6_inter_external_cmd, + "distance ospf6 inter-area <1-255> external <1-255>", + "Define an administrative distance\n" + "OSPF6 Administrative distance\n" + "Inter-area routes\n" + "Distance for inter-area routes\n" + "External routes\n" + "Distance for external routes\n") +{ + struct ospf6 *o; + + o = (struct ospf6 *) vty->index; + + o->distance_inter = atoi (argv[0]); + o->distance_external = atoi (argv[1]); + + return CMD_SUCCESS; +} + +DEFUN (ospf6_distance_ospf6_inter_intra_external, + ospf6_distance_ospf6_inter_intra_external_cmd, + "distance ospf6 inter-area <1-255> intra-area <1-255> external <1-255>", + "Define an administrative distance\n" + "OSPF6 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 ospf6 *o; + + o = (struct ospf6 *) vty->index; + + o->distance_inter = atoi (argv[0]); + o->distance_intra = atoi (argv[1]); + o->distance_external = atoi (argv[2]); + + return CMD_SUCCESS; +} + +DEFUN (ospf6_distance_ospf6_inter_external_intra, + ospf6_distance_ospf6_inter_external_intra_cmd, + "distance ospf6 inter-area <1-255> external <1-255> intra-area <1-255>", + "Define an administrative distance\n" + "OSPF6 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 ospf6 *o; + + o = (struct ospf6 *) vty->index; + + o->distance_inter = atoi (argv[0]); + o->distance_external = atoi (argv[1]); + o->distance_intra = atoi (argv[2]); + + return CMD_SUCCESS; +} + +DEFUN (ospf6_distance_ospf6_external, + ospf6_distance_ospf6_external_cmd, + "distance ospf6 external <1-255>", + "Define an administrative distance\n" + "OSPF6 Administrative distance\n" + "External routes\n" + "Distance for external routes\n") +{ + struct ospf6 *o; + + o = (struct ospf6 *) vty->index; + + o->distance_external = atoi (argv[0]); + + return CMD_SUCCESS; +} + +DEFUN (ospf6_distance_ospf6_external_intra, + ospf6_distance_ospf6_external_intra_cmd, + "distance ospf6 external <1-255> intra-area <1-255>", + "Define an administrative distance\n" + "OSPF6 Administrative distance\n" + "External routes\n" + "Distance for external routes\n" + "Intra-area routes\n" + "Distance for intra-area routes\n") +{ + struct ospf6 *o; + + o = (struct ospf6 *) vty->index; + + o->distance_external = atoi (argv[0]); + o->distance_intra = atoi (argv[1]); + + return CMD_SUCCESS; +} + +DEFUN (ospf6_distance_ospf6_external_inter, + ospf6_distance_ospf6_external_inter_cmd, + "distance ospf6 external <1-255> inter-area <1-255>", + "Define an administrative distance\n" + "OSPF6 Administrative distance\n" + "External routes\n" + "Distance for external routes\n" + "Inter-area routes\n" + "Distance for inter-area routes\n") +{ + struct ospf6 *o; + + o = (struct ospf6 *) vty->index; + + o->distance_external = atoi (argv[0]); + o->distance_inter = atoi (argv[1]); + + return CMD_SUCCESS; +} + +DEFUN (ospf6_distance_ospf6_external_intra_inter, + ospf6_distance_ospf6_external_intra_inter_cmd, + "distance ospf6 external <1-255> intra-area <1-255> inter-area <1-255>", + "Define an administrative distance\n" + "OSPF6 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 ospf6 *o; + + o = (struct ospf6 *) vty->index; + + o->distance_external = atoi (argv[0]); + o->distance_intra = atoi (argv[1]); + o->distance_inter = atoi (argv[2]); + + return CMD_SUCCESS; +} + +DEFUN (ospf6_distance_ospf6_external_inter_intra, + ospf6_distance_ospf6_external_inter_intra_cmd, + "distance ospf6 external <1-255> inter-area <1-255> intra-area <1-255>", + "Define an administrative distance\n" + "OSPF6 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 ospf6 *o; + + o = (struct ospf6 *) vty->index; + + o->distance_external = atoi (argv[0]); + o->distance_inter = atoi (argv[1]); + o->distance_intra = atoi (argv[2]); + + return CMD_SUCCESS; +} + +DEFUN (ospf6_distance_source, + ospf6_distance_source_cmd, + "distance <1-255> X:X::X:X/M", + "Administrative distance\n" + "Distance value\n" + "IP source prefix\n") +{ + struct ospf6 *o; + + o = (struct ospf6 *) vty->index; + + ospf6_distance_set (vty, o, argv[0], argv[1], NULL); + + return CMD_SUCCESS; +} + +DEFUN (no_ospf6_distance_source, + no_ospf6_distance_source_cmd, + "no distance <1-255> X:X::X:X/M", + NO_STR + "Administrative distance\n" + "Distance value\n" + "IP source prefix\n") +{ + struct ospf6 *o; + + o = (struct ospf6 *) vty->index; + + /* XXX: distance arg seems to be irrelevant */ + ospf6_distance_unset (vty, o, argv[1], NULL); + + return CMD_SUCCESS; +} + +DEFUN (ospf6_distance_source_access_list, + ospf6_distance_source_access_list_cmd, + "distance <1-255> X:X::X:X/M WORD", + "Administrative distance\n" + "Distance value\n" + "IP source prefix\n" + "Access list name\n") +{ + struct ospf6 *o; + + o = (struct ospf6 *) vty->index; + + ospf6_distance_set (vty, o, argv[0], argv[1], argv[2]); + + return CMD_SUCCESS; +} + +DEFUN (no_ospf6_distance_source_access_list, + no_ospf6_distance_source_access_list_cmd, + "no distance <1-255> X:X::X:X/M WORD", + NO_STR + "Administrative distance\n" + "Distance value\n" + "IP source prefix\n" + "Access list name\n") +{ + struct ospf6 *o; + + o = (struct ospf6 *) vty->index; + + ospf6_distance_unset (vty, o, argv[1], argv[2]); + + return CMD_SUCCESS; +} + DEFUN (ospf6_interface_area, ospf6_interface_area_cmd, "interface IFNAME area A.B.C.D", @@ -359,8 +882,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 +906,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 +962,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 +1080,36 @@ 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 %lld sec %lld usec%s", + (long long)o->ts_spf_duration.tv_sec, + (long long)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 +1118,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); } @@ -495,6 +1155,8 @@ DEFUN (show_ipv6_ospf6_route, ROUTE_STR ) { + OSPF6_CMD_CHECK_RUNNING (); + ospf6_route_table_show (vty, argc, argv, ospf6->route_table); return CMD_SUCCESS; } @@ -526,6 +1188,8 @@ DEFUN (show_ipv6_ospf6_route_match, const char *sargv[CMD_ARGC_MAX]; int i, sargc; + OSPF6_CMD_CHECK_RUNNING (); + /* copy argv to sargv and then append "match" */ for (i = 0; i < argc; i++) sargv[i] = argv[i]; @@ -560,6 +1224,8 @@ DEFUN (show_ipv6_ospf6_route_match_detail, sargv[sargc++] = "detail"; sargv[sargc] = NULL; + OSPF6_CMD_CHECK_RUNNING (); + ospf6_route_table_show (vty, sargc, sargv, ospf6->route_table); return CMD_SUCCESS; } @@ -624,10 +1290,59 @@ DEFUN (show_ipv6_ospf6_route_type_detail, sargv[sargc++] = "detail"; sargv[sargc] = NULL; + OSPF6_CMD_CHECK_RUNNING (); + ospf6_route_table_show (vty, sargc, sargv, ospf6->route_table); 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; +} + +static int +ospf6_distance_config_write (struct vty *vty) +{ + struct route_node *rn; + struct ospf6_distance *odistance; + + if (ospf6->distance_all) + vty_out (vty, " distance %d%s", ospf6->distance_all, VTY_NEWLINE); + + if (ospf6->distance_intra + || ospf6->distance_inter + || ospf6->distance_external) + { + vty_out (vty, " distance ospf6"); + + if (ospf6->distance_intra) + vty_out (vty, " intra-area %d", ospf6->distance_intra); + if (ospf6->distance_inter) + vty_out (vty, " inter-area %d", ospf6->distance_inter); + if (ospf6->distance_external) + vty_out (vty, " external %d", ospf6->distance_external); + + vty_out (vty, "%s", VTY_NEWLINE); + } + + for (rn = route_top (ospf6->distance_table); rn; rn = route_next (rn)) + if ((odistance = rn->info) != NULL) + { + char pstr[128]; + vty_out (vty, " distance %d %s %s%s", odistance->distance, + prefix2str (&rn->p, pstr, sizeof(pstr)), + odistance->access_list ? odistance->access_list : "", + VTY_NEWLINE); + } + return 0; +} + /* OSPF configuration write function. */ static int config_write_ospf6 (struct vty *vty) @@ -640,16 +1355,30 @@ 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); + ospf6_distance_config_write (vty); for (ALL_LIST_ELEMENTS_RO (ospf6->area_list, j, oa)) { @@ -677,7 +1406,6 @@ ospf6_top_init (void) install_node (&ospf6_node, config_write_ospf6); install_element (VIEW_NODE, &show_ipv6_ospf6_cmd); - install_element (ENABLE_NODE, &show_ipv6_ospf6_cmd); install_element (CONFIG_NODE, &router_ospf6_cmd); install_element (CONFIG_NODE, &no_router_ospf6_cmd); @@ -689,19 +1417,47 @@ ospf6_top_init (void) install_element (VIEW_NODE, &show_ipv6_ospf6_route_longer_detail_cmd); install_element (VIEW_NODE, &show_ipv6_ospf6_route_type_cmd); install_element (VIEW_NODE, &show_ipv6_ospf6_route_type_detail_cmd); - install_element (ENABLE_NODE, &show_ipv6_ospf6_route_cmd); - install_element (ENABLE_NODE, &show_ipv6_ospf6_route_detail_cmd); - install_element (ENABLE_NODE, &show_ipv6_ospf6_route_match_cmd); - install_element (ENABLE_NODE, &show_ipv6_ospf6_route_match_detail_cmd); - install_element (ENABLE_NODE, &show_ipv6_ospf6_route_longer_cmd); - install_element (ENABLE_NODE, &show_ipv6_ospf6_route_longer_detail_cmd); - install_element (ENABLE_NODE, &show_ipv6_ospf6_route_type_cmd); - install_element (ENABLE_NODE, &show_ipv6_ospf6_route_type_detail_cmd); 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); + */ + + install_element (OSPF6_NODE, &ospf6_distance_cmd); + install_element (OSPF6_NODE, &no_ospf6_distance_cmd); + install_element (OSPF6_NODE, &no_ospf6_distance_ospf6_cmd); + install_element (OSPF6_NODE, &ospf6_distance_ospf6_intra_cmd); + install_element (OSPF6_NODE, &ospf6_distance_ospf6_intra_inter_cmd); + install_element (OSPF6_NODE, &ospf6_distance_ospf6_intra_external_cmd); + install_element (OSPF6_NODE, &ospf6_distance_ospf6_intra_inter_external_cmd); + install_element (OSPF6_NODE, &ospf6_distance_ospf6_intra_external_inter_cmd); + install_element (OSPF6_NODE, &ospf6_distance_ospf6_inter_cmd); + install_element (OSPF6_NODE, &ospf6_distance_ospf6_inter_intra_cmd); + install_element (OSPF6_NODE, &ospf6_distance_ospf6_inter_external_cmd); + install_element (OSPF6_NODE, &ospf6_distance_ospf6_inter_intra_external_cmd); + install_element (OSPF6_NODE, &ospf6_distance_ospf6_inter_external_intra_cmd); + install_element (OSPF6_NODE, &ospf6_distance_ospf6_external_cmd); + install_element (OSPF6_NODE, &ospf6_distance_ospf6_external_intra_cmd); + install_element (OSPF6_NODE, &ospf6_distance_ospf6_external_inter_cmd); + install_element (OSPF6_NODE, &ospf6_distance_ospf6_external_intra_inter_cmd); + install_element (OSPF6_NODE, &ospf6_distance_ospf6_external_inter_intra_cmd); + + install_element (OSPF6_NODE, &ospf6_distance_source_cmd); + install_element (OSPF6_NODE, &no_ospf6_distance_source_cmd); + install_element (OSPF6_NODE, &ospf6_distance_source_access_list_cmd); + install_element (OSPF6_NODE, &no_ospf6_distance_source_access_list_cmd); } diff --git a/ospf6d/ospf6_top.h b/ospf6d/ospf6_top.h index 4b2d2c3ef..97fac0d66 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,40 @@ 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; + + /* Distance parameters */ + u_char distance_all; + u_char distance_intra; + u_char distance_inter; + u_char distance_external; + + struct route_table *distance_table; }; #define OSPF6_DISABLED 0x01 +#define OSPF6_STUB_ROUTER 0x02 /* global pointer for OSPF top data structure */ extern struct ospf6 *ospf6; @@ -74,5 +105,3 @@ extern void ospf6_delete (struct ospf6 *o); extern void ospf6_maxage_remove (struct ospf6 *o); #endif /* OSPF6_TOP_H */ - - diff --git a/ospf6d/ospf6_zebra.c b/ospf6d/ospf6_zebra.c index f09e9d22c..2976214b0 100644 --- a/ospf6d/ospf6_zebra.c +++ b/ospf6d/ospf6_zebra.c @@ -49,7 +49,7 @@ struct in_addr router_id_zebra; /* Router-id update message from zebra. */ static int ospf6_router_id_update_zebra (int command, struct zclient *zclient, - zebra_size_t length) + zebra_size_t length, vrf_id_t vrf_id) { struct prefix router_id; struct ospf6 *o = ospf6; @@ -70,30 +70,33 @@ ospf6_router_id_update_zebra (int command, struct zclient *zclient, void ospf6_zebra_redistribute (int type) { - if (zclient->redist[type]) + if (vrf_bitmap_check (zclient->redist[type], VRF_DEFAULT)) return; - zclient->redist[type] = 1; + vrf_bitmap_set (zclient->redist[type], VRF_DEFAULT); if (zclient->sock > 0) - zebra_redistribute_send (ZEBRA_REDISTRIBUTE_ADD, zclient, type); + zebra_redistribute_send (ZEBRA_REDISTRIBUTE_ADD, zclient, type, + VRF_DEFAULT); } void ospf6_zebra_no_redistribute (int type) { - if (! zclient->redist[type]) + if (! vrf_bitmap_check (zclient->redist[type], VRF_DEFAULT)) return; - zclient->redist[type] = 0; + vrf_bitmap_unset (zclient->redist[type], VRF_DEFAULT); if (zclient->sock > 0) - zebra_redistribute_send (ZEBRA_REDISTRIBUTE_DELETE, zclient, type); + zebra_redistribute_send (ZEBRA_REDISTRIBUTE_DELETE, zclient, type, + VRF_DEFAULT); } /* Inteface addition message from zebra. */ static int -ospf6_zebra_if_add (int command, struct zclient *zclient, zebra_size_t length) +ospf6_zebra_if_add (int command, struct zclient *zclient, zebra_size_t length, + vrf_id_t vrf_id) { struct interface *ifp; - ifp = zebra_interface_add_read (zclient->ibuf); + ifp = zebra_interface_add_read (zclient->ibuf, vrf_id); if (IS_OSPF6_DEBUG_ZEBRA (RECV)) zlog_debug ("Zebra Interface add: %s index %d mtu %d", ifp->name, ifp->ifindex, ifp->mtu6); @@ -102,11 +105,12 @@ ospf6_zebra_if_add (int command, struct zclient *zclient, zebra_size_t length) } static int -ospf6_zebra_if_del (int command, struct zclient *zclient, zebra_size_t length) +ospf6_zebra_if_del (int command, struct zclient *zclient, zebra_size_t length, + vrf_id_t vrf_id) { struct interface *ifp; - if (!(ifp = zebra_interface_state_read(zclient->ibuf))) + if (!(ifp = zebra_interface_state_read (zclient->ibuf, vrf_id))) return 0; if (if_is_up (ifp)) @@ -116,30 +120,25 @@ ospf6_zebra_if_del (int command, struct zclient *zclient, zebra_size_t length) zlog_debug ("Zebra Interface delete: %s index %d mtu %d", ifp->name, ifp->ifindex, ifp->mtu6); -#if 0 - /* Why is this commented out? */ - ospf6_interface_if_del (ifp); -#endif /*0*/ - ifp->ifindex = IFINDEX_INTERNAL; return 0; } static int ospf6_zebra_if_state_update (int command, struct zclient *zclient, - zebra_size_t length) + zebra_size_t length, vrf_id_t vrf_id) { struct interface *ifp; - ifp = zebra_interface_state_read (zclient->ibuf); + ifp = zebra_interface_state_read (zclient->ibuf, vrf_id); if (ifp == NULL) return 0; 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; @@ -147,12 +146,13 @@ ospf6_zebra_if_state_update (int command, struct zclient *zclient, static int ospf6_zebra_if_address_update_add (int command, struct zclient *zclient, - zebra_size_t length) + zebra_size_t length, vrf_id_t vrf_id) { struct connected *c; char buf[128]; - c = zebra_interface_address_read (ZEBRA_INTERFACE_ADDRESS_ADD, zclient->ibuf); + c = zebra_interface_address_read (ZEBRA_INTERFACE_ADDRESS_ADD, zclient->ibuf, + vrf_id); if (c == NULL) return 0; @@ -163,19 +163,22 @@ 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; } static int ospf6_zebra_if_address_update_delete (int command, struct zclient *zclient, - zebra_size_t length) + zebra_size_t length, vrf_id_t vrf_id) { struct connected *c; char buf[128]; - c = zebra_interface_address_read (ZEBRA_INTERFACE_ADDRESS_DELETE, zclient->ibuf); + c = zebra_interface_address_read (ZEBRA_INTERFACE_ADDRESS_DELETE, zclient->ibuf, + vrf_id); if (c == NULL) return 0; @@ -186,20 +189,24 @@ 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; } static int ospf6_zebra_read_ipv6 (int command, struct zclient *zclient, - zebra_size_t length) + zebra_size_t length, vrf_id_t vrf_id) { struct stream *s; struct zapi_ipv6 api; unsigned long ifindex; struct prefix_ipv6 p; struct in6_addr *nexthop; + unsigned char plength = 0; s = zclient->ibuf; ifindex = 0; @@ -214,7 +221,8 @@ ospf6_zebra_read_ipv6 (int command, struct zclient *zclient, /* IPv6 prefix. */ memset (&p, 0, sizeof (struct prefix_ipv6)); p.family = AF_INET6; - p.prefixlen = stream_getc (s); + plength = stream_getc (s); + p.prefixlen = MIN(IPV6_MAX_PREFIXLEN, plength); stream_get (&p.prefix, s, PSIZE (p.prefixlen)); /* Nexthop, ifindex, distance, metric. */ @@ -239,6 +247,11 @@ ospf6_zebra_read_ipv6 (int command, struct zclient *zclient, else api.metric = 0; + if (CHECK_FLAG (api.message, ZAPI_MESSAGE_TAG)) + api.tag = stream_getl (s); + else + api.tag = 0; + if (IS_OSPF6_DEBUG_ZEBRA (RECV)) { char prefixstr[128], nexthopstr[128]; @@ -248,14 +261,14 @@ ospf6_zebra_read_ipv6 (int command, struct zclient *zclient, else snprintf (nexthopstr, sizeof (nexthopstr), "::"); - zlog_debug ("Zebra Receive route %s: %s %s nexthop %s ifindex %ld", + zlog_debug ("Zebra Receive route %s: %s %s nexthop %s ifindex %ld tag %u", (command == ZEBRA_IPV6_ROUTE_ADD ? "add" : "delete"), - zebra_route_string(api.type), prefixstr, nexthopstr, ifindex); + zebra_route_string(api.type), prefixstr, nexthopstr, ifindex, api.tag); } if (command == ZEBRA_IPV6_ROUTE_ADD) ospf6_asbr_redistribute_add (api.type, ifindex, (struct prefix *) &p, - api.nexthop_num, nexthop); + api.nexthop_num, nexthop, api.tag); else ospf6_asbr_redistribute_remove (api.type, ifindex, (struct prefix *) &p); @@ -267,7 +280,7 @@ ospf6_zebra_read_ipv6 (int command, struct zclient *zclient, - + DEFUN (show_zebra, show_zebra_cmd, "show zebra", @@ -284,12 +297,13 @@ DEFUN (show_zebra, vty_out (vty, "Zebra Infomation%s", VNL); vty_out (vty, " enable: %d fail: %d%s", zclient->enable, zclient->fail, VNL); - vty_out (vty, " redistribute default: %d%s", zclient->redist_default, + vty_out (vty, " redistribute default: %d%s", + vrf_bitmap_check (zclient->default_information, VRF_DEFAULT), VNL); vty_out (vty, " redistribute:"); for (i = 0; i < ZEBRA_ROUTE_MAX; i++) { - if (zclient->redist[i]) + if (vrf_bitmap_check (zclient->redist[i], VRF_DEFAULT)) vty_out (vty, " %s", zebra_route_string(i)); } vty_out (vty, "%s", VNL); @@ -329,7 +343,7 @@ config_write_ospf6_zebra (struct vty *vty) vty_out (vty, "no router zebra%s", VNL); vty_out (vty, "!%s", VNL); } - else if (! zclient->redist[ZEBRA_ROUTE_OSPF6]) + else if (! vrf_bitmap_check (zclient->redist[ZEBRA_ROUTE_OSPF6], VRF_DEFAULT)) { vty_out (vty, "router zebra%s", VNL); vty_out (vty, " no redistribute ospf6%s", VNL); @@ -354,7 +368,7 @@ ospf6_zebra_route_update (int type, struct ospf6_route *request) char buf[64]; int nhcount; struct in6_addr **nexthops; - unsigned int *ifindexes; + ifindex_t *ifindexes; int i, ret = 0; struct prefix_ipv6 *dest; @@ -436,11 +450,10 @@ ospf6_zebra_route_update (int type, struct ospf6_route *request) { if (IS_OSPF6_DEBUG_ZEBRA (SEND)) { - char ifname[IFNAMSIZ]; + const char *ifname; inet_ntop (AF_INET6, &request->nexthop[i].address, buf, sizeof (buf)); - if (!if_indextoname(request->nexthop[i].ifindex, ifname)) - strlcpy(ifname, "unknown", sizeof(ifname)); + ifname = ifindex2ifname (request->nexthop[i].ifindex); zlog_debug (" nexthop: %s%%%.*s(%d)", buf, IFNAMSIZ, ifname, request->nexthop[i].ifindex); } @@ -448,6 +461,7 @@ ospf6_zebra_route_update (int type, struct ospf6_route *request) ifindexes[i] = request->nexthop[i].ifindex; } + api.vrf_id = VRF_DEFAULT; api.type = ZEBRA_ROUTE_OSPF6; api.flags = 0; api.message = 0; @@ -461,7 +475,12 @@ ospf6_zebra_route_update (int type, struct ospf6_route *request) SET_FLAG (api.message, ZAPI_MESSAGE_METRIC); api.metric = (request->path.metric_type == 2 ? request->path.cost_e2 : request->path.cost); - + if (request->path.tag) + { + SET_FLAG (api.message, ZAPI_MESSAGE_TAG); + api.tag = request->path.tag; + } + dest = (struct prefix_ipv6 *) &request->prefix; if (type == REM) ret = zapi_ipv6_route (ZEBRA_IPV6_ROUTE_DELETE, zclient, dest, &api); @@ -481,7 +500,7 @@ ospf6_zebra_route_update (int type, struct ospf6_route *request) void ospf6_zebra_route_update_add (struct ospf6_route *request) { - if (! zclient->redist[ZEBRA_ROUTE_OSPF6]) + if (! vrf_bitmap_check (zclient->redist[ZEBRA_ROUTE_OSPF6], VRF_DEFAULT)) { ospf6->route_table->hook_add = NULL; ospf6->route_table->hook_remove = NULL; @@ -493,7 +512,7 @@ ospf6_zebra_route_update_add (struct ospf6_route *request) void ospf6_zebra_route_update_remove (struct ospf6_route *request) { - if (! zclient->redist[ZEBRA_ROUTE_OSPF6]) + if (! vrf_bitmap_check (zclient->redist[ZEBRA_ROUTE_OSPF6], VRF_DEFAULT)) { ospf6->route_table->hook_add = NULL; ospf6->route_table->hook_remove = NULL; @@ -510,10 +529,10 @@ DEFUN (redistribute_ospf6, { struct ospf6_route *route; - if (zclient->redist[ZEBRA_ROUTE_OSPF6]) + if (vrf_bitmap_check (zclient->redist[ZEBRA_ROUTE_OSPF6], VRF_DEFAULT)) return CMD_SUCCESS; - zclient->redist[ZEBRA_ROUTE_OSPF6] = 1; + vrf_bitmap_set (zclient->redist[ZEBRA_ROUTE_OSPF6], VRF_DEFAULT); if (ospf6 == NULL) return CMD_SUCCESS; @@ -538,10 +557,10 @@ DEFUN (no_redistribute_ospf6, { struct ospf6_route *route; - if (! zclient->redist[ZEBRA_ROUTE_OSPF6]) + if (! vrf_bitmap_check (zclient->redist[ZEBRA_ROUTE_OSPF6], VRF_DEFAULT)) return CMD_SUCCESS; - zclient->redist[ZEBRA_ROUTE_OSPF6] = 0; + vrf_bitmap_unset (zclient->redist[ZEBRA_ROUTE_OSPF6], VRF_DEFAULT); if (ospf6 == NULL) return CMD_SUCCESS; @@ -557,12 +576,160 @@ DEFUN (no_redistribute_ospf6, return CMD_SUCCESS; } +static void +ospf6_zebra_connected (struct zclient *zclient) +{ + zclient_send_requests (zclient, VRF_DEFAULT); +} + +static struct ospf6_distance * +ospf6_distance_new (void) +{ + return XCALLOC (MTYPE_OSPF6_DISTANCE, sizeof (struct ospf6_distance)); +} + +static void +ospf6_distance_free (struct ospf6_distance *odistance) +{ + XFREE (MTYPE_OSPF6_DISTANCE, odistance); +} + +int +ospf6_distance_set (struct vty *vty, struct ospf6 *o, + const char *distance_str, + const char *ip_str, + const char *access_list_str) +{ + int ret; + struct prefix_ipv6 p; + u_char distance; + struct route_node *rn; + struct ospf6_distance *odistance; + + ret = str2prefix_ipv6 (ip_str, &p); + if (ret == 0) + { + vty_out (vty, "Malformed prefix%s", VTY_NEWLINE); + return CMD_WARNING; + } + + distance = atoi (distance_str); + + /* Get OSPF6 distance node. */ + rn = route_node_get (o->distance_table, (struct prefix *) &p); + if (rn->info) + { + odistance = rn->info; + route_unlock_node (rn); + } + else + { + odistance = ospf6_distance_new (); + rn->info = odistance; + } + + /* Set distance value. */ + odistance->distance = distance; + + /*Reset access-list configuration. */ + if (odistance->access_list) + { + free (odistance->access_list); + odistance->access_list = NULL; + } + if (access_list_str) + odistance->access_list = strdup (access_list_str); + + return CMD_SUCCESS; +} + +int +ospf6_distance_unset (struct vty *vty, struct ospf6 *o, + const char *ip_str, + const char *access_list_str) +{ + int ret; + struct prefix_ipv6 p; + struct route_node *rn; + struct ospf6_distance *odistance; + + ret = str2prefix_ipv6 (ip_str, &p); + if (ret == 0) + { + vty_out (vty, "Malformed prefix%s", VTY_NEWLINE); + return CMD_WARNING; + } + + rn = route_node_lookup (o->distance_table, (struct prefix *) &p); + if (!rn) + { + vty_out (vty, "Cant't find specified prefix%s", VTY_NEWLINE); + return CMD_WARNING; + } + + odistance = rn->info; + + if (odistance->access_list) + free (odistance->access_list); + ospf6_distance_free (odistance); + + rn->info = NULL; + route_unlock_node (rn); + route_unlock_node (rn); + + return CMD_SUCCESS; +} + void -ospf6_zebra_init (void) +ospf6_distance_reset (struct ospf6 *o) +{ + struct route_node *rn; + struct ospf6_distance *odistance; + + for (rn = route_top (o->distance_table); rn; rn = route_next (rn)) + if ((odistance = rn->info) != NULL) + { + if (odistance->access_list) + free (odistance->access_list); + ospf6_distance_free (odistance); + rn->info = NULL; + route_unlock_node (rn); + } +} + +u_char +ospf6_distance_apply (struct ospf6_route *or, struct ospf6 *o) +{ + + if (o == NULL) + return 0; + + if (o->distance_intra) + if (or->path.type == OSPF6_PATH_TYPE_INTRA) + return o->distance_intra; + + if (o->distance_inter) + if (or->path.type == OSPF6_PATH_TYPE_INTER) + return o->distance_inter; + + if (o->distance_external) + if(or->path.type == OSPF6_PATH_TYPE_EXTERNAL1 + || or->path.type == OSPF6_PATH_TYPE_EXTERNAL2) + return o->distance_external; + + if (o->distance_all) + return o->distance_all; + + return 0; +} + +void +ospf6_zebra_init (struct thread_master *master) { /* Allocate zebra structure. */ - zclient = zclient_new (); + zclient = zclient_new (master); zclient_init (zclient, ZEBRA_ROUTE_OSPF6); + zclient->zebra_connected = ospf6_zebra_connected; zclient->router_id_update = ospf6_router_id_update_zebra; zclient->interface_add = ospf6_zebra_if_add; zclient->interface_delete = ospf6_zebra_if_del; @@ -583,7 +750,6 @@ ospf6_zebra_init (void) /* Install command element for zebra node. */ install_element (VIEW_NODE, &show_zebra_cmd); - install_element (ENABLE_NODE, &show_zebra_cmd); install_element (CONFIG_NODE, &router_zebra_cmd); install_element (CONFIG_NODE, &no_router_zebra_cmd); @@ -595,7 +761,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/ospf6_zebra.h b/ospf6d/ospf6_zebra.h index 24a4ae639..51eb9d7d5 100644 --- a/ospf6d/ospf6_zebra.h +++ b/ospf6d/ospf6_zebra.h @@ -23,6 +23,7 @@ #define OSPF6_ZEBRA_H #include "zclient.h" +#include "ospf6_top.h" /* Debug option */ extern unsigned char conf_debug_ospf6_zebra; @@ -35,6 +36,16 @@ extern unsigned char conf_debug_ospf6_zebra; #define IS_OSPF6_DEBUG_ZEBRA(e) \ (conf_debug_ospf6_zebra & OSPF6_DEBUG_ZEBRA_ ## e) +/* OSPF6 distance */ +struct ospf6_distance +{ + /* Distance value for the IP source prefix */ + u_char distance; + + /* Name of the access-list to be matched */ + char *access_list; +}; + extern struct zclient *zclient; extern void ospf6_zebra_route_update_add (struct ospf6_route *request); @@ -42,8 +53,18 @@ extern void ospf6_zebra_route_update_remove (struct ospf6_route *request); extern void ospf6_zebra_redistribute (int); extern void ospf6_zebra_no_redistribute (int); -#define ospf6_zebra_is_redistribute(type) (zclient->redist[type]) -extern void ospf6_zebra_init (void); +#define ospf6_zebra_is_redistribute(type) \ + vrf_bitmap_check (zclient->redist[type], VRF_DEFAULT) + +extern void ospf6_distance_reset (struct ospf6 *); +extern u_char ospf6_distance_apply (struct ospf6_route *, + struct ospf6 *); +extern int ospf6_distance_set (struct vty *, struct ospf6 *, const char *, + const char *, const char *); +extern int ospf6_distance_unset (struct vty *, struct ospf6 *, const char *, + const char *); + +extern void ospf6_zebra_init(struct thread_master *); extern int config_write_ospf6_debug_zebra (struct vty *vty); extern void install_element_ospf6_debug_zebra (void); diff --git a/ospf6d/ospf6d.c b/ospf6d/ospf6d.c index 3fdbda18e..387f6923c 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, @@ -1690,6 +1690,8 @@ DEFUN (show_ipv6_ospf6_linkstate, struct listnode *node; struct ospf6_area *oa; + OSPF6_CMD_CHECK_RUNNING (); + for (ALL_LIST_ELEMENTS_RO (ospf6->area_list, node, oa)) { vty_out (vty, "%s SPF Result in Area %s%s%s", @@ -1738,6 +1740,8 @@ DEFUN (show_ipv6_ospf6_linkstate_detail, struct listnode *node; struct ospf6_area *oa; + OSPF6_CMD_CHECK_RUNNING (); + /* copy argv to sargv and then append "detail" */ for (i = 0; i < argc; i++) sargv[i] = argv[i]; @@ -1764,7 +1768,7 @@ ospf6_init (void) ospf6_area_init (); ospf6_interface_init (); ospf6_neighbor_init (); - ospf6_zebra_init (); + ospf6_zebra_init (master); ospf6_lsa_init (); ospf6_spf_init (); @@ -1790,22 +1794,17 @@ ospf6_init (void) install_element_ospf6_debug_abr (); install_element_ospf6_debug_flood (); + install_element_ospf6_clear_interface (); + install_element (VIEW_NODE, &show_version_ospf6_cmd); - install_element (ENABLE_NODE, &show_version_ospf6_cmd); install_element (VIEW_NODE, &show_ipv6_ospf6_border_routers_cmd); install_element (VIEW_NODE, &show_ipv6_ospf6_border_routers_detail_cmd); - install_element (ENABLE_NODE, &show_ipv6_ospf6_border_routers_cmd); - install_element (ENABLE_NODE, &show_ipv6_ospf6_border_routers_detail_cmd); install_element (VIEW_NODE, &show_ipv6_ospf6_linkstate_cmd); install_element (VIEW_NODE, &show_ipv6_ospf6_linkstate_router_cmd); install_element (VIEW_NODE, &show_ipv6_ospf6_linkstate_network_cmd); install_element (VIEW_NODE, &show_ipv6_ospf6_linkstate_detail_cmd); - install_element (ENABLE_NODE, &show_ipv6_ospf6_linkstate_cmd); - install_element (ENABLE_NODE, &show_ipv6_ospf6_linkstate_router_cmd); - install_element (ENABLE_NODE, &show_ipv6_ospf6_linkstate_network_cmd); - install_element (ENABLE_NODE, &show_ipv6_ospf6_linkstate_detail_cmd); #define INSTALL(n,c) \ install_element (n ## _NODE, &show_ipv6_ospf6_ ## c) @@ -1847,43 +1846,6 @@ ospf6_init (void) INSTALL (VIEW, database_type_self_originated_linkstate_id_cmd); INSTALL (VIEW, database_type_self_originated_linkstate_id_detail_cmd); - INSTALL (ENABLE, database_cmd); - INSTALL (ENABLE, database_detail_cmd); - INSTALL (ENABLE, database_type_cmd); - INSTALL (ENABLE, database_type_detail_cmd); - INSTALL (ENABLE, database_id_cmd); - INSTALL (ENABLE, database_id_detail_cmd); - INSTALL (ENABLE, database_linkstate_id_cmd); - INSTALL (ENABLE, database_linkstate_id_detail_cmd); - INSTALL (ENABLE, database_router_cmd); - INSTALL (ENABLE, database_router_detail_cmd); - INSTALL (ENABLE, database_adv_router_cmd); - INSTALL (ENABLE, database_adv_router_detail_cmd); - INSTALL (ENABLE, database_type_id_cmd); - INSTALL (ENABLE, database_type_id_detail_cmd); - INSTALL (ENABLE, database_type_linkstate_id_cmd); - INSTALL (ENABLE, database_type_linkstate_id_detail_cmd); - INSTALL (ENABLE, database_type_router_cmd); - INSTALL (ENABLE, database_type_router_detail_cmd); - INSTALL (ENABLE, database_type_adv_router_cmd); - INSTALL (ENABLE, database_type_adv_router_detail_cmd); - INSTALL (ENABLE, database_adv_router_linkstate_id_cmd); - INSTALL (ENABLE, database_adv_router_linkstate_id_detail_cmd); - INSTALL (ENABLE, database_id_router_cmd); - INSTALL (ENABLE, database_id_router_detail_cmd); - INSTALL (ENABLE, database_type_id_router_cmd); - INSTALL (ENABLE, database_type_id_router_detail_cmd); - INSTALL (ENABLE, database_type_adv_router_linkstate_id_cmd); - INSTALL (ENABLE, database_type_adv_router_linkstate_id_detail_cmd); - INSTALL (ENABLE, database_self_originated_cmd); - INSTALL (ENABLE, database_self_originated_detail_cmd); - INSTALL (ENABLE, database_type_self_originated_cmd); - INSTALL (ENABLE, database_type_self_originated_detail_cmd); - INSTALL (ENABLE, database_type_id_self_originated_cmd); - INSTALL (ENABLE, database_type_id_self_originated_detail_cmd); - INSTALL (ENABLE, database_type_self_originated_linkstate_id_cmd); - INSTALL (ENABLE, database_type_self_originated_linkstate_id_detail_cmd); - /* Make ospf protocol socket. */ ospf6_serv_sock (); thread_add_read (master, ospf6_receive, NULL, ospf6_sock); @@ -1892,6 +1854,8 @@ ospf6_init (void) void ospf6_clean (void) { + if (!ospf6) + return; if (ospf6->route_table) ospf6_route_remove_all (ospf6->route_table); if (ospf6->brouter_table) diff --git a/ospf6d/ospf6d.h b/ospf6d/ospf6d.h index 2ac6300e5..9e2efb41d 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 @@ -74,19 +71,19 @@ extern struct thread_master *master; } \ } while (0) #endif /*timersub*/ -#define timerstring(tv, buf, size) \ - do { \ - if ((tv)->tv_sec / 60 / 60 / 24) \ - snprintf (buf, size, "%ldd%02ld:%02ld:%02ld", \ - (tv)->tv_sec / 60 / 60 / 24, \ - (tv)->tv_sec / 60 / 60 % 24, \ - (tv)->tv_sec / 60 % 60, \ - (tv)->tv_sec % 60); \ - else \ - snprintf (buf, size, "%02ld:%02ld:%02ld", \ - (tv)->tv_sec / 60 / 60 % 24, \ - (tv)->tv_sec / 60 % 60, \ - (tv)->tv_sec % 60); \ +#define timerstring(tv, buf, size) \ + do { \ + if ((tv)->tv_sec / 60 / 60 / 24) \ + snprintf (buf, size, "%lldd%02lld:%02lld:%02lld", \ + (tv)->tv_sec / 60LL / 60 / 24, \ + (tv)->tv_sec / 60LL / 60 % 24, \ + (tv)->tv_sec / 60LL % 60, \ + (tv)->tv_sec % 60LL); \ + else \ + snprintf (buf, size, "%02lld:%02lld:%02lld", \ + (tv)->tv_sec / 60LL / 60 % 24, \ + (tv)->tv_sec / 60LL % 60, \ + (tv)->tv_sec % 60LL); \ } while (0) #define timerstring_local(tv, buf, size) \ do { \ @@ -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 a43ea2126..b8cf3a1ab 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 b8aae84bb..1fca431a1 100644 --- a/ospfclient/Makefile.am +++ b/ospfclient/Makefile.am @@ -1,9 +1,11 @@ ## Automake.am for OSPF API client -INCLUDES = @INCLUDES@ -I.. -I$(top_srcdir) -I$(top_srcdir)/lib +AM_CPPFLAGS = -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib +AM_CFLAGS = $(WERROR) lib_LTLIBRARIES = libospfapiclient.la -libospfapiclient_la_LDFLAGS = -version 0:0:0 +libospfapiclient_la_LDFLAGS = -version-info 0:0:0 +libospfapiclient_la_LIBADD = ../ospfd/libospf.la ../lib/libzebra.la sbin_PROGRAMS = ospfclient @@ -21,6 +23,5 @@ ospfclient_SOURCES = \ ospfclient_LDADD = libospfapiclient.la \ ../ospfd/libospf.la ../lib/libzebra.la @LIBCAP@ -ospfclient_CFLAGS = $(AM_CFLAGS) $(PICFLAGS) -ospfclient_LDFLAGS = $(AM_LDFLAGS) $(PILDFLAGS) - +ospfclient_CFLAGS = $(AM_CFLAGS) +ospfclient_LDFLAGS = $(AM_LDFLAGS) diff --git a/ospfclient/ospf_apiclient.h b/ospfclient/ospf_apiclient.h index 0e74787ae..809861995 100644 --- a/ospfclient/ospf_apiclient.h +++ b/ospfclient/ospf_apiclient.h @@ -23,7 +23,7 @@ #ifndef _OSPF_APICLIENT_H #define _OSPF_APICLIENT_H -#define MTYPE_OSPF_APICLIENT 0 +#define MTYPE_OSPF_APICLIENT MTYPE_TMP /* Structure for the OSPF API client */ struct ospf_apiclient diff --git a/ospfclient/ospfclient.c b/ospfclient/ospfclient.c index 3608ebaca..1de7644f6 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 2e4d5c8e1..f586d7343 100644 --- a/ospfd/Makefile.am +++ b/ospfd/Makefile.am @@ -1,11 +1,13 @@ ## Process this file with automake to produce Makefile.in. -INCLUDES = @INCLUDES@ -I.. -I$(top_srcdir) -I$(top_srcdir)/lib @SNMP_INCLUDES@ +AM_CPPFLAGS = -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib +AM_CFLAGS = $(WERROR) DEFS = @DEFS@ $(LOCAL_OPTS) -DSYSCONFDIR=\"$(sysconfdir)/\" INSTALL_SDATA=@INSTALL@ -m 600 lib_LTLIBRARIES = libospf.la -libospf_la_LDFLAGS = -version 0:0:0 +libospf_la_LDFLAGS = -version-info 0:0:0 +libospf_la_LIBADD = ../lib/libzebra.la sbin_PROGRAMS = ospfd @@ -14,7 +16,7 @@ libospf_la_SOURCES = \ ospf_nsm.c ospf_dump.c ospf_network.c ospf_packet.c ospf_lsa.c \ ospf_spf.c ospf_route.c ospf_ase.c ospf_abr.c ospf_ia.c ospf_flood.c \ ospf_lsdb.c ospf_asbr.c ospf_routemap.c ospf_snmp.c \ - ospf_opaque.c ospf_te.c ospf_vty.c ospf_api.c ospf_apiserver.c + ospf_opaque.c ospf_te.c ospf_ri.c ospf_vty.c ospf_api.c ospf_apiserver.c ospfdheaderdir = $(pkgincludedir)/ospfd @@ -25,11 +27,11 @@ ospfdheader_HEADERS = \ noinst_HEADERS = \ ospf_interface.h ospf_neighbor.h ospf_network.h ospf_packet.h \ ospf_zebra.h ospf_spf.h ospf_route.h ospf_ase.h ospf_abr.h ospf_ia.h \ - ospf_flood.h ospf_snmp.h ospf_te.h ospf_vty.h ospf_apiserver.h + ospf_flood.h ospf_snmp.h ospf_te.h ospf_ri.h ospf_vty.h ospf_apiserver.h ospfd_SOURCES = ospf_main.c -ospfd_LDADD = libospf.la ../lib/libzebra.la @LIBCAP@ +ospfd_LDADD = libospf.la ../lib/libzebra.la @LIBCAP@ @LIBM@ EXTRA_DIST = OSPF-MIB.txt OSPF-TRAP-MIB.txt ChangeLog.opaque.txt diff --git a/ospfd/ospf_abr.c b/ospfd/ospf_abr.c index b7cc20dd8..95c2d4689 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) { @@ -88,23 +88,18 @@ ospf_area_range_add (struct ospf_area *area, struct ospf_area_range *range) } static void -ospf_area_range_delete (struct ospf_area *area, struct ospf_area_range *range) +ospf_area_range_delete (struct ospf_area *area, struct route_node *rn) { - struct route_node *rn; - struct prefix_ipv4 p; + struct ospf_area_range *range = rn->info; - p.family = AF_INET; - p.prefixlen = range->masklen; - p.prefix = range->addr; + if (range->specifics != 0) + ospf_delete_discard_route (area->ospf->new_table, + (struct prefix_ipv4 *) &rn->p); - rn = route_node_lookup (area->ranges, (struct prefix *)&p); - if (rn) - { - ospf_area_range_free (rn->info); - rn->info = NULL; - route_unlock_node (rn); - route_unlock_node (rn); - } + ospf_area_range_free (range); + rn->info = NULL; + route_unlock_node (rn); + route_unlock_node (rn); } struct ospf_area_range * @@ -263,20 +258,20 @@ ospf_area_range_unset (struct ospf *ospf, struct in_addr area_id, struct prefix_ipv4 *p) { struct ospf_area *area; - struct ospf_area_range *range; + struct route_node *rn; area = ospf_area_lookup_by_area_id (ospf, area_id); if (area == NULL) return 0; - range = ospf_area_range_lookup (area, p); - if (range == NULL) + rn = route_node_lookup (area->ranges, (struct prefix*)p); + if (rn == NULL) return 0; - if (ospf_area_range_active (range)) + if (ospf_area_range_active (rn->info)) ospf_schedule_abr_task (ospf); - ospf_area_range_delete (area, range); + ospf_area_range_delete (area, rn); return 1; } @@ -561,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; @@ -571,12 +566,20 @@ ospf_check_abr_status (struct ospf *ospf) static void ospf_abr_update_aggregate (struct ospf_area_range *range, - struct ospf_route *or) + struct ospf_route *or, struct ospf_area *area) { if (IS_DEBUG_OSPF_EVENT) zlog_debug ("ospf_abr_update_aggregate(): Start"); - if (range->cost_config != OSPF_AREA_RANGE_COST_UNSPEC) + if (CHECK_FLAG (area->stub_router_state, OSPF_AREA_IS_STUB_ROUTED) && + (range->cost != OSPF_STUB_MAX_METRIC_SUMMARY_COST)) + { + range->cost = OSPF_STUB_MAX_METRIC_SUMMARY_COST; + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_abr_update_aggregate(): use summary max-metric 0x%08x", + range->cost); + } + else if (range->cost_config != OSPF_AREA_RANGE_COST_UNSPEC) { if (IS_DEBUG_OSPF_EVENT) zlog_debug ("ospf_abr_update_aggregate(): use configured cost %d", @@ -587,12 +590,18 @@ ospf_abr_update_aggregate (struct ospf_area_range *range, else { if (range->specifics == 0) - range->cost = or->cost; /* 1st time get 1st cost */ + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_abr_update_aggregate(): use or->cost %d", + or->cost); + + range->cost = or->cost; /* 1st time get 1st cost */ + } if (or->cost > range->cost) { if (IS_DEBUG_OSPF_EVENT) - zlog_debug ("ospf_abr_update_aggregate(): largest cost, update"); + zlog_debug ("ospf_abr_update_aggregate(): update to %d", or->cost); range->cost = or->cost; } @@ -692,7 +701,7 @@ ospf_abr_translate_nssa (struct ospf_area *area, struct ospf_lsa *lsa) zlog_debug ("ospf_abr_translate_nssa(): Could not translate " "Type-7 for %s to Type-5", inet_ntoa (lsa->data->id)); - return 1; + return 1; } } @@ -716,10 +725,16 @@ ospf_abr_announce_network_to_area (struct prefix_ipv4 *p, u_int32_t cost, { struct ospf_lsa *lsa, *old = NULL; struct summary_lsa *sl = NULL; + u_int32_t full_cost; if (IS_DEBUG_OSPF_EVENT) zlog_debug ("ospf_abr_announce_network_to_area(): Start"); + if (CHECK_FLAG (area->stub_router_state, OSPF_AREA_IS_STUB_ROUTED)) + full_cost = OSPF_STUB_MAX_METRIC_SUMMARY_COST; + else + full_cost = cost; + old = ospf_lsa_lookup_by_prefix (area->lsdb, OSPF_SUMMARY_LSA, (struct prefix_ipv4 *) p, area->ospf->router_id); @@ -734,8 +749,9 @@ ospf_abr_announce_network_to_area (struct prefix_ipv4 *p, u_int32_t cost, zlog_debug ("ospf_abr_announce_network_to_area(): " "old metric: %d, new metric: %d", GET_METRIC (sl->metric), cost); - - if (GET_METRIC (sl->metric) == cost) + + if ((GET_METRIC (sl->metric) == full_cost) && + ((old->flags & OSPF_LSA_IN_MAXAGE) == 0)) { /* unchanged. simply reapprove it */ if (IS_DEBUG_OSPF_EVENT) @@ -749,7 +765,7 @@ ospf_abr_announce_network_to_area (struct prefix_ipv4 *p, u_int32_t cost, if (IS_DEBUG_OSPF_EVENT) zlog_debug ("ospf_abr_announce_network_to_area(): " "refreshing summary"); - set_metric (old, cost); + set_metric (old, full_cost); lsa = ospf_lsa_refresh (area->ospf, old); if (!lsa) @@ -773,7 +789,7 @@ ospf_abr_announce_network_to_area (struct prefix_ipv4 *p, u_int32_t cost, if (IS_DEBUG_OSPF_EVENT) zlog_debug ("ospf_abr_announce_network_to_area(): " "creating new summary"); - lsa = ospf_summary_lsa_originate ( (struct prefix_ipv4 *)p, cost, area); + lsa = ospf_summary_lsa_originate ( (struct prefix_ipv4 *)p, full_cost, area); /* This will flood through area. */ if (!lsa) @@ -932,11 +948,11 @@ ospf_abr_announce_network (struct ospf *ospf, zlog_debug ("ospf_abr_announce_network(): " "this is intra-area route to %s/%d", inet_ntoa (p->prefix), p->prefixlen); - if ((range = ospf_area_range_match (or_area, p)) - && !ospf_area_is_transit (area)) - ospf_abr_update_aggregate (range, or); - else - ospf_abr_announce_network_to_area (p, or->cost, area); + if ((range = ospf_area_range_match (or_area, p)) + && !ospf_area_is_transit (area)) + ospf_abr_update_aggregate (range, or, area); + else + ospf_abr_announce_network_to_area (p, or->cost, area); } } } @@ -1124,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"); @@ -1695,7 +1712,8 @@ ospf_abr_manage_discard_routes (struct ospf *ospf) ospf_add_discard_route (ospf->new_table, area, (struct prefix_ipv4 *) &rn->p); else - ospf_delete_discard_route ((struct prefix_ipv4 *) &rn->p); + ospf_delete_discard_route (ospf->new_table, + (struct prefix_ipv4 *) &rn->p); } } diff --git a/ospfd/ospf_api.c b/ospfd/ospf_api.c index fc3b51ddf..e92f162eb 100644 --- a/ospfd/ospf_api.c +++ b/ospfd/ospf_api.c @@ -23,9 +23,6 @@ #include #ifdef SUPPORT_OSPF_API -#ifndef HAVE_OPAQUE_LSA -#error "Core Opaque-LSA module must be configured." -#endif /* HAVE_OPAQUE_LSA */ #include "linklist.h" #include "prefix.h" @@ -155,7 +152,7 @@ ospf_api_typename (int msgtype) { MSG_NSM_CHANGE, "NSM change", }, }; - int i, n = sizeof (NameTab) / sizeof (NameTab[0]); + int i, n = array_size(NameTab); const char *name = NULL; for (i = 0; i < n; i++) @@ -187,7 +184,7 @@ ospf_api_errname (int errcode) { OSPF_API_UNDEF, "Undefined", }, }; - int i, n = sizeof (NameTab) / sizeof (NameTab[0]); + int i, n = array_size(NameTab); const char *name = NULL; for (i = 0; i < n; i++) @@ -464,7 +461,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 +469,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 +480,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 +488,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 +501,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 +627,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 2a9003b72..aac8ef4b8 100644 --- a/ospfd/ospf_apiserver.c +++ b/ospfd/ospf_apiserver.c @@ -23,9 +23,6 @@ #include #ifdef SUPPORT_OSPF_API -#ifndef HAVE_OPAQUE_LSA -#error "Core Opaque-LSA module must be configured." -#endif /* HAVE_OPAQUE_LSA */ #include "linklist.h" #include "prefix.h" @@ -105,7 +102,7 @@ ospf_apiserver_if_lookup_by_ifp (struct interface *ifp) struct ospf_interface *oi; struct ospf *ospf; - if (!(ospf = ospf_lookup ())); + if (!(ospf = ospf_lookup ())) return NULL; for (ALL_LIST_ELEMENTS (ospf->oiflist, node, nnode, oi)) @@ -244,7 +241,8 @@ static int ospf_apiserver_new_lsa_hook (struct ospf_lsa *lsa) { if (IS_DEBUG_OSPF_EVENT) - zlog_debug ("API: Put LSA(%p)[%s] into reserve, total=%ld", lsa, dump_lsa_key (lsa), lsa->lsdb->total); + zlog_debug ("API: Put LSA(%p)[%s] into reserve, total=%ld", (void *)lsa, + dump_lsa_key (lsa), lsa->lsdb->total); return 0; } @@ -252,7 +250,8 @@ static int ospf_apiserver_del_lsa_hook (struct ospf_lsa *lsa) { if (IS_DEBUG_OSPF_EVENT) - zlog_debug ("API: Get LSA(%p)[%s] from reserve, total=%ld", lsa, dump_lsa_key (lsa), lsa->lsdb->total); + zlog_debug ("API: Get LSA(%p)[%s] from reserve, total=%ld", (void *)lsa, + dump_lsa_key (lsa), lsa->lsdb->total); return 0; } @@ -299,13 +298,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 = @@ -398,7 +394,8 @@ ospf_apiserver_free (struct ospf_apiserver *apiserv) listnode_delete (apiserver_list, apiserv); if (IS_DEBUG_OSPF_EVENT) - zlog_debug ("API: Delete apiserv(%p), total#(%d)", apiserv, apiserver_list->count); + zlog_debug ("API: Delete apiserv(%p), total#(%d)", + (void *)apiserv, apiserver_list->count); /* And free instance. */ XFREE (MTYPE_OSPF_APISERVER, apiserv); @@ -758,7 +755,8 @@ ospf_apiserver_accept (struct thread *thread) #endif /* USE_ASYNC_READ */ if (IS_DEBUG_OSPF_EVENT) - zlog_debug ("API: New apiserv(%p), total#(%d)", apiserv, apiserver_list->count); + zlog_debug ("API: New apiserv(%p), total#(%d)", + (void *)apiserv, apiserver_list->count); return 0; } @@ -947,7 +945,7 @@ ospf_apiserver_register_opaque_type (struct ospf_apiserver *apiserv, if (IS_DEBUG_OSPF_EVENT) zlog_debug ("API: Add LSA-type(%d)/Opaque-type(%d) into" " apiserv(%p), total#(%d)", - lsa_type, opaque_type, apiserv, + lsa_type, opaque_type, (void *)apiserv, listcount (apiserv->opaque_types)); return 0; @@ -979,7 +977,7 @@ ospf_apiserver_unregister_opaque_type (struct ospf_apiserver *apiserv, if (IS_DEBUG_OSPF_EVENT) zlog_debug ("API: Del LSA-type(%d)/Opaque-type(%d)" " from apiserv(%p), total#(%d)", - lsa_type, opaque_type, apiserv, + lsa_type, opaque_type, (void *)apiserv, listcount (apiserv->opaque_types)); return 0; diff --git a/ospfd/ospf_asbr.c b/ospfd/ospf_asbr.c index a23b4f2be..0a411e434 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) @@ -135,7 +135,8 @@ ospf_route_map_set_compare (struct route_map_set_values *values1, /* Add an External info for AS-external-LSA. */ struct external_info * ospf_external_info_add (u_char type, struct prefix_ipv4 p, - unsigned int ifindex, struct in_addr nexthop) + ifindex_t ifindex, struct in_addr nexthop, + route_tag_t tag) { struct external_info *new; struct route_node *rn; @@ -162,9 +163,10 @@ ospf_external_info_add (u_char type, struct prefix_ipv4 p, new->p = p; new->ifindex = ifindex; new->nexthop = nexthop; - new->tag = 0; + new->tag = tag; - 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 +235,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 +265,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_asbr.h b/ospfd/ospf_asbr.h index 724acf481..0fc330268 100644 --- a/ospfd/ospf_asbr.h +++ b/ospfd/ospf_asbr.h @@ -39,12 +39,12 @@ struct external_info struct prefix_ipv4 p; /* Interface index. */ - unsigned int ifindex; + ifindex_t ifindex; /* Nexthop address. */ struct in_addr nexthop; - /* Additional Route tag. */ + /* Additional Route tag: this is the wire type */ u_int32_t tag; struct route_map_set_values route_map_set; @@ -61,8 +61,9 @@ extern int ospf_route_map_set_compare (struct route_map_set_values *, struct route_map_set_values *); extern struct external_info *ospf_external_info_add (u_char, struct prefix_ipv4, - unsigned int, - struct in_addr); + ifindex_t, + struct in_addr, + route_tag_t); extern void ospf_external_info_delete (u_char, struct prefix_ipv4); extern struct external_info *ospf_external_info_lookup (u_char, struct prefix_ipv4 *); diff --git a/ospfd/ospf_ase.c b/ospfd/ospf_ase.c index 6a72e31dc..fe40b1017 100644 --- a/ospfd/ospf_ase.c +++ b/ospfd/ospf_ase.c @@ -598,6 +598,10 @@ ospf_ase_route_match_same (struct route_table *rt, struct prefix *prefix, if (op->ifindex != newop->ifindex) return 0; } + + if (or->u.ext.tag != newor->u.ext.tag) + return 0; + return 1; } @@ -636,6 +640,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 +649,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 +680,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: %lld\n", + (stop_time.tv_sec - start_time.tv_sec)*1000000LL+ + (stop_time.tv_usec - start_time.tv_usec)); } return 0; } @@ -714,6 +727,8 @@ ospf_ase_register_external_lsa (struct ospf_lsa *lsa, struct ospf *top) rn = route_node_get (top->external_lsas, (struct prefix *) &p); if ((lst = rn->info) == NULL) rn->info = lst = list_new(); + else + route_unlock_node (rn); /* We assume that if LSA is deleted from DB is is also deleted from this RT */ @@ -734,13 +749,13 @@ ospf_ase_unregister_external_lsa (struct ospf_lsa *lsa, struct ospf *top) p.prefixlen = ip_masklen (al->mask); apply_mask_ipv4 (&p); - rn = route_node_get (top->external_lsas, (struct prefix *) &p); - lst = rn->info; + rn = route_node_lookup (top->external_lsas, (struct prefix *) &p); - /* XXX lst can be NULL */ - if (lst) { + if (rn) { + lst = rn->info; listnode_delete (lst, lsa); ospf_lsa_unlock (&lsa); /* external_lsas list */ + route_unlock_node (rn); } } @@ -812,6 +827,7 @@ ospf_ase_incremental_update (struct ospf *ospf, struct ospf_lsa *lsa) { rn2 = route_node_get (tmp_old, (struct prefix *) &p); rn2->info = rn->info; + route_unlock_node (rn); } /* install changes to zebra */ @@ -836,7 +852,6 @@ ospf_ase_incremental_update (struct ospf *ospf, struct ospf_lsa *lsa) { rn->info = NULL; route_unlock_node (rn); - route_unlock_node (rn); } } diff --git a/ospfd/ospf_dump.c b/ospfd/ospf_dump.c index 7e11e2513..be9fa2a75 100644 --- a/ospfd/ospf_dump.c +++ b/ospfd/ospf_dump.c @@ -133,6 +133,7 @@ unsigned long conf_debug_ospf_nsm = 0; unsigned long conf_debug_ospf_lsa = 0; unsigned long conf_debug_ospf_zebra = 0; unsigned long conf_debug_ospf_nssa = 0; +unsigned long conf_debug_ospf_te = 0; /* Enable debug option variables -- valid only session. */ unsigned long term_debug_ospf_packet[5] = {0, 0, 0, 0, 0}; @@ -142,8 +143,8 @@ unsigned long term_debug_ospf_nsm = 0; unsigned long term_debug_ospf_lsa = 0; unsigned long term_debug_ospf_zebra = 0; unsigned long term_debug_ospf_nssa = 0; +unsigned long term_debug_ospf_te = 0; - const char * ospf_redist_string(u_int route_type) @@ -218,7 +219,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 +248,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; @@ -294,12 +300,14 @@ ospf_timeval_dump (struct timeval *t, char *buf, size_t size) else if (d) snprintf (buf, size, "%1ldd%02ldh%02ldm", d, h, m); else if (h) - snprintf (buf, size, "%ldh%02ldm%02lds", h, m, t->tv_sec); + snprintf (buf, size, "%ldh%02ldm%02lds", h, m, (long)t->tv_sec); else if (m) - snprintf (buf, size, "%ldm%02lds", m, t->tv_sec); + snprintf (buf, size, "%ldm%02lds", m, (long)t->tv_sec); + else if (ms) + snprintf (buf, size, "%ld.%03lds", (long)t->tv_sec, ms); else - snprintf (buf, size, "%ld.%03lds", t->tv_sec, ms); - + snprintf (buf, size, "%ld usecs", (long)t->tv_usec); + return buf; } @@ -321,13 +329,14 @@ ospf_options_dump (u_char options) { static char buf[OSPF_OPTION_STR_MAXLEN]; - snprintf (buf, OSPF_OPTION_STR_MAXLEN, "*|%s|%s|%s|%s|%s|%s|*", + snprintf (buf, OSPF_OPTION_STR_MAXLEN, "*|%s|%s|%s|%s|%s|%s|%s", (options & OSPF_OPTION_O) ? "O" : "-", (options & OSPF_OPTION_DC) ? "DC" : "-", (options & OSPF_OPTION_EA) ? "EA" : "-", (options & OSPF_OPTION_NP) ? "N/P" : "-", (options & OSPF_OPTION_MC) ? "MC" : "-", - (options & OSPF_OPTION_E) ? "E" : "-"); + (options & OSPF_OPTION_E) ? "E" : "-", + (options & OSPF_OPTION_MT) ? "M/T" : "-"); return buf; } @@ -484,7 +493,7 @@ ospf_as_external_lsa_dump (struct stream *s, u_int16_t length) IS_EXTERNAL_METRIC (al->e[i].tos) ? "E" : "-", al->e[i].tos & 0x7f, GET_METRIC (al->e[i].metric)); zlog_debug (" Forwarding address %s", inet_ntoa (al->e[i].fwd_addr)); - zlog_debug (" External Route Tag %d", al->e[i].route_tag); + zlog_debug (" External Route Tag %u", al->e[i].route_tag); } } @@ -612,13 +621,11 @@ ospf_packet_ls_upd_dump (struct stream *s, u_int16_t length) case OSPF_AS_NSSA_LSA: ospf_as_external_lsa_dump (s, length); break; -#ifdef HAVE_OPAQUE_LSA case OSPF_OPAQUE_LINK_LSA: case OSPF_OPAQUE_AREA_LSA: case OSPF_OPAQUE_AS_LSA: ospf_opaque_lsa_dump (s, length); break; -#endif /* HAVE_OPAQUE_LSA */ default: break; } @@ -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]] @@ -895,7 +902,7 @@ DEFUN (no_debug_ospf_packet, else if (strncmp (argv[1], "r", 1) == 0) flag = OSPF_DEBUG_RECV | OSPF_DEBUG_DETAIL; else if (strncmp (argv[1], "d", 1) == 0) - flag = OSPF_DEBUG_DETAIL; + flag = OSPF_DEBUG_SEND | OSPF_DEBUG_RECV | OSPF_DEBUG_DETAIL; } /* 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,33 @@ DEFUN (no_debug_ospf_nssa, return CMD_SUCCESS; } - +DEFUN (debug_ospf_te, + debug_ospf_te_cmd, + "debug ospf te", + DEBUG_STR + OSPF_STR + "OSPF-TE information\n") +{ + if (vty->node == CONFIG_NODE) + CONF_DEBUG_ON (te, TE); + TERM_DEBUG_ON (te, TE); + return CMD_SUCCESS; +} + +DEFUN (no_debug_ospf_te, + no_debug_ospf_te_cmd, + "no debug ospf te", + NO_STR + DEBUG_STR + OSPF_STR + "OSPF-TE information\n") +{ + if (vty->node == CONFIG_NODE) + CONF_DEBUG_OFF (te, TE); + TERM_DEBUG_OFF (te, TE); + return CMD_SUCCESS; +} + DEFUN (show_debugging_ospf, show_debugging_ospf_cmd, "show debugging ospf", @@ -1668,6 +1701,7 @@ debug_init () install_element (ENABLE_NODE, &debug_ospf_zebra_cmd); install_element (ENABLE_NODE, &debug_ospf_event_cmd); install_element (ENABLE_NODE, &debug_ospf_nssa_cmd); + install_element (ENABLE_NODE, &debug_ospf_te_cmd); install_element (ENABLE_NODE, &no_debug_ospf_packet_send_recv_detail_cmd); install_element (ENABLE_NODE, &no_debug_ospf_packet_send_recv_cmd); install_element (ENABLE_NODE, &no_debug_ospf_packet_all_cmd); @@ -1681,6 +1715,7 @@ debug_init () install_element (ENABLE_NODE, &no_debug_ospf_zebra_cmd); install_element (ENABLE_NODE, &no_debug_ospf_event_cmd); install_element (ENABLE_NODE, &no_debug_ospf_nssa_cmd); + install_element (ENABLE_NODE, &no_debug_ospf_te_cmd); install_element (CONFIG_NODE, &debug_ospf_packet_send_recv_detail_cmd); install_element (CONFIG_NODE, &debug_ospf_packet_send_recv_cmd); @@ -1695,6 +1730,7 @@ debug_init () install_element (CONFIG_NODE, &debug_ospf_zebra_cmd); install_element (CONFIG_NODE, &debug_ospf_event_cmd); install_element (CONFIG_NODE, &debug_ospf_nssa_cmd); + install_element (CONFIG_NODE, &debug_ospf_te_cmd); install_element (CONFIG_NODE, &no_debug_ospf_packet_send_recv_detail_cmd); install_element (CONFIG_NODE, &no_debug_ospf_packet_send_recv_cmd); install_element (CONFIG_NODE, &no_debug_ospf_packet_all_cmd); @@ -1708,4 +1744,5 @@ debug_init () install_element (CONFIG_NODE, &no_debug_ospf_zebra_cmd); install_element (CONFIG_NODE, &no_debug_ospf_event_cmd); install_element (CONFIG_NODE, &no_debug_ospf_nssa_cmd); + install_element (CONFIG_NODE, &no_debug_ospf_te_cmd); } diff --git a/ospfd/ospf_dump.h b/ospfd/ospf_dump.h index a2d5e8ba1..f843df03a 100644 --- a/ospfd/ospf_dump.h +++ b/ospfd/ospf_dump.h @@ -57,6 +57,7 @@ #define OSPF_DEBUG_EVENT 0x01 #define OSPF_DEBUG_NSSA 0x02 +#define OSPF_DEBUG_TE 0x04 /* Macro for setting debug option. */ #define CONF_DEBUG_PACKET_ON(a, b) conf_debug_ospf_packet[a] |= (b) @@ -98,6 +99,8 @@ #define IS_DEBUG_OSPF_NSSA IS_DEBUG_OSPF(nssa,NSSA) +#define IS_DEBUG_OSPF_TE IS_DEBUG_OSPF(te,TE) + #define IS_CONF_DEBUG_OSPF_PACKET(a, b) \ (conf_debug_ospf_packet[a] & OSPF_DEBUG_ ## b) #define IS_CONF_DEBUG_OSPF(a, b) \ @@ -119,6 +122,7 @@ extern unsigned long term_debug_ospf_nsm; extern unsigned long term_debug_ospf_lsa; extern unsigned long term_debug_ospf_zebra; extern unsigned long term_debug_ospf_nssa; +extern unsigned long term_debug_ospf_te; /* Message Strings. */ extern char *ospf_lsa_type_str[]; diff --git a/ospfd/ospf_flood.c b/ospfd/ospf_flood.c index 2ebae89ac..8de497411 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. */ @@ -138,9 +138,7 @@ ospf_process_self_originated_lsa (struct ospf *ospf, ospf_router_lsa_update_area (area); return; case OSPF_NETWORK_LSA: -#ifdef HAVE_OPAQUE_LSA case OSPF_OPAQUE_LINK_LSA: -#endif /* HAVE_OPAQUE_LSA */ /* We must find the interface the LSA could belong to. If the interface is no more a broadcast type or we are no more the DR, we flush the LSA otherwise -- create the new instance and @@ -160,13 +158,11 @@ ospf_process_self_originated_lsa (struct ospf *ospf, return; } -#ifdef HAVE_OPAQUE_LSA if (new->data->type == OSPF_OPAQUE_LINK_LSA) { ospf_opaque_lsa_refresh (new); return; } -#endif /* HAVE_OPAQUE_LSA */ if (oi->network_lsa_self) oi->network_lsa_self->data->ls_seqnum = new->data->ls_seqnum; @@ -193,14 +189,12 @@ ospf_process_self_originated_lsa (struct ospf *ospf, else ospf_lsa_flush_as (ospf, new); break; -#ifdef HAVE_OPAQUE_LSA case OSPF_OPAQUE_AREA_LSA: ospf_opaque_lsa_refresh (new); break; case OSPF_OPAQUE_AS_LSA: ospf_opaque_lsa_refresh (new); /* Reconsideration may needed. *//* XXX */ break; -#endif /* HAVE_OPAQUE_LSA */ default: break; } @@ -244,7 +238,7 @@ ospf_flood (struct ospf *ospf, struct ospf_neighbor *nbr, zlog_debug ("LSA[Flooding]: start, NBR %s (%s), cur(%p), New-LSA[%s]", inet_ntoa (nbr->router_id), LOOKUP (ospf_nsm_state_msg, nbr->state), - current, + (void *)current, dump_lsa_key (new)); lsa_ack_flag = 0; @@ -266,7 +260,7 @@ ospf_flood (struct ospf *ospf, struct ospf_neighbor *nbr, ; /* Accept this LSA for quick LSDB resynchronization. */ } else if (tv_cmp (tv_sub (recent_relative_time (), current->tv_recv), - int2tv (OSPF_MIN_LS_ARRIVAL)) < 0) + msec2tv (ospf->min_ls_arrival)) < 0) { if (IS_DEBUG_OSPF_EVENT) zlog_debug ("LSA[Flooding]: LSA is received recently."); @@ -281,25 +275,17 @@ ospf_flood (struct ospf *ospf, struct ospf_neighbor *nbr, interface. */ lsa_ack_flag = ospf_flood_through (ospf, nbr, new); -#ifdef HAVE_OPAQUE_LSA /* Remove the current database copy from all neighbors' Link state retransmission lists. AS_EXTERNAL and AS_EXTERNAL_OPAQUE does ^^^^^^^^^^^^^^^^^^^^^^^ not have area ID. All other (even NSSA's) do have area ID. */ -#else /* HAVE_OPAQUE_LSA */ - /* Remove the current database copy from all neighbors' Link state - retransmission lists. Only AS_EXTERNAL does not have area ID. - All other (even NSSA's) do have area ID. */ -#endif /* HAVE_OPAQUE_LSA */ if (current) { switch (current->data->type) { case OSPF_AS_EXTERNAL_LSA: -#ifdef HAVE_OPAQUE_LSA case OSPF_OPAQUE_AS_LSA: -#endif /* HAVE_OPAQUE_LSA */ ospf_ls_retransmit_delete_nbr_as (ospf, current); break; default: @@ -421,7 +407,6 @@ ospf_flood_through_interface (struct ospf_interface *oi, } } -#ifdef HAVE_OPAQUE_LSA if (IS_OPAQUE_LSA (lsa->data->type)) { if (! CHECK_FLAG (onbr->options, OSPF_OPTION_O)) @@ -430,18 +415,7 @@ ospf_flood_through_interface (struct ospf_interface *oi, zlog_debug ("Skip this neighbor: Not Opaque-capable."); continue; } - - if (IS_OPAQUE_LSA_ORIGINATION_BLOCKED (oi->ospf->opaque) - && IS_LSA_SELF (lsa) - && onbr->state == NSM_Full) - { - /* Small attempt to reduce unnecessary retransmission. */ - if (IS_DEBUG_OSPF (lsa, LSA_FLOODING)) - zlog_debug ("Skip this neighbor: Initial flushing done."); - continue; - } } -#endif /* HAVE_OPAQUE_LSA */ /* If the new LSA was received from this neighbor, examine the next neighbor. */ @@ -576,7 +550,6 @@ ospf_flood_through_area (struct ospf_area *area, oi->type == OSPF_IFTYPE_VIRTUALLINK) continue; -#ifdef HAVE_OPAQUE_LSA if ((lsa->data->type == OSPF_OPAQUE_LINK_LSA) && (lsa->oi != oi)) { /* @@ -584,10 +557,10 @@ ospf_flood_through_area (struct ospf_area *area, * for the link on which the LSA has received. */ if (IS_DEBUG_OSPF (lsa, LSA_FLOODING)) - zlog_debug ("Type-9 Opaque-LSA: lsa->oi(%p) != oi(%p)", lsa->oi, oi); + zlog_debug ("Type-9 Opaque-LSA: lsa->oi(%p) != oi(%p)", + (void *)lsa->oi, (void *)oi); continue; } -#endif /* HAVE_OPAQUE_LSA */ if (ospf_flood_through_interface (oi, inbr, lsa)) lsa_ack_flag = 1; @@ -697,16 +670,12 @@ ospf_flood_through (struct ospf *ospf, case OSPF_NETWORK_LSA: case OSPF_SUMMARY_LSA: case OSPF_ASBR_SUMMARY_LSA: -#ifdef HAVE_OPAQUE_LSA case OSPF_OPAQUE_LINK_LSA: /* ospf_flood_through_interface ? */ case OSPF_OPAQUE_AREA_LSA: -#endif /* HAVE_OPAQUE_LSA */ lsa_ack_flag = ospf_flood_through_area (inbr->oi->area, inbr, lsa); break; case OSPF_AS_EXTERNAL_LSA: /* Type-5 */ -#ifdef HAVE_OPAQUE_LSA case OSPF_OPAQUE_AS_LSA: -#endif /* HAVE_OPAQUE_LSA */ lsa_ack_flag = ospf_flood_through_as (ospf, inbr, lsa); break; /* Type-7 Only received within NSSA, then flooded */ @@ -736,9 +705,7 @@ ospf_flood_through (struct ospf *ospf, switch (lsa->data->type) { case OSPF_AS_EXTERNAL_LSA: /* Type-5 */ -#ifdef HAVE_OPAQUE_LSA case OSPF_OPAQUE_AS_LSA: -#endif /* HAVE_OPAQUE_LSA */ lsa_ack_flag = ospf_flood_through_as (ospf, inbr, lsa); break; /* Type-7 Only received within NSSA, then flooded */ @@ -757,7 +724,7 @@ ospf_flood_through (struct ospf *ospf, return (lsa_ack_flag); } - + /* Management functions for neighbor's Link State Request list. */ void @@ -835,7 +802,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 +940,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 +960,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); } @@ -1005,16 +982,12 @@ ospf_lsa_flush (struct ospf *ospf, struct ospf_lsa *lsa) case OSPF_SUMMARY_LSA: case OSPF_ASBR_SUMMARY_LSA: case OSPF_AS_NSSA_LSA: -#ifdef HAVE_OPAQUE_LSA case OSPF_OPAQUE_LINK_LSA: case OSPF_OPAQUE_AREA_LSA: -#endif /* HAVE_OPAQUE_LSA */ ospf_lsa_flush_area (lsa, lsa->area); break; case OSPF_AS_EXTERNAL_LSA: -#ifdef HAVE_OPAQUE_LSA case OSPF_OPAQUE_AS_LSA: -#endif /* HAVE_OPAQUE_LSA */ ospf_lsa_flush_as (ospf, lsa); break; default: diff --git a/ospfd/ospf_ia.c b/ospfd/ospf_ia.c index 6a8c3c631..b2d0faeb7 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 dc0787d56..a46ca6d46 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) { @@ -144,6 +144,29 @@ ospf_if_reset_variables (struct ospf_interface *oi) oi->v_ls_ack = 1; } +void +ospf_if_reset_type (struct interface *ifp, u_char type) +{ + struct route_node *rn; + + for (rn = route_top (IF_OIFS (ifp)); rn; rn = route_next (rn)) + { + struct ospf_interface *oi = rn->info; + u_char orig_ism_state; + + if (!oi) + continue; + + orig_ism_state = oi->state; + OSPF_ISM_EVENT_EXECUTE (oi, ISM_InterfaceDown); + + oi->type = IF_DEF_PARAMS (ifp)->type; + + if (orig_ism_state > ISM_Down) + OSPF_ISM_EVENT_EXECUTE (oi, ISM_InterfaceUp); + } +} + /* lookup oi for specified prefix/ifp */ struct ospf_interface * ospf_if_table_lookup (struct interface *ifp, struct prefix *prefix) @@ -232,8 +255,8 @@ ospf_if_new (struct ospf *ospf, struct interface *ifp, struct prefix *p) /* Set default values. */ ospf_if_reset_variables (oi); - /* Add pseudo neighbor. */ - oi->nbr_self = ospf_nbr_new (oi); + /* Set pseudo neighbor to Null */ + oi->nbr_self = NULL; oi->ls_upd_queue = route_table_init (); oi->t_ls_upd_event = NULL; @@ -241,9 +264,7 @@ ospf_if_new (struct ospf *ospf, struct interface *ifp, struct prefix *p) oi->crypt_seqnum = time (NULL); -#ifdef HAVE_OPAQUE_LSA ospf_opaque_type9_lsa_init (oi); -#endif /* HAVE_OPAQUE_LSA */ oi->ospf = ospf; @@ -295,9 +316,7 @@ ospf_if_cleanup (struct ospf_interface *oi) ospf_ls_upd_queue_empty (oi); /* Reset pseudo neighbor. */ - ospf_nbr_delete (oi->nbr_self); - oi->nbr_self = ospf_nbr_new (oi); - ospf_nbr_add_self (oi); + ospf_nbr_self_reset (oi); } void @@ -307,9 +326,7 @@ ospf_if_free (struct ospf_interface *oi) assert (oi->state == ISM_Down); -#ifdef HAVE_OPAQUE_LSA ospf_opaque_type9_lsa_term (oi); -#endif /* HAVE_OPAQUE_LSA */ /* Free Pseudo Neighbour */ ospf_nbr_delete (oi->nbr_self); @@ -334,7 +351,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. @@ -392,6 +409,21 @@ ospf_if_exists (struct ospf_interface *oic) return NULL; } +/* Lookup OSPF interface by router LSA posistion */ +struct ospf_interface * +ospf_if_lookup_by_lsa_pos (struct ospf_area *area, int lsa_pos) +{ + struct listnode *node; + struct ospf_interface *oi; + + for (ALL_LIST_ELEMENTS_RO (area->oiflist, node, oi)) + { + if (lsa_pos >= oi->lsa_pos_beg && lsa_pos < oi->lsa_pos_end) + return oi; + } + return NULL; +} + struct ospf_interface * ospf_if_lookup_by_local_addr (struct ospf *ospf, struct interface *ifp, struct in_addr address) @@ -474,7 +506,7 @@ ospf_if_lookup_recv_if (struct ospf *ospf, struct in_addr src, return match; } - + void ospf_if_stream_set (struct ospf_interface *oi) { @@ -503,7 +535,7 @@ ospf_if_stream_unset (struct ospf_interface *oi) } } - + static struct ospf_if_params * ospf_new_if_params (void) { @@ -673,9 +705,7 @@ ospf_if_new_hook (struct interface *ifp) SET_IF_PARAM (IF_DEF_PARAMS (ifp), auth_type); IF_DEF_PARAMS (ifp)->auth_type = OSPF_AUTH_NOTSET; -#ifdef HAVE_OPAQUE_LSA rc = ospf_opaque_new_if (ifp); -#endif /* HAVE_OPAQUE_LSA */ return rc; } @@ -684,9 +714,7 @@ ospf_if_delete_hook (struct interface *ifp) { int rc = 0; struct route_node *rn; -#ifdef HAVE_OPAQUE_LSA rc = ospf_opaque_del_if (ifp); -#endif /* HAVE_OPAQUE_LSA */ route_table_finish (IF_OIFS (ifp)); @@ -801,13 +829,16 @@ ospf_if_down (struct ospf_interface *oi) return 0; OSPF_ISM_EVENT_EXECUTE (oi, ISM_InterfaceDown); + /* delete position in router LSA */ + oi->lsa_pos_beg = 0; + oi->lsa_pos_end = 0; /* Shutdown packet reception and sending */ ospf_if_stream_unset (oi); return 1; } - + /* Virtual Link related functions. */ struct ospf_vl_data * @@ -858,6 +889,9 @@ ospf_vl_new (struct ospf *ospf, struct ospf_vl_data *vl_data) snprintf (ifname, sizeof(ifname), "VLINK%d", vlink_count); vi = if_create (ifname, strnlen(ifname, sizeof(ifname))); + /* Ensure that linkdetection is not enabled on the stub interfaces + * created for OSPF virtual links. */ + UNSET_FLAG(vi->status, ZEBRA_INTERFACE_LINKDETECTION); co = connected_new (); co->ifp = vi; listnode_add (vi->connected, co); @@ -894,7 +928,9 @@ ospf_vl_new (struct ospf *ospf, struct ospf_vl_data *vl_data) if (IS_DEBUG_OSPF_EVENT) zlog_debug ("ospf_vl_new(): set associated area to the backbone"); - ospf_nbr_add_self (voi); + /* Add pseudo neighbor. */ + ospf_nbr_self_reset (voi); + ospf_area_add_if (voi->area, voi); ospf_if_stream_set (voi); @@ -995,7 +1031,7 @@ ospf_vl_set_params (struct ospf_vl_data *vl_data, struct vertex *v) struct ospf_interface *voi; struct listnode *node; struct vertex_parent *vp = NULL; - int i; + unsigned int i; struct router_lsa *rl; voi = vl_data->vl_oi; @@ -1176,7 +1212,7 @@ ospf_vls_in_area (struct ospf_area *area) return c; } - + struct crypt_key * ospf_crypt_key_new () { @@ -1236,7 +1272,6 @@ void ospf_if_init () { /* Initialize Zebra interface data structure. */ - if_init (); om->iflist = iflist; if_add_hook (IF_NEW_HOOK, ospf_if_new_hook); if_add_hook (IF_DELETE_HOOK, ospf_if_delete_hook); diff --git a/ospfd/ospf_interface.h b/ospfd/ospf_interface.h index 6db887738..707035378 100644 --- a/ospfd/ospf_interface.h +++ b/ospfd/ospf_interface.h @@ -30,8 +30,17 @@ #define IF_DEF_PARAMS(I) (IF_OSPF_IF_INFO (I)->def_params) #define IF_OIFS(I) (IF_OSPF_IF_INFO (I)->oifs) #define IF_OIFS_PARAMS(I) (IF_OSPF_IF_INFO (I)->params) - + +/* Despite the name, this macro probably is for specialist use only */ #define OSPF_IF_PARAM_CONFIGURED(S, P) ((S) && (S)->P##__config) + +/* Test whether an OSPF interface parameter is set, generally, given some + * existing ospf interface + */ +#define OSPF_IF_PARAM_IS_SET(O,P) \ + (OSPF_IF_PARAM_CONFIGURED ((O)->params, P) || \ + OSPF_IF_PARAM_CONFIGURED(IF_DEF_PARAMS((O)->ifp)->P)) + #define OSPF_IF_PARAM(O, P) \ (OSPF_IF_PARAM_CONFIGURED ((O)->params, P)?\ (O)->params->P:IF_DEF_PARAMS((O)->ifp)->P) @@ -47,6 +56,7 @@ struct ospf_if_params DECLARE_IF_PARAM (u_int32_t, retransmit_interval); /* Retransmission Interval */ DECLARE_IF_PARAM (u_char, passive_interface); /* OSPF Interface is passive: no sending or receiving (no need to join multicast groups) */ DECLARE_IF_PARAM (u_char, priority); /* OSPF Interface priority */ + DECLARE_IF_PARAM (struct in_addr, if_area); /* Enable OSPF on this interface with area if_area */ DECLARE_IF_PARAM (u_char, type); /* type of interface */ #define OSPF_IF_ACTIVE 0 #define OSPF_IF_PASSIVE 1 @@ -127,6 +137,10 @@ struct ospf_interface /* OSPF Area. */ struct ospf_area *area; + /* Position range in Router LSA */ + uint16_t lsa_pos_beg; /* inclusive, >= */ + uint16_t lsa_pos_end; /* exclusive, < */ + /* Interface data from zebra. */ struct interface *ifp; struct ospf_vl_data *vl_data; /* Data for Virtual Link */ @@ -136,14 +150,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; @@ -187,9 +193,7 @@ struct ospf_interface /* self-originated LSAs. */ struct ospf_lsa *network_lsa_self; /* network-LSA. */ -#ifdef HAVE_OPAQUE_LSA struct list *opaque_lsa_self; /* Type-9 Opaque-LSAs */ -#endif /* HAVE_OPAQUE_LSA */ struct route_table *ls_upd_queue; @@ -210,9 +214,7 @@ struct ospf_interface struct thread *t_ls_ack; /* timer */ struct thread *t_ls_ack_direct; /* event */ struct thread *t_ls_upd_event; /* event */ -#ifdef HAVE_OPAQUE_LSA struct thread *t_opaque_lsa_self; /* Type-9 Opaque-LSAs */ -#endif /* HAVE_OPAQUE_LSA */ int on_write_q; @@ -244,6 +246,8 @@ extern int ospf_if_down (struct ospf_interface *); extern int ospf_if_is_up (struct ospf_interface *); extern struct ospf_interface *ospf_if_exists (struct ospf_interface *); +extern struct ospf_interface *ospf_if_lookup_by_lsa_pos (struct ospf_area *, + int); extern struct ospf_interface *ospf_if_lookup_by_local_addr (struct ospf *, struct interface *, @@ -272,6 +276,7 @@ extern void ospf_if_init (void); extern void ospf_if_stream_set (struct ospf_interface *); extern void ospf_if_stream_unset (struct ospf_interface *); extern void ospf_if_reset_variables (struct ospf_interface *); +extern void ospf_if_reset_type (struct interface *, u_char type); extern int ospf_if_is_enable (struct ospf_interface *); extern int ospf_if_get_output_cost (struct ospf_interface *); extern void ospf_if_recalculate_output_cost (struct interface *); diff --git a/ospfd/ospf_ism.c b/ospfd/ospf_ism.c index db53882d0..ae6d0cdbc 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) { @@ -594,9 +593,7 @@ ism_change_state (struct ospf_interface *oi, int state) oi->network_lsa_self = NULL; } -#ifdef HAVE_OPAQUE_LSA ospf_opaque_ism_change (oi, old_state); -#endif /* HAVE_OPAQUE_LSA */ /* Check area border status. */ ospf_check_abr_status (oi->ospf); diff --git a/ospfd/ospf_lsa.c b/ospfd/ospf_lsa.c index d5959eb11..f49e263c8 100644 --- a/ospfd/ospf_lsa.c +++ b/ospfd/ospf_lsa.c @@ -49,8 +49,9 @@ #include "ospfd/ospf_route.h" #include "ospfd/ospf_ase.h" #include "ospfd/ospf_zebra.h" +#include "ospfd/ospf_abr.h" + - u_int32_t get_metric (u_char *metric) { @@ -61,7 +62,7 @@ get_metric (u_char *metric) return m; } - + struct timeval tv_adjust (struct timeval a) { @@ -107,6 +108,17 @@ int2tv (int a) return ret; } +struct timeval +msec2tv (int a) +{ + struct timeval ret; + + ret.tv_sec = 0; + ret.tv_usec = a * 1000; + + return tv_adjust (ret); +} + struct timeval tv_add (struct timeval a, struct timeval b) { @@ -145,9 +157,9 @@ ospf_lsa_refresh_delay (struct ospf_lsa *lsa) quagga_gettime (QUAGGA_CLK_MONOTONIC, &now); delta = tv_sub (now, lsa->tv_orig); - if (tv_cmp (delta, int2tv (OSPF_MIN_LS_INTERVAL)) < 0) + if (tv_cmp (delta, msec2tv (OSPF_MIN_LS_INTERVAL)) < 0) { - delay = tv_ceil (tv_sub (int2tv (OSPF_MIN_LS_INTERVAL), delta)); + delay = tv_ceil (tv_sub (msec2tv (OSPF_MIN_LS_INTERVAL), delta)); if (IS_DEBUG_OSPF (lsa, LSA_GENERATE)) zlog_debug ("LSA[Type%d:%s]: Refresh timer delay %d seconds", @@ -159,7 +171,7 @@ ospf_lsa_refresh_delay (struct ospf_lsa *lsa) return delay; } - + int get_age (struct ospf_lsa *lsa) { @@ -171,7 +183,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 @@ -192,8 +204,20 @@ ospf_lsa_checksum (struct lsa_header *lsa) return fletcher_checksum(buffer, len, checksum_offset); } +int +ospf_lsa_checksum_valid (struct lsa_header *lsa) +{ + u_char *buffer = (u_char *) &lsa->options; + int options_offset = buffer - (u_char *) &lsa->ls_age; /* should be 2 */ + + /* Skip the AGE field */ + u_int16_t len = ntohs(lsa->length) - options_offset; + + return(fletcher_checksum(buffer, len, FLETCHER_CHECKSUM_VALIDATE) == 0); +} + + - /* Create OSPF LSA. */ struct ospf_lsa * ospf_lsa_new () @@ -236,7 +260,7 @@ ospf_lsa_dup (struct ospf_lsa *lsa) new->refresh_list = -1; if (IS_DEBUG_OSPF (lsa, LSA)) - zlog_debug ("LSA: duplicated %p (new: %p)", lsa, new); + zlog_debug ("LSA: duplicated %p (new: %p)", (void *)lsa, (void *)new); return new; } @@ -248,7 +272,7 @@ ospf_lsa_free (struct ospf_lsa *lsa) assert (lsa->lock == 0); if (IS_DEBUG_OSPF (lsa, LSA)) - zlog_debug ("LSA: freed %p", lsa); + zlog_debug ("LSA: freed %p", (void *)lsa); /* Delete LSA data. */ if (lsa->data != NULL) @@ -324,12 +348,12 @@ ospf_lsa_data_free (struct lsa_header *lsah) { if (IS_DEBUG_OSPF (lsa, LSA)) zlog_debug ("LSA[Type%d:%s]: data freed %p", - lsah->type, inet_ntoa (lsah->id), lsah); + lsah->type, inet_ntoa (lsah->id), (void *)lsah); XFREE (MTYPE_OSPF_LSA_DATA, lsah); } - + /* LSA general functions. */ const char * @@ -381,7 +405,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. */ @@ -556,6 +580,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, @@ -568,12 +595,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, @@ -592,7 +625,7 @@ lsa_link_loopback_set (struct stream *s, struct ospf_interface *oi) mask.s_addr = 0xffffffff; id.s_addr = oi->address->u.prefix4.s_addr; - return link_info_set (s, id, mask, LSA_LINK_TYPE_STUB, 0, oi->output_cost); + return link_info_set (s, id, mask, LSA_LINK_TYPE_STUB, 0, 0); } /* Describe Virtual Link. */ @@ -670,6 +703,7 @@ router_lsa_link_set (struct stream *s, struct ospf_area *area) { if (oi->state != ISM_Down) { + oi->lsa_pos_beg = links; /* Describe each link. */ switch (oi->type) { @@ -691,6 +725,7 @@ router_lsa_link_set (struct stream *s, struct ospf_area *area) case OSPF_IFTYPE_LOOPBACK: links += lsa_link_loopback_set (s, oi); } + oi->lsa_pos_end = links; } } } @@ -723,7 +758,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) { @@ -780,7 +815,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) @@ -865,7 +900,7 @@ ospf_router_lsa_originate (struct ospf_area *area) if (IS_DEBUG_OSPF (lsa, LSA_GENERATE)) { zlog_debug ("LSA[Type%d:%s]: Originate router-LSA %p", - new->data->type, inet_ntoa (new->data->id), new); + new->data->type, inet_ntoa (new->data->id), (void *)new); ospf_lsa_header_dump (new->data); } @@ -982,7 +1017,7 @@ ospf_router_lsa_update (struct ospf *ospf) return 0; } - + /* network-LSA related functions. */ /* Originate Network-LSA. */ static void @@ -1100,7 +1135,7 @@ ospf_network_lsa_update (struct ospf_interface *oi) if (IS_DEBUG_OSPF (lsa, LSA_GENERATE)) { zlog_debug ("LSA[Type%d:%s]: Originate network-LSA %p", - new->data->type, inet_ntoa (new->data->id), new); + new->data->type, inet_ntoa (new->data->id), (void *)new); ospf_lsa_header_dump (new->data); } @@ -1161,7 +1196,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) { @@ -1277,7 +1312,7 @@ ospf_summary_lsa_originate (struct prefix_ipv4 *p, u_int32_t metric, if (IS_DEBUG_OSPF (lsa, LSA_GENERATE)) { zlog_debug ("LSA[Type%d:%s]: Originate summary-LSA %p", - new->data->type, inet_ntoa (new->data->id), new); + new->data->type, inet_ntoa (new->data->id), (void *)new); ospf_lsa_header_dump (new->data); } @@ -1320,18 +1355,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); @@ -1424,7 +1455,7 @@ ospf_summary_asbr_lsa_originate (struct prefix_ipv4 *p, u_int32_t metric, if (IS_DEBUG_OSPF (lsa, LSA_GENERATE)) { zlog_debug ("LSA[Type%d:%s]: Originate summary-ASBR-LSA %p", - new->data->type, inet_ntoa (new->data->id), new); + new->data->type, inet_ntoa (new->data->id), (void *)new); ospf_lsa_header_dump (new->data); } @@ -1619,8 +1650,8 @@ ospf_external_lsa_body_set (struct stream *s, struct external_info *ei, /* Put forwarding address. */ stream_put_ipv4 (s, fwd_addr.s_addr); - /* Put route tag -- This value should be introduced from configuration. */ - stream_putl (s, 0); + /* Put route tag */ + stream_putl (s, ei->tag); } /* Create new external-LSA. */ @@ -1852,7 +1883,7 @@ ospf_translated_nssa_originate (struct ospf *ospf, struct ospf_lsa *type7) if ( (new = ospf_lsa_install (ospf, NULL, new)) == NULL) { - if (IS_DEBUG_OSPF_NSSA); + if (IS_DEBUG_OSPF_NSSA) zlog_debug ("ospf_lsa_translated_nssa_originate(): " "Could not install LSA " "id %s", inet_ntoa (type7->data->id)); @@ -2056,7 +2087,7 @@ ospf_external_lsa_originate (struct ospf *ospf, struct external_info *ei) if (IS_DEBUG_OSPF (lsa, LSA_GENERATE)) { zlog_debug ("LSA[Type%d:%s]: Originate AS-external-LSA %p", - new->data->type, inet_ntoa (new->data->id), new); + new->data->type, inet_ntoa (new->data->id), (void *)new); ospf_lsa_header_dump (new->data); } @@ -2133,7 +2164,7 @@ ospf_default_originate_timer (struct thread *thread) /* If there is no default route via redistribute, then originate AS-external-LSA with nexthop 0 (self). */ nexthop.s_addr = 0; - ospf_external_info_add (DEFAULT_ROUTE, p, 0, nexthop); + ospf_external_info_add (DEFAULT_ROUTE, p, 0, nexthop, 0); } if ((ei = ospf_default_external_info (ospf))) @@ -2176,7 +2207,7 @@ ospf_nssa_lsa_flush (struct ospf *ospf, struct prefix_ipv4 *p) void ospf_external_lsa_flush (struct ospf *ospf, u_char type, struct prefix_ipv4 *p, - unsigned int ifindex /*, struct in_addr nexthop */) + ifindex_t ifindex /*, struct in_addr nexthop */) { struct ospf_lsa *lsa; @@ -2241,7 +2272,8 @@ ospf_external_lsa_refresh_default (struct ospf *ospf) if (lsa) { if (IS_DEBUG_OSPF_EVENT) - zlog_debug ("LSA[Type5:0.0.0.0]: Refresh AS-external-LSA %p", lsa); + zlog_debug ("LSA[Type5:0.0.0.0]: Refresh AS-external-LSA %p", + (void *)lsa); ospf_external_lsa_refresh (ospf, lsa, ei, LSA_REFRESH_FORCE); } else @@ -2358,7 +2390,7 @@ ospf_external_lsa_refresh (struct ospf *ospf, struct ospf_lsa *lsa, return new; } - + /* LSA installation functions. */ /* Install router-LSA to an area. */ @@ -2390,8 +2422,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; } @@ -2425,7 +2456,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; } @@ -2448,11 +2479,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)) @@ -2481,7 +2510,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 */ } @@ -2523,6 +2552,7 @@ ospf_external_lsa_install (struct ospf *ospf, struct ospf_lsa *new, * New translations will be taken care of by the abr_task. */ ospf_translated_nssa_refresh (ospf, new, NULL); + ospf_schedule_abr_task(ospf); } } @@ -2566,11 +2596,9 @@ ospf_discard_from_db (struct ospf *ospf, ospf_ase_unregister_external_lsa (old, ospf); ospf_ls_retransmit_delete_nbr_as (ospf, old); break; -#ifdef HAVE_OPAQUE_LSA case OSPF_OPAQUE_AS_LSA: ospf_ls_retransmit_delete_nbr_as (ospf, old); break; -#endif /* HAVE_OPAQUE_LSA */ case OSPF_AS_NSSA_LSA: ospf_ls_retransmit_delete_nbr_area (old->area, old); ospf_ase_unregister_external_lsa (old, ospf); @@ -2604,9 +2632,7 @@ ospf_lsa_install (struct ospf *ospf, struct ospf_interface *oi, lsdb = ospf->lsdb; break; case OSPF_AS_EXTERNAL_LSA: -#ifdef HAVE_OPAQUE_LSA case OSPF_OPAQUE_AS_LSA: -#endif /* HAVE_OPAQUE_LSA */ lsdb = ospf->lsdb; break; default: @@ -2671,7 +2697,7 @@ ospf_lsa_install (struct ospf *ospf, struct ospf_interface *oi, { zlog_debug ("ospf_lsa_install() Premature Aging " "lsa 0x%p, seqnum 0x%x", - lsa, ntohl(lsa->data->ls_seqnum)); + (void *)lsa, ntohl(lsa->data->ls_seqnum)); ospf_lsa_header_dump (lsa->data); } } @@ -2718,18 +2744,18 @@ ospf_lsa_install (struct ospf *ospf, struct ospf_interface *oi, case OSPF_AS_EXTERNAL_LSA: new = ospf_external_lsa_install (ospf, lsa, rt_recalc); break; -#ifdef HAVE_OPAQUE_LSA case OSPF_OPAQUE_LINK_LSA: 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: new = ospf_opaque_lsa_install (lsa, rt_recalc); break; -#endif /* HAVE_OPAQUE_LSA */ case OSPF_AS_NSSA_LSA: new = ospf_external_lsa_install (ospf, lsa, rt_recalc); default: /* type-6,8,9....nothing special */ @@ -2747,9 +2773,7 @@ ospf_lsa_install (struct ospf *ospf, struct ospf_interface *oi, switch (lsa->data->type) { case OSPF_AS_EXTERNAL_LSA: -#ifdef HAVE_OPAQUE_LSA case OSPF_OPAQUE_AS_LSA: -#endif /* HAVE_OPAQUE_LSA */ case OSPF_AS_NSSA_LSA: zlog_debug ("LSA[%s]: Install %s", dump_lsa_key (new), @@ -2768,22 +2792,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); + new->data->type, + inet_ntoa (new->data->id), + (void *)lsa); + ospf_lsa_maxage (ospf, lsa); } return new; } - -static int + +int ospf_check_nbr_status (struct ospf *ospf) { struct listnode *node, *nnode; @@ -2807,14 +2830,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; @@ -2825,8 +2848,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; @@ -2835,7 +2866,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)) @@ -2850,7 +2885,7 @@ ospf_maxage_lsa_remover (struct thread *thread) if (CHECK_FLAG (lsa->flags, OSPF_LSA_PREMATURE_AGE)) { if (IS_DEBUG_OSPF (lsa, LSA_FLOODING)) - zlog_debug ("originating new lsa for lsa 0x%p\n", lsa); + zlog_debug ("originating new lsa for lsa 0x%p\n", (void *)lsa); ospf_lsa_refresh (ospf, lsa); } @@ -2879,13 +2914,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 */ } } @@ -2897,18 +2943,45 @@ 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)) { if (IS_DEBUG_OSPF (lsa, LSA_FLOODING)) zlog_debug ("LSA[Type%d:%s]: %p already exists on MaxAge LSA list", - lsa->data->type, inet_ntoa (lsa->data->id), lsa); + lsa->data->type, inet_ntoa (lsa->data->id), (void *)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, (void *)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)); @@ -2934,7 +3007,6 @@ ospf_lsa_maxage_walker_remover (struct ospf *ospf, struct ospf_lsa *lsa) switch (lsa->data->type) { -#ifdef HAVE_OPAQUE_LSA case OSPF_OPAQUE_LINK_LSA: case OSPF_OPAQUE_AREA_LSA: case OSPF_OPAQUE_AS_LSA: @@ -2947,13 +3019,12 @@ ospf_lsa_maxage_walker_remover (struct ospf *ospf, struct ospf_lsa *lsa) * topology, and thus, routing recalculation is not needed here. */ break; -#endif /* HAVE_OPAQUE_LSA */ case OSPF_AS_EXTERNAL_LSA: case OSPF_AS_NSSA_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); @@ -2988,12 +3059,10 @@ ospf_lsa_maxage_walker (struct thread *thread) ospf_lsa_maxage_walker_remover (ospf, lsa); LSDB_LOOP (ASBR_SUMMARY_LSDB (area), rn, lsa) ospf_lsa_maxage_walker_remover (ospf, lsa); -#ifdef HAVE_OPAQUE_LSA LSDB_LOOP (OPAQUE_AREA_LSDB (area), rn, lsa) ospf_lsa_maxage_walker_remover (ospf, lsa); LSDB_LOOP (OPAQUE_LINK_LSDB (area), rn, lsa) ospf_lsa_maxage_walker_remover (ospf, lsa); -#endif /* HAVE_OPAQUE_LSA */ LSDB_LOOP (NSSA_LSDB (area), rn, lsa) ospf_lsa_maxage_walker_remover (ospf, lsa); } @@ -3003,10 +3072,8 @@ ospf_lsa_maxage_walker (struct thread *thread) { LSDB_LOOP (EXTERNAL_LSDB (ospf), rn, lsa) ospf_lsa_maxage_walker_remover (ospf, lsa); -#ifdef HAVE_OPAQUE_LSA LSDB_LOOP (OPAQUE_AS_LSDB (ospf), rn, lsa) ospf_lsa_maxage_walker_remover (ospf, lsa); -#endif /* HAVE_OPAQUE_LSA */ } OSPF_TIMER_ON (ospf->t_maxage_walker, ospf_lsa_maxage_walker, @@ -3059,15 +3126,11 @@ ospf_lsa_lookup (struct ospf_area *area, u_int32_t type, case OSPF_SUMMARY_LSA: case OSPF_ASBR_SUMMARY_LSA: case OSPF_AS_NSSA_LSA: -#ifdef HAVE_OPAQUE_LSA case OSPF_OPAQUE_LINK_LSA: case OSPF_OPAQUE_AREA_LSA: -#endif /* HAVE_OPAQUE_LSA */ return ospf_lsdb_lookup_by_id (area->lsdb, type, id, adv_router); case OSPF_AS_EXTERNAL_LSA: -#ifdef HAVE_OPAQUE_LSA case OSPF_OPAQUE_AS_LSA: -#endif /* HAVE_OPAQUE_LSA */ return ospf_lsdb_lookup_by_id (ospf->lsdb, type, id, adv_router); default: break; @@ -3103,13 +3166,11 @@ ospf_lsa_lookup_by_id (struct ospf_area *area, u_int32_t type, return ospf_lsdb_lookup_by_id (area->lsdb, type, id, id); case OSPF_AS_EXTERNAL_LSA: case OSPF_AS_NSSA_LSA: -#ifdef HAVE_OPAQUE_LSA case OSPF_OPAQUE_LINK_LSA: case OSPF_OPAQUE_AREA_LSA: case OSPF_OPAQUE_AS_LSA: /* Currently not used. */ break; -#endif /* HAVE_OPAQUE_LSA */ default: break; } @@ -3122,14 +3183,12 @@ ospf_lsa_lookup_by_header (struct ospf_area *area, struct lsa_header *lsah) { struct ospf_lsa *match; -#ifdef HAVE_OPAQUE_LSA /* * Strictly speaking, the LSA-ID field for Opaque-LSAs (type-9/10/11) * is redefined to have two subfields; opaque-type and opaque-id. * However, it is harmless to treat the two sub fields together, as if * they two were forming a unique LSA-ID. */ -#endif /* HAVE_OPAQUE_LSA */ match = ospf_lsa_lookup (area, lsah->type, lsah->id, lsah->adv_router); @@ -3280,14 +3339,12 @@ ospf_lsa_flush_schedule (struct ospf *ospf, struct ospf_lsa *lsa) switch (lsa->data->type) { -#ifdef HAVE_OPAQUE_LSA /* Opaque wants to be notified of flushes */ case OSPF_OPAQUE_LINK_LSA: case OSPF_OPAQUE_AREA_LSA: case OSPF_OPAQUE_AS_LSA: ospf_opaque_lsa_refresh (lsa); break; -#endif /* HAVE_OPAQUE_LSA */ default: ospf_refresher_unregister_lsa (ospf, lsa); ospf_lsa_flush (ospf, lsa); @@ -3347,22 +3404,18 @@ ospf_flush_self_originated_lsas_now (struct ospf *ospf) ospf_lsa_flush_schedule (ospf, lsa); LSDB_LOOP (ASBR_SUMMARY_LSDB (area), rn, lsa) ospf_lsa_flush_schedule (ospf, lsa); -#ifdef HAVE_OPAQUE_LSA LSDB_LOOP (OPAQUE_LINK_LSDB (area), rn, lsa) ospf_lsa_flush_schedule (ospf, lsa); LSDB_LOOP (OPAQUE_AREA_LSDB (area), rn, lsa) ospf_lsa_flush_schedule (ospf, lsa); -#endif /* HAVE_OPAQUE_LSA */ } if (need_to_flush_ase) { LSDB_LOOP (EXTERNAL_LSDB (ospf), rn, lsa) ospf_lsa_flush_schedule (ospf, lsa); -#ifdef HAVE_OPAQUE_LSA LSDB_LOOP (OPAQUE_AS_LSDB (ospf), rn, lsa) ospf_lsa_flush_schedule (ospf, lsa); -#endif /* HAVE_OPAQUE_LSA */ } /* @@ -3470,7 +3523,7 @@ ospf_lsa_unique_id (struct ospf *ospf, return id; } - + #define LSA_ACTION_FLOOD_AREA 1 #define LSA_ACTION_FLUSH_AREA 2 @@ -3533,7 +3586,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) @@ -3571,13 +3624,11 @@ ospf_lsa_refresh (struct ospf *ospf, struct ospf_lsa *lsa) else ospf_lsa_flush_as (ospf, lsa); break; -#ifdef HAVE_OPAQUE_LSA case OSPF_OPAQUE_LINK_LSA: case OSPF_OPAQUE_AREA_LSA: case OSPF_OPAQUE_AS_LSA: new = ospf_opaque_lsa_refresh (lsa); break; -#endif /* HAVE_OPAQUE_LSA */ default: break; } @@ -3625,7 +3676,7 @@ ospf_refresher_register_lsa (struct ospf *ospf, struct ospf_lsa *lsa) if (IS_DEBUG_OSPF (lsa, LSA_REFRESH)) zlog_debug ("LSA[Refresh:%s]: ospf_refresher_register_lsa(): " "setting refresh_list on lsa %p (slod %d)", - inet_ntoa (lsa->data->id), lsa, index); + inet_ntoa (lsa->data->id), (void *)lsa, index); } } @@ -3696,9 +3747,9 @@ ospf_lsa_refresh_walker (struct thread *t) { if (IS_DEBUG_OSPF (lsa, LSA_REFRESH)) zlog_debug ("LSA[Refresh:%s]: ospf_lsa_refresh_walker(): " - "refresh lsa %p (slot %d)", - inet_ntoa (lsa->data->id), lsa, i); - + "refresh lsa %p (slot %d)", + inet_ntoa (lsa->data->id), (void *)lsa, i); + assert (lsa->lock > 0); list_delete_node (refresh_list, node); lsa->refresh_list = -1; diff --git a/ospfd/ospf_lsa.h b/ospfd/ospf_lsa.h index 297cd9843..3c87962f2 100644 --- a/ospfd/ospf_lsa.h +++ b/ospfd/ospf_lsa.h @@ -27,11 +27,7 @@ /* OSPF LSA Range definition. */ #define OSPF_MIN_LSA 1 /* begin range here */ -#if defined (HAVE_OPAQUE_LSA) #define OSPF_MAX_LSA 12 -#else -#define OSPF_MAX_LSA 8 -#endif /* OSPF LSA Type definition. */ #define OSPF_UNKNOWN_LSA 0 @@ -153,7 +149,14 @@ struct router_lsa_link }; /* OSPF Router-LSAs structure. */ -#define OSPF_ROUTER_LSA_MIN_SIZE 16U /* w/1 link descriptor */ +#define OSPF_ROUTER_LSA_MIN_SIZE 4U /* w/0 link descriptors */ +/* There is an edge case, when number of links in a Router-LSA may be 0 without + breaking the specification. A router, which has no other links to backbone + area besides one virtual link, will not put any VL descriptor blocks into + the Router-LSA generated for area 0 until a full adjacency over the VL is + reached (RFC2328 12.4.1.3). In this case the Router-LSA initially received + by the other end of the VL will have 0 link descriptor blocks, but soon will + be replaced with the next revision having 1 descriptor block. */ struct router_lsa { struct lsa_header header; @@ -204,9 +207,7 @@ struct as_external_lsa } e[1]; }; -#ifdef HAVE_OPAQUE_LSA #include "ospfd/ospf_opaque.h" -#endif /* HAVE_OPAQUE_LSA */ /* Macros. */ #define GET_METRIC(x) get_metric(x) @@ -230,12 +231,14 @@ extern struct timeval tv_adjust (struct timeval); extern int tv_ceil (struct timeval); extern int tv_floor (struct timeval); extern struct timeval int2tv (int); +extern struct timeval msec2tv (int); extern struct timeval tv_add (struct timeval, struct timeval); extern struct timeval tv_sub (struct timeval, struct timeval); 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 *); @@ -243,6 +246,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); @@ -273,7 +277,7 @@ extern struct ospf_lsa *ospf_lsa_install (struct ospf *, extern void ospf_nssa_lsa_flush (struct ospf *ospf, struct prefix_ipv4 *p); extern void ospf_external_lsa_flush (struct ospf *, u_char, struct prefix_ipv4 *, - unsigned int /* , struct in_addr nexthop */); + ifindex_t /* , struct in_addr nexthop */); extern struct in_addr ospf_get_ip_from_ifp (struct ospf_interface *); diff --git a/ospfd/ospf_lsdb.c b/ospfd/ospf_lsdb.c index ea9a35284..b92e7494a 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? */ @@ -155,7 +158,7 @@ ospf_lsdb_delete (struct ospf_lsdb *lsdb, struct ospf_lsa *lsa) if (lsa) zlog_warn ("LSA[Type%d:%s]: LSA %p, lsa->lsdb %p", lsa->data->type, inet_ntoa (lsa->data->id), - lsa, lsa->lsdb); + (void *)lsa, (void *)lsa->lsdb); return; } @@ -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 4157b6856..51ae45bf5 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 bad674b6a..32b64e2aa 100644 --- a/ospfd/ospf_main.c +++ b/ospfd/ospf_main.c @@ -39,6 +39,7 @@ #include "privs.h" #include "sigevent.h" #include "zclient.h" +#include "vrf.h" #include "ospfd/ospfd.h" #include "ospfd/ospf_interface.h" @@ -68,7 +69,7 @@ struct zebra_privs_t ospfd_privs = .vty_group = VTY_GROUP, #endif .caps_p = _caps_p, - .cap_num_p = sizeof(_caps_p)/sizeof(_caps_p[0]), + .cap_num_p = array_size(_caps_p), .cap_num_i = 0 }; @@ -132,7 +133,7 @@ Report bugs to %s\n", progname, ZEBRA_BUG_ADDRESS); } exit (status); } - + /* SIGHUP handler. */ static void sighup (void) @@ -174,7 +175,7 @@ struct quagga_signal_t ospf_signals[] = .handler = &sigint, }, }; - + /* OSPFd main routine. */ int main (int argc, char **argv) @@ -191,6 +192,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,43 +281,36 @@ 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; /* Library inits. */ zprivs_init (&ospfd_privs); - signal_init (master, Q_SIGC(ospf_signals), ospf_signals); + signal_init (master, array_size(ospf_signals), ospf_signals); cmd_init (1); debug_init (); vty_init (master); memory_init (); + vrf_init (); access_list_init (); prefix_list_init (); /* OSPFd inits. */ ospf_if_init (); - ospf_zebra_init (); + ospf_zebra_init (master); /* OSPF vty inits. */ ospf_vty_init (); ospf_vty_show_init (); + ospf_vty_clear_init (); ospf_route_map_init (); #ifdef HAVE_SNMP ospf_snmp_init (); #endif /* HAVE_SNMP */ -#ifdef HAVE_OPAQUE_LSA ospf_opaque_init (); -#endif /* HAVE_OPAQUE_LSA */ - sort_node (); - /* Get configuration file. */ vty_read_config (config_file, config_default); diff --git a/ospfd/ospf_neighbor.c b/ospfd/ospf_neighbor.c index 967ca15db..06e63dd28 100644 --- a/ospfd/ospf_neighbor.c +++ b/ospfd/ospf_neighbor.c @@ -45,8 +45,8 @@ /* Fill in the the 'key' as appropriate to retrieve the entry for nbr * from the ospf_interface's nbrs table. Indexed by interface address - * for all cases except Virtual-link interfaces, where neighbours are - * indexed by router-ID instead. + * for all cases except Virtual-link and PointToPoint interfaces, where + * neighbours are indexed by router-ID instead. */ static void ospf_nbr_key (struct ospf_interface *oi, struct ospf_neighbor *nbr, @@ -56,7 +56,8 @@ ospf_nbr_key (struct ospf_interface *oi, struct ospf_neighbor *nbr, key->prefixlen = IPV4_MAX_BITLEN; /* vlinks are indexed by router-id */ - if (oi->type == OSPF_IFTYPE_VIRTUALLINK) + if (oi->type == OSPF_IFTYPE_VIRTUALLINK || + oi->type == OSPF_IFTYPE_POINTOPOINT) key->u.prefix4 = nbr->router_id; else key->u.prefix4 = nbr->src; @@ -180,6 +181,35 @@ ospf_nbr_delete (struct ospf_neighbor *nbr) route_unlock_node (rn); } + else + { + /* + * This neighbor was not found, but before we move on and + * free the neighbor structre, make sure that it was not + * indexed incorrectly and ended up in the "worng" place + */ + + /* Reverse the lookup rules */ + if (oi->type == OSPF_IFTYPE_VIRTUALLINK || + oi->type == OSPF_IFTYPE_POINTOPOINT) + p.u.prefix4 = nbr->src; + else + p.u.prefix4 = nbr->router_id; + + rn = route_node_lookup (oi->nbrs, &p); + if (rn){ + /* We found the neighbor! + * Now make sure it is not the exact same neighbor + * structure that we are about to free + */ + if (nbr == rn->info){ + /* Same neighbor, drop the reference to it */ + rn->info = NULL; + route_unlock_node (rn); + } + route_unlock_node (rn); + } + } /* Free ospf_neighbor structure. */ ospf_nbr_free (nbr); @@ -202,6 +232,17 @@ ospf_nbr_bidirectional (struct in_addr *router_id, return 0; } +/* reset nbr_self */ +void +ospf_nbr_self_reset (struct ospf_interface *oi) +{ + if (oi->nbr_self) + ospf_nbr_delete (oi->nbr_self); + + oi->nbr_self = ospf_nbr_new (oi); + ospf_nbr_add_self (oi); +} + /* Add self to nbr list. */ void ospf_nbr_add_self (struct ospf_interface *oi) @@ -262,7 +303,6 @@ ospf_nbr_count (struct ospf_interface *oi, int state) return count; } -#ifdef HAVE_OPAQUE_LSA int ospf_nbr_count_opaque_capable (struct ospf_interface *oi) { @@ -279,11 +319,10 @@ ospf_nbr_count_opaque_capable (struct ospf_interface *oi) return count; } -#endif /* HAVE_OPAQUE_LSA */ /* lookup nbr by address - use this only if you know you must - * otherwise use the ospf_nbr_lookup() wrapper, which deals - * with virtual link neighbours + * otherwise use the ospf_nbr_lookup() wrapper, which deals + * with virtual link and PointToPoint neighbours */ struct ospf_neighbor * ospf_nbr_lookup_by_addr (struct route_table *nbrs, @@ -375,7 +414,8 @@ struct ospf_neighbor * ospf_nbr_lookup (struct ospf_interface *oi, struct ip *iph, struct ospf_header *ospfh) { - if (oi->type == OSPF_IFTYPE_VIRTUALLINK) + if (oi->type == OSPF_IFTYPE_VIRTUALLINK || + oi->type == OSPF_IFTYPE_POINTOPOINT) return (ospf_nbr_lookup_by_routerid (oi->nbrs, &ospfh->router_id)); else return (ospf_nbr_lookup_by_addr (oi->nbrs, &iph->ip_src)); @@ -435,8 +475,9 @@ ospf_nbr_get (struct ospf_interface *oi, struct ospf_header *ospfh, key.family = AF_INET; key.prefixlen = IPV4_MAX_BITLEN; - if (oi->type == OSPF_IFTYPE_VIRTUALLINK) - key.u.prefix4 = ospfh->router_id; /* index vlink nbrs by router-id */ + if (oi->type == OSPF_IFTYPE_VIRTUALLINK || + oi->type == OSPF_IFTYPE_POINTOPOINT) + key.u.prefix4 = ospfh->router_id;/* index vlink and ptp nbrs by router-id */ else key.u.prefix4 = iph->ip_src; diff --git a/ospfd/ospf_neighbor.h b/ospfd/ospf_neighbor.h index 25f135242..38f8cb5cb 100644 --- a/ospfd/ospf_neighbor.h +++ b/ospfd/ospf_neighbor.h @@ -99,11 +99,10 @@ extern struct ospf_neighbor *ospf_nbr_new (struct ospf_interface *); extern void ospf_nbr_free (struct ospf_neighbor *); extern void ospf_nbr_delete (struct ospf_neighbor *); extern int ospf_nbr_bidirectional (struct in_addr *, struct in_addr *, int); +extern void ospf_nbr_self_reset (struct ospf_interface *); extern void ospf_nbr_add_self (struct ospf_interface *); extern int ospf_nbr_count (struct ospf_interface *, int); -#ifdef HAVE_OPAQUE_LSA extern int ospf_nbr_count_opaque_capable (struct ospf_interface *); -#endif /* HAVE_OPAQUE_LSA */ extern struct ospf_neighbor *ospf_nbr_get (struct ospf_interface *, struct ospf_header *, struct ip *, struct prefix *); diff --git a/ospfd/ospf_network.c b/ospfd/ospf_network.c index 3e326a8c3..02ddf92f0 100644 --- a/ospfd/ospf_network.c +++ b/ospfd/ospf_network.c @@ -48,7 +48,7 @@ extern struct zebra_privs_t ospfd_privs; /* Join to the OSPF ALL SPF ROUTERS multicast group. */ int ospf_if_add_allspfrouters (struct ospf *top, struct prefix *p, - unsigned int ifindex) + ifindex_t ifindex) { int ret; @@ -69,7 +69,7 @@ ospf_if_add_allspfrouters (struct ospf *top, struct prefix *p, int ospf_if_drop_allspfrouters (struct ospf *top, struct prefix *p, - unsigned int ifindex) + ifindex_t ifindex) { int ret; @@ -89,8 +89,7 @@ ospf_if_drop_allspfrouters (struct ospf *top, struct prefix *p, /* Join to the OSPF ALL Designated ROUTERS multicast group. */ int -ospf_if_add_alldrouters (struct ospf *top, struct prefix *p, unsigned int - ifindex) +ospf_if_add_alldrouters (struct ospf *top, struct prefix *p, ifindex_t ifindex) { int ret; @@ -110,8 +109,7 @@ ospf_if_add_alldrouters (struct ospf *top, struct prefix *p, unsigned int } int -ospf_if_drop_alldrouters (struct ospf *top, struct prefix *p, unsigned int - ifindex) +ospf_if_drop_alldrouters (struct ospf *top, struct prefix *p, ifindex_t ifindex) { int ret; @@ -130,7 +128,7 @@ ospf_if_drop_alldrouters (struct ospf *top, struct prefix *p, unsigned int } int -ospf_if_ipmulticast (struct ospf *top, struct prefix *p, unsigned int ifindex) +ospf_if_ipmulticast (struct ospf *top, struct prefix *p, ifindex_t ifindex) { u_char val; int ret, len; @@ -228,7 +226,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 +247,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 f69099127..8257adb4a 100644 --- a/ospfd/ospf_network.h +++ b/ospfd/ospf_network.h @@ -25,15 +25,15 @@ /* Prototypes. */ extern int ospf_if_add_allspfrouters (struct ospf *, struct prefix *, - unsigned int); + ifindex_t); extern int ospf_if_drop_allspfrouters (struct ospf *, struct prefix *, - unsigned int); + ifindex_t); extern int ospf_if_add_alldrouters (struct ospf *, struct prefix *, - unsigned int); + ifindex_t); extern int ospf_if_drop_alldrouters (struct ospf *, struct prefix *, - unsigned int); -extern int ospf_if_ipmulticast (struct ospf *, struct prefix *, unsigned int); + ifindex_t); +extern int ospf_if_ipmulticast (struct ospf *, struct prefix *, ifindex_t); 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 cbc31716b..5c01a5869 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) @@ -211,7 +208,6 @@ ospf_db_summary_isempty (struct ospf_neighbor *nbr) static int ospf_db_summary_add (struct ospf_neighbor *nbr, struct ospf_lsa *lsa) { -#ifdef HAVE_OPAQUE_LSA switch (lsa->data->type) { case OSPF_OPAQUE_LINK_LSA: @@ -229,7 +225,6 @@ ospf_db_summary_add (struct ospf_neighbor *nbr, struct ospf_lsa *lsa) default: break; } -#endif /* HAVE_OPAQUE_LSA */ /* Stay away from any Local Translated Type-7 LSAs */ if (CHECK_FLAG (lsa->flags, OSPF_LSA_LOCAL_XLT)) @@ -261,7 +256,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, @@ -285,7 +280,6 @@ nsm_negotiation_done (struct ospf_neighbor *nbr) LSDB_LOOP (ASBR_SUMMARY_LSDB (area), rn, lsa) ospf_db_summary_add (nbr, lsa); -#ifdef HAVE_OPAQUE_LSA /* Process only if the neighbor is opaque capable. */ if (CHECK_FLAG (nbr->options, OSPF_OPTION_O)) { @@ -294,7 +288,6 @@ nsm_negotiation_done (struct ospf_neighbor *nbr) LSDB_LOOP (OPAQUE_AREA_LSDB (area), rn, lsa) ospf_db_summary_add (nbr, lsa); } -#endif /* HAVE_OPAQUE_LSA */ if (CHECK_FLAG (nbr->options, OSPF_OPTION_NP)) { @@ -307,13 +300,15 @@ nsm_negotiation_done (struct ospf_neighbor *nbr) LSDB_LOOP (EXTERNAL_LSDB (nbr->oi->ospf), rn, lsa) ospf_db_summary_add (nbr, lsa); -#ifdef HAVE_OPAQUE_LSA if (CHECK_FLAG (nbr->options, OSPF_OPTION_O) && (nbr->oi->type != OSPF_IFTYPE_VIRTUALLINK && area->external_routing == OSPF_AREA_DEFAULT)) LSDB_LOOP (OPAQUE_AS_LSDB (nbr->oi->ospf), rn, lsa) ospf_db_summary_add (nbr, lsa); -#endif /* HAVE_OPAQUE_LSA */ + + /* Send Link State Request. */ + if (nbr->t_ls_req == NULL) + ospf_ls_req_send (nbr); return 0; } @@ -323,10 +318,10 @@ nsm_exchange_done (struct ospf_neighbor *nbr) { if (ospf_ls_request_isempty (nbr)) return NSM_Full; - - /* Send Link State Request. */ - ospf_ls_req_send (nbr); - + + if (nbr->t_ls_req == NULL) + ospf_ls_req_send (nbr); + return NSM_Loading; } @@ -363,10 +358,8 @@ nsm_clear_adj (struct ospf_neighbor *nbr) if (!ospf_ls_retransmit_isempty (nbr)) ospf_ls_retransmit_clear (nbr); -#ifdef HAVE_OPAQUE_LSA if (CHECK_FLAG (nbr->options, OSPF_OPTION_O)) UNSET_FLAG (nbr->options, OSPF_OPTION_O); -#endif /* HAVE_OPAQUE_LSA */ } static int @@ -624,26 +617,9 @@ nsm_notice_state_change (struct ospf_neighbor *nbr, int next_state, int event) nbr->last_regress_str = ospf_nsm_event_str [event]; } -#ifdef HAVE_SNMP - /* Terminal state or regression */ - if ((next_state == NSM_Full) - || (next_state == NSM_TwoWay) - || (next_state < nbr->state)) - { - /* ospfVirtNbrStateChange */ - if (nbr->oi->type == OSPF_IFTYPE_VIRTUALLINK) - ospfTrapVirtNbrStateChange(nbr); - /* ospfNbrStateChange trap */ - else - /* To/From FULL, only managed by DR */ - if (((next_state != NSM_Full) && (nbr->state != NSM_Full)) - || (nbr->oi->state == ISM_DR)) - ospfTrapNbrStateChange(nbr); - } -#endif } -void +static void nsm_change_state (struct ospf_neighbor *nbr, int state) { struct ospf_interface *oi = nbr->oi; @@ -664,6 +640,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)) @@ -736,9 +731,7 @@ nsm_change_state (struct ospf_neighbor *nbr, int state) } } -#ifdef HAVE_OPAQUE_LSA ospf_opaque_nsm_change (nbr, old_state); -#endif /* HAVE_OPAQUE_LSA */ /* State changes from > ExStart to <= ExStart should clear any Exchange * or Full/LSA Update related lists and state. @@ -763,20 +756,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 +766,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), @@ -830,7 +807,34 @@ ospf_nsm_event (struct thread *thread) if (next_state != nbr->state) { nsm_notice_state_change (nbr, next_state, event); +#ifdef HAVE_SNMP + int send_trap_virt = 0; + int send_trap = 0; + /* Terminal state or regression */ + if ((next_state == NSM_Full) + || (next_state == NSM_TwoWay) + || (next_state < nbr->state)) + { + /* ospfVirtNbrStateChange */ + if (nbr->oi->type == OSPF_IFTYPE_VIRTUALLINK) + send_trap_virt = 1; + /* ospfNbrStateChange trap */ + else + /* To/From FULL, only managed by DR */ + if (((next_state != NSM_Full) && (nbr->state != NSM_Full)) + || (nbr->oi->state == ISM_DR)) + send_trap = 1; + } +#endif nsm_change_state (nbr, next_state); + +#ifdef HAVE_SNMP + if (send_trap_virt) { + ospfTrapVirtNbrStateChange(nbr); + } else if (send_trap) { + ospfTrapNbrStateChange(nbr); + } +#endif } /* Make sure timer is set. */ diff --git a/ospfd/ospf_nsm.h b/ospfd/ospf_nsm.h index 4f2ae8083..9b7e14a4a 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 aa126e192..39465c15a 100644 --- a/ospfd/ospf_opaque.c +++ b/ospfd/ospf_opaque.c @@ -22,12 +22,11 @@ */ /***** MTYPE definitions are not reflected to "memory.h" yet. *****/ -#define MTYPE_OSPF_OPAQUE_FUNCTAB 0 -#define MTYPE_OPAQUE_INFO_PER_TYPE 0 -#define MTYPE_OPAQUE_INFO_PER_ID 0 +#define MTYPE_OSPF_OPAQUE_FUNCTAB MTYPE_TMP +#define MTYPE_OPAQUE_INFO_PER_TYPE MTYPE_TMP +#define MTYPE_OPAQUE_INFO_PER_ID MTYPE_TMP #include -#ifdef HAVE_OPAQUE_LSA #include "linklist.h" #include "prefix.h" @@ -62,9 +61,8 @@ * Followings are initialize/terminate functions for Opaque-LSAs handling. *------------------------------------------------------------------------*/ -#ifdef HAVE_OSPF_TE #include "ospfd/ospf_te.h" -#endif /* HAVE_OSPF_TE */ +#include "ospfd/ospf_ri.h" #ifdef SUPPORT_OSPF_API int ospf_apiserver_init (void); @@ -87,10 +85,11 @@ ospf_opaque_init (void) ospf_opaque_register_vty (); ospf_opaque_funclist_init (); -#ifdef HAVE_OSPF_TE if (ospf_mpls_te_init () != 0) exit (1); -#endif /* HAVE_OSPF_TE */ + + if (ospf_router_info_init () != 0) + exit (1); #ifdef SUPPORT_OSPF_API if ((ospf_apiserver_enable) && (ospf_apiserver_init () != 0)) @@ -103,9 +102,9 @@ ospf_opaque_init (void) void ospf_opaque_term (void) { -#ifdef HAVE_OSPF_TE ospf_mpls_te_term (); -#endif /* HAVE_OSPF_TE */ + + ospf_router_info_term (); #ifdef SUPPORT_OSPF_API ospf_apiserver_term (); @@ -220,12 +219,24 @@ ospf_opaque_type_name (u_char opaque_type) case OPAQUE_TYPE_GRACE_LSA: name = "Grace-LSA"; break; + case OPAQUE_TYPE_INTER_AS_LSA: + name = "Inter-AS TE-v2 LSA"; + break; + case OPAQUE_TYPE_ROUTER_INFORMATION_LSA: + name = "Router Information LSA"; + break; 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; @@ -1305,13 +1316,7 @@ ospf_opaque_lsa_originate_schedule (struct ospf_interface *oi, int *delay0) zlog_debug ("ospf_opaque_lsa_originate_schedule: Not operational."); goto out; /* This is not an error. */ } - if (IS_OPAQUE_LSA_ORIGINATION_BLOCKED (top->opaque)) - { - if (IS_DEBUG_OSPF_EVENT) - zlog_debug ("ospf_opaque_lsa_originate_schedule: Under blockade."); - goto out; /* This is not an error, too. */ - } - + if (delay0 != NULL) delay = *delay0; @@ -1331,10 +1336,10 @@ ospf_opaque_lsa_originate_schedule (struct ospf_interface *oi, int *delay0) && oi->t_opaque_lsa_self == NULL) { if (IS_DEBUG_OSPF_EVENT) - zlog_debug ("Schedule Type-9 Opaque-LSA origination in %d sec later.", delay); + zlog_debug ("Schedule Type-9 Opaque-LSA origination in %d ms later.", delay); oi->t_opaque_lsa_self = - thread_add_timer (master, ospf_opaque_type9_lsa_originate, oi, delay); - delay += OSPF_MIN_LS_INTERVAL; + thread_add_timer_msec (master, ospf_opaque_type9_lsa_originate, oi, delay); + delay += top->min_ls_interval; } if (! list_isempty (ospf_opaque_type10_funclist) @@ -1347,11 +1352,11 @@ ospf_opaque_lsa_originate_schedule (struct ospf_interface *oi, int *delay0) * again and again. */ if (IS_DEBUG_OSPF_EVENT) - zlog_debug ("Schedule Type-10 Opaque-LSA origination in %d sec later.", delay); + zlog_debug ("Schedule Type-10 Opaque-LSA origination in %d ms later.", delay); area->t_opaque_lsa_self = - thread_add_timer (master, ospf_opaque_type10_lsa_originate, + thread_add_timer_msec (master, ospf_opaque_type10_lsa_originate, area, delay); - delay += OSPF_MIN_LS_INTERVAL; + delay += top->min_ls_interval; } if (! list_isempty (ospf_opaque_type11_funclist) @@ -1364,11 +1369,11 @@ ospf_opaque_lsa_originate_schedule (struct ospf_interface *oi, int *delay0) * again and again. */ if (IS_DEBUG_OSPF_EVENT) - zlog_debug ("Schedule Type-11 Opaque-LSA origination in %d sec later.", delay); + zlog_debug ("Schedule Type-11 Opaque-LSA origination in %d ms later.", delay); top->t_opaque_lsa_self = - thread_add_timer (master, ospf_opaque_type11_lsa_originate, + thread_add_timer_msec (master, ospf_opaque_type11_lsa_originate, top, delay); - delay += OSPF_MIN_LS_INTERVAL; + delay += top->min_ls_interval; } /* @@ -1646,7 +1651,7 @@ ospf_opaque_lsa_refresh (struct ospf_lsa *lsa) #define OSPF_OPAQUE_TIMER_ON(T,F,L,V) \ if (!(T)) \ - (T) = thread_add_timer (master, (F), (L), (V)) + (T) = thread_add_timer_msec (master, (F), (L), (V)) static struct ospf_lsa *pseudo_lsa (struct ospf_interface *oi, struct ospf_area *area, u_char lsa_type, u_char opaque_type); static int ospf_opaque_type9_lsa_reoriginate_timer (struct thread *t); @@ -1753,13 +1758,7 @@ ospf_opaque_lsa_reoriginate_schedule (void *lsa_type_dependent, zlog_debug ("ospf_opaque_lsa_reoriginate_schedule: Not operational."); goto out; /* This is not an error. */ } - if (IS_OPAQUE_LSA_ORIGINATION_BLOCKED (top->opaque)) - { - if (IS_DEBUG_OSPF_EVENT) - zlog_debug ("ospf_opaque_lsa_reoriginate_schedule: Under blockade."); - goto out; /* This is not an error, too. */ - } - + /* Generate a dummy lsa to be passed for a lookup function. */ lsa = pseudo_lsa (oi, area, lsa_type, opaque_type); @@ -1800,11 +1799,11 @@ ospf_opaque_lsa_reoriginate_schedule (void *lsa_type_dependent, * it is highly possible that these conditions might not be satisfied * at the time of re-origination function is to be called. */ - delay = OSPF_MIN_LS_INTERVAL; /* XXX */ + delay = top->min_ls_interval; /* XXX */ if (IS_DEBUG_OSPF_EVENT) zlog_debug ("Schedule Type-%u Opaque-LSA to RE-ORIGINATE in %d" - " sec later: [opaque-type=%u]", + " ms later: [opaque-type=%u]", lsa_type, delay, GET_OPAQUE_TYPE (ntohl (lsa->data->id.s_addr))); @@ -1986,6 +1985,7 @@ ospf_opaque_lsa_refresh_schedule (struct ospf_lsa *lsa0) struct opaque_info_per_type *oipt; struct opaque_info_per_id *oipi; struct ospf_lsa *lsa; + struct ospf *top; int delay; if ((oipt = lookup_opaque_info_by_type (lsa0)) == NULL @@ -2017,7 +2017,10 @@ ospf_opaque_lsa_refresh_schedule (struct ospf_lsa *lsa0) ospf_ls_retransmit_delete_nbr_area (lsa->area, lsa); break; case OSPF_OPAQUE_AS_LSA: - ospf_ls_retransmit_delete_nbr_as (lsa0->area->ospf, lsa); + top = ospf_lookup (); + if ((lsa0->area != NULL) && (lsa0->area->ospf != NULL)) + top = lsa0->area->ospf; + ospf_ls_retransmit_delete_nbr_as (top, lsa); break; default: zlog_warn ("ospf_opaque_lsa_refresh_schedule: Unexpected LSA-type(%u)", lsa->data->type); @@ -2030,7 +2033,7 @@ ospf_opaque_lsa_refresh_schedule (struct ospf_lsa *lsa0) zlog_debug ("Schedule Type-%u Opaque-LSA to REFRESH in %d sec later: [opaque-type=%u, opaque-id=%x]", lsa->data->type, delay, GET_OPAQUE_TYPE (ntohl (lsa->data->id.s_addr)), GET_OPAQUE_ID (ntohl (lsa->data->id.s_addr))); OSPF_OPAQUE_TIMER_ON (oipi->t_opaque_lsa_self, - ospf_opaque_lsa_refresh_timer, oipi, delay); + ospf_opaque_lsa_refresh_timer, oipi, delay * 1000); out: return; } @@ -2062,6 +2065,9 @@ ospf_opaque_lsa_flush_schedule (struct ospf_lsa *lsa0) struct opaque_info_per_type *oipt; struct opaque_info_per_id *oipi; struct ospf_lsa *lsa; + struct ospf *top; + + top = ospf_lookup (); if ((oipt = lookup_opaque_info_by_type (lsa0)) == NULL || (oipi = lookup_opaque_info_by_id (oipt, lsa0)) == NULL) @@ -2085,7 +2091,9 @@ ospf_opaque_lsa_flush_schedule (struct ospf_lsa *lsa0) ospf_ls_retransmit_delete_nbr_area (lsa->area, lsa); break; case OSPF_OPAQUE_AS_LSA: - ospf_ls_retransmit_delete_nbr_as (lsa0->area->ospf, lsa); + if ((lsa0->area != NULL) && (lsa0->area->ospf != NULL)) + top = lsa0->area->ospf; + ospf_ls_retransmit_delete_nbr_as (top, lsa); break; default: zlog_warn ("ospf_opaque_lsa_flush_schedule: Unexpected LSA-type(%u)", lsa->data->type); @@ -2109,136 +2117,21 @@ ospf_opaque_lsa_flush_schedule (struct ospf_lsa *lsa0) zlog_debug ("Schedule Type-%u Opaque-LSA to FLUSH: [opaque-type=%u, opaque-id=%x]", lsa->data->type, GET_OPAQUE_TYPE (ntohl (lsa->data->id.s_addr)), GET_OPAQUE_ID (ntohl (lsa->data->id.s_addr))); /* This lsa will be flushed and removed eventually. */ - ospf_lsa_flush (lsa0->area->ospf, lsa); + ospf_lsa_flush (top, lsa); out: return; } -/*------------------------------------------------------------------------* - * Followings are control functions to block origination after restart. - *------------------------------------------------------------------------*/ - -static void ospf_opaque_exclude_lsa_from_lsreq (struct route_table *nbrs, struct ospf_neighbor *inbr, struct ospf_lsa *lsa); -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); - -void -ospf_opaque_adjust_lsreq (struct ospf_neighbor *nbr, struct list *lsas) -{ - struct ospf *top; - struct ospf_area *area; - struct ospf_interface *oi; - struct listnode *node1, *nnode1; - struct listnode *node2, *nnode2; - struct ospf_lsa *lsa; - - if ((top = oi_to_top (nbr->oi)) == NULL) - goto out; - - /* - * If an instance of self-originated Opaque-LSA is found in the given - * LSA list, and it is not installed to LSDB yet, exclude it from the - * list "nbr->ls_req". In this way, it is assured that an LSReq message, - * which might be sent in the process of flooding, will not request for - * the LSA to be flushed immediately; otherwise, depending on timing, - * an LSUpd message will carry instances of target LSAs with MaxAge, - * while other LSUpd message might carry old LSA instances (non-MaxAge). - * Obviously, the latter would trigger miserable situations that repeat - * installation and removal of unwanted LSAs indefinitely. - */ - for (ALL_LIST_ELEMENTS (lsas, node1, nnode1, lsa)) - { - /* Filter out unwanted LSAs. */ - if (! IS_OPAQUE_LSA (lsa->data->type)) - continue; - if (! IPV4_ADDR_SAME (&lsa->data->adv_router, &top->router_id)) - continue; - - /* - * Don't touch an LSA which has MaxAge; two possible cases. - * - * 1) This LSA has originally flushed by myself (received LSUpd - * message's router-id is equal to my router-id), and flooded - * back by an opaque-capable router. - * - * 2) This LSA has expired in an opaque-capable router and thus - * flushed by the router. - */ - if (IS_LSA_MAXAGE (lsa)) - continue; - - /* If the LSA has installed in the LSDB, nothing to do here. */ - if (ospf_lsa_lookup_by_header (nbr->oi->area, lsa->data) != NULL) - continue; - - /* Ok, here we go. */ - switch (lsa->data->type) - { - case OSPF_OPAQUE_LINK_LSA: - oi = nbr->oi; - ospf_opaque_exclude_lsa_from_lsreq (oi->nbrs, nbr, lsa); - break; - case OSPF_OPAQUE_AREA_LSA: - area = nbr->oi->area; - for (ALL_LIST_ELEMENTS (area->oiflist, node2, nnode2, oi)) - ospf_opaque_exclude_lsa_from_lsreq (oi->nbrs, nbr, lsa); - break; - case OSPF_OPAQUE_AS_LSA: - for (ALL_LIST_ELEMENTS (top->oiflist, node2, nnode2, oi)) - ospf_opaque_exclude_lsa_from_lsreq (oi->nbrs, nbr, lsa); - break; - default: - break; - } - } - -out: - return; -} - -static void -ospf_opaque_exclude_lsa_from_lsreq (struct route_table *nbrs, - struct ospf_neighbor *inbr, - struct ospf_lsa *lsa) -{ - struct route_node *rn; - struct ospf_neighbor *onbr; - struct ospf_lsa *ls_req; - - for (rn = route_top (nbrs); rn; rn = route_next (rn)) - { - if ((onbr = rn->info) == NULL) - continue; - if (onbr == inbr) - continue; - if ((ls_req = ospf_ls_request_lookup (onbr, lsa)) == NULL) - continue; - - if (IS_DEBUG_OSPF_EVENT) - zlog_debug ("LSA[%s]: Exclude this entry from LSReq to send.", dump_lsa_key (lsa)); - - ospf_ls_request_delete (onbr, ls_req); -/* ospf_check_nbr_loading (onbr);*//* XXX */ - } - - return; -} - void ospf_opaque_self_originated_lsa_received (struct ospf_neighbor *nbr, struct ospf_lsa *lsa) { struct ospf *top; - u_char before; - + if ((top = oi_to_top (nbr->oi)) == NULL) return; - before = IS_OPAQUE_LSA_ORIGINATION_BLOCKED (top->opaque); - /* * Since these LSA entries are not yet installed into corresponding * LSDB, just flush them without calling ospf_ls_maxage() afterward. @@ -2247,219 +2140,25 @@ ospf_opaque_self_originated_lsa_received (struct ospf_neighbor *nbr, switch (lsa->data->type) { case OSPF_OPAQUE_LINK_LSA: - SET_FLAG (top->opaque, OPAQUE_BLOCK_TYPE_09_LSA_BIT); ospf_flood_through_area (nbr->oi->area, NULL/*inbr*/, lsa); break; case OSPF_OPAQUE_AREA_LSA: - SET_FLAG (top->opaque, OPAQUE_BLOCK_TYPE_10_LSA_BIT); ospf_flood_through_area (nbr->oi->area, NULL/*inbr*/, lsa); break; case OSPF_OPAQUE_AS_LSA: - SET_FLAG (top->opaque, OPAQUE_BLOCK_TYPE_11_LSA_BIT); ospf_flood_through_as (top, NULL/*inbr*/, lsa); break; default: zlog_warn ("ospf_opaque_self_originated_lsa_received: Unexpected LSA-type(%u)", lsa->data->type); return; } - - ospf_lsa_discard (lsa); /* List "lsas" will be deleted by caller. */ - - if (before == 0 && IS_OPAQUE_LSA_ORIGINATION_BLOCKED (top->opaque)) - { - if (IS_DEBUG_OSPF_EVENT) - zlog_debug ("Block Opaque-LSA origination: OFF -> ON"); - } -} - -void -ospf_opaque_ls_ack_received (struct ospf_neighbor *nbr, struct ospf_lsa *lsa) -{ - struct ospf *top; - int delay; - struct ospf_interface *oi; - struct listnode *node, *nnode; - - if ((top = oi_to_top (nbr->oi)) == NULL) - return; - - if (!IS_OPAQUE_LSA_ORIGINATION_BLOCKED (top->opaque)) - return; - - switch (lsa->data->type) - { - 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); - /* 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); - /* 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); - /* Callback function... */ - break; - default: - zlog_warn ("ospf_opaque_ls_ack_received: Unexpected LSA-type(%u)", lsa->data->type); - return; - } - - 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 (! CHECK_FLAG (top->config, OSPF_OPAQUE_CAPABLE)) - return; /* Opaque capability condition must have changed. */ - - /* Ok, let's start origination of Opaque-LSAs. */ - delay = OSPF_MIN_LS_INTERVAL; - - for (ALL_LIST_ELEMENTS (top->oiflist, node, nnode, oi)) - { - if (! ospf_if_is_enable (oi) - || ospf_nbr_count_opaque_capable (oi) == 0) - continue; - - ospf_opaque_lsa_originate_schedule (oi, &delay); - } - - return; -} - -static void -ospf_opaque_type9_lsa_rxmt_nbr_check (struct ospf_interface *oi) -{ - unsigned long n; - - n = ospf_opaque_nrxmt_self (oi->nbrs, OSPF_OPAQUE_LINK_LSA); - if (n == 0) - { - if (IS_DEBUG_OSPF_EVENT) - zlog_debug ("Self-originated type-9 Opaque-LSAs: OI(%s): Flush completed", IF_NAME (oi)); - - UNSET_FLAG (oi->area->ospf->opaque, OPAQUE_BLOCK_TYPE_09_LSA_BIT); - } - return; -} - -static void -ospf_opaque_type10_lsa_rxmt_nbr_check (struct ospf_area *area) -{ - struct listnode *node; - struct ospf_interface *oi; - unsigned long n = 0; - - for (ALL_LIST_ELEMENTS_RO (area->oiflist, node, oi)) - { - if (area->area_id.s_addr != OSPF_AREA_BACKBONE - && oi->type == OSPF_IFTYPE_VIRTUALLINK) - continue; - - n = ospf_opaque_nrxmt_self (oi->nbrs, OSPF_OPAQUE_AREA_LSA); - if (n > 0) - break; - } - - if (n == 0) - { - if (IS_DEBUG_OSPF_EVENT) - zlog_debug ("Self-originated type-10 Opaque-LSAs: AREA(%s): Flush completed", inet_ntoa (area->area_id)); - - UNSET_FLAG (area->ospf->opaque, OPAQUE_BLOCK_TYPE_10_LSA_BIT); - } - - return; -} - -static void -ospf_opaque_type11_lsa_rxmt_nbr_check (struct ospf *top) -{ - struct listnode *node; - struct ospf_interface *oi; - unsigned long n = 0; - - for (ALL_LIST_ELEMENTS_RO (top->oiflist, node, oi)) - { - switch (oi->type) - { - case OSPF_IFTYPE_VIRTUALLINK: - continue; - default: - break; - } - - n = ospf_opaque_nrxmt_self (oi->nbrs, OSPF_OPAQUE_AS_LSA); - if (n > 0) - goto out; - } - - if (n == 0) - { - if (IS_DEBUG_OSPF_EVENT) - zlog_debug ("Self-originated type-11 Opaque-LSAs: Flush completed"); - - UNSET_FLAG (top->opaque, OPAQUE_BLOCK_TYPE_11_LSA_BIT); - } - -out: - return; -} - -static unsigned long -ospf_opaque_nrxmt_self (struct route_table *nbrs, int lsa_type) -{ - struct route_node *rn; - struct ospf_neighbor *nbr; - struct ospf *top; - unsigned long n = 0; - - for (rn = route_top (nbrs); rn; rn = route_next (rn)) - { - if ((nbr = rn->info) == NULL) - continue; - if ((top = oi_to_top (nbr->oi)) == NULL) - continue; - if (IPV4_ADDR_SAME (&nbr->router_id, &top->router_id)) - continue; - n += ospf_ls_retransmit_count_self (nbr, lsa_type); - } - - return n; + ospf_lsa_discard (lsa); /* List "lsas" will be deleted by caller. */ } /*------------------------------------------------------------------------* * Followings are util functions; probably be used by Opaque-LSAs only... *------------------------------------------------------------------------*/ -void -htonf (float *src, float *dst) -{ - u_int32_t lu1, lu2; - - memcpy (&lu1, src, sizeof (u_int32_t)); - lu2 = htonl (lu1); - memcpy (dst, &lu2, sizeof (u_int32_t)); - return; -} - -void -ntohf (float *src, float *dst) -{ - u_int32_t lu1, lu2; - - memcpy (&lu1, src, sizeof (u_int32_t)); - lu2 = ntohl (lu1); - memcpy (dst, &lu2, sizeof (u_int32_t)); - return; -} - struct ospf * oi_to_top (struct ospf_interface *oi) { @@ -2472,4 +2171,3 @@ oi_to_top (struct ospf_interface *oi) return top; } -#endif /* HAVE_OPAQUE_LSA */ diff --git a/ospfd/ospf_opaque.h b/ospfd/ospf_opaque.h index 227306456..2ac9b41ef 100644 --- a/ospfd/ospf_opaque.h +++ b/ospfd/ospf_opaque.h @@ -31,23 +31,6 @@ (type) == OSPF_OPAQUE_AREA_LSA || \ (type) == OSPF_OPAQUE_AS_LSA) -/* - * Usage of Opaque-LSA administrative flags in "struct ospf". - * - * 7 6 5 4 3 2 1 0 - * +---+---+---+---+---+---+---+---+ - * |///|///|///|///|B11|B10|B09| O | - * +---+---+---+---+---+---+---+---+ - * |<--------->| A - * | +--- Operation status (operational = 1) - * +----------- Blocking status for each LSA type - */ - -#define IS_OPAQUE_LSA_ORIGINATION_BLOCKED(V) \ - CHECK_FLAG((V), (OPAQUE_BLOCK_TYPE_09_LSA_BIT | \ - OPAQUE_BLOCK_TYPE_10_LSA_BIT | \ - OPAQUE_BLOCK_TYPE_11_LSA_BIT)) - /* * Opaque LSA's link state ID is redefined as follows. * @@ -77,6 +60,10 @@ #define OPAQUE_TYPE_TRAFFIC_ENGINEERING_LSA 1 #define OPAQUE_TYPE_SYCAMORE_OPTICAL_TOPOLOGY_DESC 2 #define OPAQUE_TYPE_GRACE_LSA 3 +#define OPAQUE_TYPE_L1VPN_LSA 5 +#define OPAQUE_TYPE_ROUTER_INFORMATION_LSA 4 +#define OPAQUE_TYPE_INTER_AS_LSA 6 +#define OPAQUE_TYPE_MAX 6 /* Followings types are proposed in internet-draft documents. */ #define OPAQUE_TYPE_8021_QOSPF 129 @@ -87,7 +74,7 @@ #define OPAQUE_TYPE_WILDCARD 0 #define OPAQUE_TYPE_RANGE_UNASSIGNED(type) \ - ( 4 <= (type) && (type) <= 127) + ( OPAQUE_TYPE_MAX <= (type) && (type) <= 127) #define OPAQUE_TYPE_RANGE_RESERVED(type) \ (127 < (type) && (type) <= 255) @@ -151,16 +138,9 @@ extern void ospf_opaque_lsa_reoriginate_schedule (void *lsa_type_dependent, extern void ospf_opaque_lsa_refresh_schedule (struct ospf_lsa *lsa); extern void ospf_opaque_lsa_flush_schedule (struct ospf_lsa *lsa); -extern void ospf_opaque_adjust_lsreq (struct ospf_neighbor *nbr, - struct list *lsas); extern void ospf_opaque_self_originated_lsa_received (struct ospf_neighbor *nbr, struct ospf_lsa *lsa); -extern void ospf_opaque_ls_ack_received (struct ospf_neighbor *nbr, - struct ospf_lsa *lsa); - -extern void htonf (float *src, float *dst); -extern void ntohf (float *src, float *dst); extern struct ospf *oi_to_top (struct ospf_interface *oi); #endif /* _ZEBRA_OSPF_OPAQUE_H */ diff --git a/ospfd/ospf_packet.c b/ospfd/ospf_packet.c index 351fb210b..facba8950 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) { @@ -644,8 +644,8 @@ ospf_write (struct thread *thread) struct listnode *node; #ifdef WANT_OSPF_WRITE_FRAGMENT static u_int16_t ipid = 0; -#endif /* WANT_OSPF_WRITE_FRAGMENT */ u_int16_t maxdatasize; +#endif /* WANT_OSPF_WRITE_FRAGMENT */ #define OSPF_WRITE_IPHL_SHIFT 2 ospf->t_write = NULL; @@ -659,7 +659,6 @@ ospf_write (struct thread *thread) /* seed ipid static with low order bits of time */ if (ipid == 0) ipid = (time(NULL) & 0xffff); -#endif /* WANT_OSPF_WRITE_FRAGMENT */ /* convenience - max OSPF data per packet, * and reliability - not more data, than our @@ -667,6 +666,7 @@ ospf_write (struct thread *thread) */ maxdatasize = MIN (oi->ifp->mtu, ospf->maxsndbuflen) - sizeof (struct ip); +#endif /* WANT_OSPF_WRITE_FRAGMENT */ /* Get one packet from queue. */ op = ospf_fifo_head (oi->obuf); @@ -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; @@ -879,7 +882,7 @@ ospf_hello (struct ip *iph, struct ospf_header *ospfh, /* Compare options. */ #define REJECT_IF_TBIT_ON 1 /* XXX */ #ifdef REJECT_IF_TBIT_ON - if (CHECK_FLAG (hello->options, OSPF_OPTION_T)) + if (CHECK_FLAG (hello->options, OSPF_OPTION_MT)) { /* * This router does not support non-zero TOS. @@ -891,7 +894,6 @@ ospf_hello (struct ip *iph, struct ospf_header *ospfh, } #endif /* REJECT_IF_TBIT_ON */ -#ifdef HAVE_OPAQUE_LSA if (CHECK_FLAG (oi->ospf->config, OSPF_OPAQUE_CAPABLE) && CHECK_FLAG (hello->options, OSPF_OPTION_O)) { @@ -907,7 +909,6 @@ ospf_hello (struct ip *iph, struct ospf_header *ospfh, UNSET_FLAG (hello->options, OSPF_OPTION_O); /* Ignore O-bit. */ #endif /* STRICT_OBIT_USAGE_CHECK */ } -#endif /* HAVE_OPAQUE_LSA */ /* new for NSSA is to ensure that NP is on and E is off */ @@ -1059,7 +1060,6 @@ ospf_db_desc_proc (struct stream *s, struct ospf_interface *oi, return; } -#ifdef HAVE_OPAQUE_LSA if (IS_OPAQUE_LSA (lsah->type) && ! CHECK_FLAG (nbr->options, OSPF_OPTION_O)) { @@ -1067,14 +1067,11 @@ ospf_db_desc_proc (struct stream *s, struct ospf_interface *oi, OSPF_NSM_EVENT_SCHEDULE (nbr, NSM_SeqNumberMismatch); return; } -#endif /* HAVE_OPAQUE_LSA */ switch (lsah->type) { case OSPF_AS_EXTERNAL_LSA: -#ifdef HAVE_OPAQUE_LSA case OSPF_OPAQUE_AS_LSA: -#endif /* HAVE_OPAQUE_LSA */ /* Check for stub area. Reject if AS-External from stub but allow if from NSSA. */ if (oi->area->external_routing == OSPF_AREA_STUB) @@ -1234,7 +1231,7 @@ ospf_db_desc (struct ip *iph, struct ospf_header *ospfh, } #ifdef REJECT_IF_TBIT_ON - if (CHECK_FLAG (dd->options, OSPF_OPTION_T)) + if (CHECK_FLAG (dd->options, OSPF_OPTION_MT)) { /* * In Hello protocol, optional capability must have checked @@ -1245,7 +1242,6 @@ ospf_db_desc (struct ip *iph, struct ospf_header *ospfh, } #endif /* REJECT_IF_TBIT_ON */ -#ifdef HAVE_OPAQUE_LSA if (CHECK_FLAG (dd->options, OSPF_OPTION_O) && !CHECK_FLAG (oi->ospf->config, OSPF_OPAQUE_CAPABLE)) { @@ -1255,7 +1251,6 @@ ospf_db_desc (struct ip *iph, struct ospf_header *ospfh, */ UNSET_FLAG (dd->options, OSPF_OPTION_O); } -#endif /* HAVE_OPAQUE_LSA */ /* Add event to thread. */ OSPF_NSM_EVENT_SCHEDULE (nbr, NSM_PacketReceived); @@ -1320,7 +1315,6 @@ ospf_db_desc (struct ip *iph, struct ospf_header *ospfh, /* This is where the real Options are saved */ nbr->options = dd->options; -#ifdef HAVE_OPAQUE_LSA if (CHECK_FLAG (oi->ospf->config, OSPF_OPAQUE_CAPABLE)) { if (IS_DEBUG_OSPF_EVENT) @@ -1338,7 +1332,6 @@ ospf_db_desc (struct ip *iph, struct ospf_header *ospfh, /* This situation is undesirable, but not a real error. */ } } -#endif /* HAVE_OPAQUE_LSA */ OSPF_NSM_EVENT_EXECUTE (nbr, NSM_NegotiationDone); @@ -1590,7 +1583,7 @@ ospf_ls_upd_list_lsa (struct ospf_neighbor *nbr, struct stream *s, /* Validate the LSA's LS checksum. */ sum = lsah->checksum; - if (sum != ospf_lsa_checksum (lsah)) + if (! ospf_lsa_checksum_valid (lsah)) { /* (bug #685) more details in a one-line message make it possible * to identify problem source on the one hand and to have a better @@ -1616,7 +1609,6 @@ ospf_ls_upd_list_lsa (struct ospf_neighbor *nbr, struct stream *s, if (ntohs (lsah->ls_age) > OSPF_LSA_MAXAGE) lsah->ls_age = htons (OSPF_LSA_MAXAGE); -#ifdef HAVE_OPAQUE_LSA if (CHECK_FLAG (nbr->options, OSPF_OPTION_O)) { #ifdef STRICT_OBIT_USAGE_CHECK @@ -1648,7 +1640,6 @@ ospf_ls_upd_list_lsa (struct ospf_neighbor *nbr, struct stream *s, zlog_warn ("LSA[Type%d:%s]: Opaque capability mismatch?", lsah->type, inet_ntoa (lsah->id)); continue; } -#endif /* HAVE_OPAQUE_LSA */ /* Create OSPF LSA instance. */ lsa = ospf_lsa_new (); @@ -1658,14 +1649,12 @@ ospf_ls_upd_list_lsa (struct ospf_neighbor *nbr, struct stream *s, switch (lsah->type) { case OSPF_AS_EXTERNAL_LSA: -#ifdef HAVE_OPAQUE_LSA case OSPF_OPAQUE_AS_LSA: lsa->area = NULL; break; case OSPF_OPAQUE_LINK_LSA: lsa->oi = oi; /* Remember incoming interface for flooding control. */ /* Fallthrough */ -#endif /* HAVE_OPAQUE_LSA */ default: lsa->area = oi->area; break; @@ -1676,7 +1665,7 @@ ospf_ls_upd_list_lsa (struct ospf_neighbor *nbr, struct stream *s, if (IS_DEBUG_OSPF_EVENT) zlog_debug("LSA[Type%d:%s]: %p new LSA created with Link State Update", - lsa->data->type, inet_ntoa (lsa->data->id), lsa); + lsa->data->type, inet_ntoa (lsa->data->id), (void *)lsa); listnode_add (lsas, lsa); } @@ -1698,7 +1687,7 @@ ospf_upd_list_clean (struct list *lsas) /* OSPF Link State Update message read -- RFC2328 Section 13. */ static void -ospf_ls_upd (struct ip *iph, struct ospf_header *ospfh, +ospf_ls_upd (struct ospf *ospf, struct ip *iph, struct ospf_header *ospfh, struct stream *s, struct ospf_interface *oi, u_int16_t size) { struct ospf_neighbor *nbr; @@ -1740,28 +1729,18 @@ ospf_ls_upd (struct ip *iph, struct ospf_header *ospfh, */ lsas = ospf_ls_upd_list_lsa (nbr, s, oi, size); -#ifdef HAVE_OPAQUE_LSA - /* - * If self-originated Opaque-LSAs that have flooded before restart - * are contained in the received LSUpd message, corresponding LSReq - * messages to be sent may have to be modified. - * To eliminate possible race conditions such that flushing and normal - * updating for the same LSA would take place alternately, this trick - * must be done before entering to the loop below. - */ - /* XXX: Why is this Opaque specific? Either our core code is deficient - * and this should be fixed generally, or Opaque is inventing strawman - * problems */ - ospf_opaque_adjust_lsreq (nbr, lsas); -#endif /* HAVE_OPAQUE_LSA */ - #define DISCARD_LSA(L,N) {\ if (IS_DEBUG_OSPF_EVENT) \ - zlog_debug ("ospf_lsa_discard() in ospf_ls_upd() point %d: lsa %p Type-%d", N, lsa, (int) lsa->data->type); \ + zlog_debug ("ospf_lsa_discard() in ospf_ls_upd() point %d: lsa %p" \ + " Type-%d", N, (void *)lsa, (int) lsa->data->type); \ 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 +1764,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,29 +1796,51 @@ 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); } -#ifdef HAVE_OPAQUE_LSA if (IS_OPAQUE_LSA (lsa->data->type) && IPV4_ADDR_SAME (&lsa->data->adv_router, &oi->ospf->router_id)) { @@ -1887,10 +1888,9 @@ ospf_ls_upd (struct ip *iph, struct ospf_header *ospfh, continue; } } -#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. @@ -1915,7 +1915,7 @@ ospf_ls_upd (struct ip *iph, struct ospf_header *ospfh, ospf_lsa_flush_area(lsa,out_if->area); if(IS_DEBUG_OSPF_EVENT) zlog_debug ("ospf_lsa_discard() in ospf_ls_upd() point 9: lsa %p Type-%d", - lsa, (int) lsa->data->type); + (void *)lsa, (int) lsa->data->type); ospf_lsa_discard (lsa); Flag = 1; } @@ -1929,7 +1929,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) @@ -2026,7 +2028,7 @@ ospf_ls_upd (struct ip *iph, struct ospf_header *ospfh, quagga_gettime (QUAGGA_CLK_MONOTONIC, &now); if (tv_cmp (tv_sub (now, current->tv_orig), - int2tv (OSPF_MIN_LS_ARRIVAL)) >= 0) + msec2tv (ospf->min_ls_arrival)) >= 0) /* Trap NSSA type later.*/ ospf_ls_upd_send_lsa (nbr, current, OSPF_SEND_PACKET_DIRECT); DISCARD_LSA (lsa, 8); @@ -2089,15 +2091,8 @@ 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) - { -#ifdef HAVE_OPAQUE_LSA - if (IS_OPAQUE_LSA (lsr->data->type)) - ospf_opaque_ls_ack_received (nbr, lsr); -#endif /* HAVE_OPAQUE_LSA */ - - ospf_ls_retransmit_delete (nbr, lsr); - } + if (lsr != NULL && ospf_lsa_more_recent (lsr, lsa) == 0) + ospf_ls_retransmit_delete (nbr, lsr); lsa->data = NULL; ospf_lsa_discard (lsa); @@ -2105,14 +2100,14 @@ 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) { int ret; struct ip *iph; u_int16_t ip_len; - unsigned int ifindex = 0; + ifindex_t ifindex = 0; struct iovec iov; /* Header and data both require alignment. */ char buff [CMSG_SPACE(SOPT_SIZE_CMSG_IFINDEX_IPV4())]; @@ -2468,7 +2463,6 @@ ospf_lsa_examin (struct lsa_header * lsah, const u_int16_t lsalen, const u_char case OSPF_SUMMARY_LSA: case OSPF_ASBR_SUMMARY_LSA: /* RFC2328 A.4.4, LSA header + 4 bytes followed by N>=1 4-bytes TOS blocks */ -#ifdef HAVE_OPAQUE_LSA case OSPF_OPAQUE_LINK_LSA: case OSPF_OPAQUE_AREA_LSA: case OSPF_OPAQUE_AS_LSA: @@ -2476,7 +2470,6 @@ ospf_lsa_examin (struct lsa_header * lsah, const u_int16_t lsalen, const u_char * data) padded to 32-bit alignment." This is considered equivalent * to 4-byte alignment of all other LSA types, see OSPF-ALIGNMENT.txt * file for the detailed analysis of this passage. */ -#endif ret = lsalen % 4 ? MSG_NG : MSG_OK; break; default: @@ -2919,7 +2912,7 @@ ospf_read (struct thread *thread) ospf_ls_req (iph, ospfh, ibuf, oi, length); break; case OSPF_MSG_LS_UPD: - ospf_ls_upd (iph, ospfh, ibuf, oi, length); + ospf_ls_upd (ospf, iph, ospfh, ibuf, oi, length); break; case OSPF_MSG_LS_ACK: ospf_ls_ack (iph, ospfh, ibuf, oi, length); @@ -3029,7 +3022,8 @@ ospf_make_hello (struct ospf_interface *oi, struct stream *s) int flag = 0; /* Set netmask of interface. */ - if (oi->type != OSPF_IFTYPE_POINTOPOINT && + if (!(CHECK_FLAG(oi->connected->flags, ZEBRA_IFA_UNNUMBERED) && + oi->type == OSPF_IFTYPE_POINTOPOINT) && oi->type != OSPF_IFTYPE_VIRTUALLINK) masklen2ip (oi->address->prefixlen, &mask); else @@ -3107,25 +3101,8 @@ ospf_make_db_desc (struct ospf_interface *oi, struct ospf_neighbor *nbr, /* Set Options. */ 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); - } -#endif /* HAVE_OPAQUE_LSA */ + SET_FLAG (options, OSPF_OPTION_O); stream_putc (s, options); /* DD flags */ @@ -3150,7 +3127,6 @@ ospf_make_db_desc (struct ospf_interface *oi, struct ospf_neighbor *nbr, for (rn = route_top (table); rn; rn = route_next (rn)) if ((lsa = rn->info) != NULL) { -#ifdef HAVE_OPAQUE_LSA if (IS_OPAQUE_LSA (lsa->data->type) && (! CHECK_FLAG (options, OSPF_OPTION_O))) { @@ -3159,7 +3135,6 @@ ospf_make_db_desc (struct ospf_interface *oi, struct ospf_neighbor *nbr, ospf_lsdb_delete (lsdb, lsa); continue; } -#endif /* HAVE_OPAQUE_LSA */ if (!CHECK_FLAG (lsa->flags, OSPF_LSA_DISCARD)) { @@ -3295,7 +3270,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 +3676,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); @@ -3823,6 +3799,8 @@ ospf_ls_upd_send (struct ospf_neighbor *nbr, struct list *update, int flag) if (rn->info == NULL) rn->info = list_new (); + else + route_unlock_node (rn); for (ALL_LIST_ELEMENTS_RO (update, node, lsa)) listnode_add (rn->info, ospf_lsa_lock (lsa)); /* oi->ls_upd_queue */ @@ -3853,9 +3831,12 @@ ospf_ls_ack_send_list (struct ospf_interface *oi, struct list *ack, /* Set packet length. */ op->length = length; - /* Set destination IP address. */ - op->dst = dst; - + /* Decide destination address. */ + if (oi->type == OSPF_IFTYPE_POINTOPOINT) + op->dst.s_addr = htonl (OSPF_ALLSPFROUTERS); + else + op->dst.s_addr = dst.s_addr; + /* Add packet to the interface output queue. */ ospf_packet_add (oi, op); diff --git a/ospfd/ospf_ri.c b/ospfd/ospf_ri.c new file mode 100644 index 000000000..f093208e9 --- /dev/null +++ b/ospfd/ospf_ri.c @@ -0,0 +1,1637 @@ +/* + * This is an implementation of RFC4970 Router Information + * with support of RFC5088 PCE Capabilites announcement + * + * Module name: Router Information + * Version: 0.99.22 + * Created: 2012-02-01 by Olivier Dugeon + * Copyright (C) 2012 Orange Labs http://www.orange.com/ + * + * This file is part of GNU Quagga. + * + * 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 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 GNU 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 "linklist.h" +#include "prefix.h" +#include "if.h" +#include "table.h" +#include "memory.h" +#include "command.h" +#include "vty.h" +#include "stream.h" +#include "log.h" +#include "thread.h" +#include "hash.h" +#include "sockunion.h" /* for inet_aton() */ + +#include "ospfd/ospfd.h" +#include "ospfd/ospf_interface.h" +#include "ospfd/ospf_ism.h" +#include "ospfd/ospf_asbr.h" +#include "ospfd/ospf_lsa.h" +#include "ospfd/ospf_lsdb.h" +#include "ospfd/ospf_neighbor.h" +#include "ospfd/ospf_nsm.h" +#include "ospfd/ospf_flood.h" +#include "ospfd/ospf_packet.h" +#include "ospfd/ospf_spf.h" +#include "ospfd/ospf_dump.h" +#include "ospfd/ospf_route.h" +#include "ospfd/ospf_ase.h" +#include "ospfd/ospf_zebra.h" +#include "ospfd/ospf_ri.h" +#include "ospfd/ospf_te.h" + +struct ospf_pce_info +{ + + /* Store Router Information PCE TLV and SubTLV in network byte order. */ + struct ri_tlv_pce pce_header; + struct ri_pce_subtlv_address pce_address; + struct ri_pce_subtlv_path_scope pce_scope; + struct list *pce_domain; + struct list *pce_neighbor; + struct ri_pce_subtlv_cap_flag pce_cap_flag; +}; + +/* Following structure are internal use only. */ +struct ospf_router_info +{ + status_t status; + + u_int8_t registered; + u_int8_t scope; + + /* Flags to manage this router information. */ +#define RIFLG_LOOKUP_DONE 0x1 +#define RIFLG_LSA_ENGAGED 0x2 +#define RIFLG_LSA_FORCED_REFRESH 0x4 + u_int32_t flags; + + /* area pointer if flooding is Type 10 Null if flooding is AS scope */ + struct ospf_area *area; + struct in_addr area_id; + + /* Store Router Information Capabilities LSA */ + struct ri_tlv_router_cap router_cap; + + /* Store PCE capability LSA */ + struct ospf_pce_info pce_info; +}; + +/* + * Global variable to manage Opaque-LSA/Router Information on this node. + * Note that all parameter values are stored in network byte order. + */ +static struct ospf_router_info OspfRI; + +/*------------------------------------------------------------------------------* + * Followings are initialize/terminate functions for Router Information handling. + *------------------------------------------------------------------------------*/ + +static void ospf_router_info_ism_change (struct ospf_interface *oi, + int old_status); +static void ospf_router_info_nsm_change (struct ospf_neighbor *nbr, + int old_status); +static void ospf_router_info_config_write_router (struct vty *vty); +static void ospf_router_info_show_info (struct vty *vty, + struct ospf_lsa *lsa); +static int ospf_router_info_lsa_originate (void *arg); +static struct ospf_lsa *ospf_router_info_lsa_refresh (struct ospf_lsa *lsa); +static void ospf_router_info_lsa_schedule (opcode_t opcode); +static void ospf_router_info_register_vty (void); +static void del_pce_info (void *val); + +int +ospf_router_info_init (void) +{ + + memset (&OspfRI, 0, sizeof (struct ospf_router_info)); + OspfRI.status = disabled; + OspfRI.registered = 0; + OspfRI.scope = OSPF_OPAQUE_AS_LSA; + OspfRI.flags = 0; + + /* Initialize pce domain and neighbor list */ + OspfRI.pce_info.pce_domain = list_new (); + OspfRI.pce_info.pce_domain->del = del_pce_info; + OspfRI.pce_info.pce_neighbor = list_new (); + OspfRI.pce_info.pce_neighbor->del = del_pce_info; + + ospf_router_info_register_vty (); + + return 0; +} + +static int +ospf_router_info_register (u_int8_t scope) +{ + int rc = 0; + + if (OspfRI.registered) + return 0; + + zlog_info ("Register Router Information with scope %s(%d)", + scope == OSPF_OPAQUE_AREA_LSA ? "Area" : "AS", scope); + rc = ospf_register_opaque_functab (scope, + OPAQUE_TYPE_ROUTER_INFORMATION_LSA, + NULL, /* new interface */ + NULL, /* del interface */ + ospf_router_info_ism_change, + ospf_router_info_nsm_change, + ospf_router_info_config_write_router, + NULL, /* Config. write interface */ + NULL, /* Config. write debug */ + ospf_router_info_show_info, + ospf_router_info_lsa_originate, + ospf_router_info_lsa_refresh, + NULL, /* new_lsa_hook */ + NULL); /* del_lsa_hook */ + + if (rc != 0) + { + zlog_warn ("ospf_router_info_init: Failed to register functions"); + return rc; + } + + OspfRI.registered = 1; + OspfRI.scope = scope; + return 0; +} + +static int +ospf_router_info_unregister () +{ + + if ((OspfRI.scope != OSPF_OPAQUE_AS_LSA) + && (OspfRI.scope != OSPF_OPAQUE_AREA_LSA)) + { + zlog_warn ("Unable to unregister Router Info functions: Wrong scope!"); + return -1; + } + + ospf_delete_opaque_functab (OspfRI.scope, + OPAQUE_TYPE_ROUTER_INFORMATION_LSA); + + OspfRI.registered = 0; + return 0; + +} + +void +ospf_router_info_term (void) +{ + + list_delete (OspfRI.pce_info.pce_domain); + list_delete (OspfRI.pce_info.pce_neighbor); + + OspfRI.pce_info.pce_domain = NULL; + OspfRI.pce_info.pce_neighbor = NULL; + OspfRI.status = disabled; + + ospf_router_info_unregister (OspfRI.scope); + + return; +} + +static void +del_pce_info (void *val) +{ + XFREE (MTYPE_OSPF_PCE_PARAMS, val); + return; +} + +/*------------------------------------------------------------------------* + * Followings are control functions for ROUTER INFORMATION parameters management. + *------------------------------------------------------------------------*/ + +static void +set_router_info_capabilities (struct ri_tlv_router_cap *ric, u_int32_t cap) +{ + ric->header.type = htons (RI_TLV_CAPABILITIES); + ric->header.length = htons (RI_TLV_LENGTH); + ric->value = htonl (cap); + return; +} + +static int +set_pce_header (struct ospf_pce_info *pce) +{ + u_int16_t length = 0; + struct listnode *node; + struct ri_pce_subtlv_domain *domain; + struct ri_pce_subtlv_neighbor *neighbor; + + /* PCE Address */ + if (ntohs (pce->pce_address.header.type) != 0) + length += RI_TLV_SIZE (&pce->pce_address.header); + + /* PCE Path Scope */ + if (ntohs (pce->pce_scope.header.type) != 0) + length += RI_TLV_SIZE (&pce->pce_scope.header); + + /* PCE Domain */ + for (ALL_LIST_ELEMENTS_RO (pce->pce_domain, node, domain)) + { + if (ntohs (domain->header.type) != 0) + length += RI_TLV_SIZE (&domain->header); + } + + /* PCE Neighbor */ + for (ALL_LIST_ELEMENTS_RO (pce->pce_neighbor, node, neighbor)) + { + if (ntohs (neighbor->header.type) != 0) + length += RI_TLV_SIZE (&neighbor->header); + } + + /* PCE Capabilities */ + if (ntohs (pce->pce_cap_flag.header.type) != 0) + length += RI_TLV_SIZE (&pce->pce_cap_flag.header); + + if (length != 0) + { + pce->pce_header.header.type = htons (RI_TLV_PCE); + pce->pce_header.header.length = htons (length); + } + else + { + pce->pce_header.header.type = 0; + pce->pce_header.header.length = 0; + } + + return length; +} + +static void +set_pce_address (struct in_addr ipv4, struct ospf_pce_info *pce) +{ + + /* Enable PCE Info */ + pce->pce_header.header.type = htons (RI_TLV_PCE); + /* Set PCE Address */ + pce->pce_address.header.type = htons (RI_PCE_SUBTLV_ADDRESS); + pce->pce_address.header.length = htons (PCE_ADDRESS_LENGTH_IPV4); + pce->pce_address.address.type = htons (PCE_ADDRESS_TYPE_IPV4); + pce->pce_address.address.value = ipv4; + + return; +} + +static void +set_pce_path_scope (u_int32_t scope, struct ospf_pce_info *pce) +{ + + /* Enable PCE Info */ + pce->pce_header.header.type = htons (RI_TLV_PCE); + /* Set PCE Scope */ + pce->pce_scope.header.type = htons (RI_PCE_SUBTLV_PATH_SCOPE); + pce->pce_scope.header.length = htons (RI_TLV_LENGTH); + pce->pce_scope.value = htonl (scope); + + return; +} + +static void +set_pce_domain (u_int16_t type, u_int32_t domain, struct ospf_pce_info *pce) +{ + + struct ri_pce_subtlv_domain *new; + + /* Enable PCE Info */ + pce->pce_header.header.type = htons (RI_TLV_PCE); + + /* Create new domain info */ + new = + XCALLOC (MTYPE_OSPF_PCE_PARAMS, + sizeof (struct ri_pce_subtlv_domain)); + + new->header.type = htons (RI_PCE_SUBTLV_DOMAIN); + new->header.length = htons (PCE_ADDRESS_LENGTH_IPV4); + new->type = htons (type); + new->value = htonl (domain); + + /* Add new domain to the list */ + listnode_add (pce->pce_domain, new); + + return; +} + +static void +unset_pce_domain (u_int16_t type, u_int32_t domain, struct ospf_pce_info *pce) +{ + struct listnode *node; + struct ri_pce_subtlv_domain *old = NULL; + int found = 0; + + /* Search the corresponding node */ + for (ALL_LIST_ELEMENTS_RO (pce->pce_domain, node, old)) + { + if ((old->type == htons (type)) && (old->value == htonl (domain))) + { + found = 1; + break; + } + } + + /* if found remove it */ + if (found) + { + listnode_delete (pce->pce_domain, old); + + /* Avoid misjudgement in the next lookup. */ + if (listcount (pce->pce_domain) == 0) + pce->pce_domain->head = pce->pce_domain->tail = NULL; + + /* Finally free the old domain */ + XFREE (MTYPE_OSPF_PCE_PARAMS, old); + } +} + +static void +set_pce_neighbor (u_int16_t type, u_int32_t domain, struct ospf_pce_info *pce) +{ + + struct ri_pce_subtlv_neighbor *new; + + /* Enable PCE Info */ + pce->pce_header.header.type = htons (RI_TLV_PCE); + + /* Create new neighbor info */ + new = + XCALLOC (MTYPE_OSPF_PCE_PARAMS, + sizeof (struct ri_pce_subtlv_neighbor)); + + new->header.type = htons (RI_PCE_SUBTLV_NEIGHBOR); + new->header.length = htons (PCE_ADDRESS_LENGTH_IPV4); + new->type = htons (type); + new->value = htonl (domain); + + /* Add new domain to the list */ + listnode_add (pce->pce_neighbor, new); + + return; +} + +static void +unset_pce_neighbor (u_int16_t type, u_int32_t domain, + struct ospf_pce_info *pce) +{ + struct listnode *node; + struct ri_pce_subtlv_neighbor *old = NULL; + int found = 0; + + /* Search the corresponding node */ + for (ALL_LIST_ELEMENTS_RO (pce->pce_neighbor, node, old)) + { + if ((old->type == htons (type)) && (old->value == htonl (domain))) + { + found = 1; + break; + } + } + + /* if found remove it */ + if (found) + { + listnode_delete (pce->pce_neighbor, old); + + /* Avoid misjudgement in the next lookup. */ + if (listcount (pce->pce_neighbor) == 0) + pce->pce_neighbor->head = pce->pce_neighbor->tail = NULL; + + /* Finally free the old domain */ + XFREE (MTYPE_OSPF_PCE_PARAMS, old); + } +} + +static void +set_pce_cap_flag (u_int32_t cap, struct ospf_pce_info *pce) +{ + + /* Enable PCE Info */ + pce->pce_header.header.type = htons (RI_TLV_PCE); + /* Set PCE Capabilities flag */ + pce->pce_cap_flag.header.type = htons (RI_PCE_SUBTLV_CAP_FLAG); + pce->pce_cap_flag.header.length = htons (RI_TLV_LENGTH); + pce->pce_cap_flag.value = htonl (cap); + + return; +} + + +static void +unset_param (struct ri_tlv_header *tlv) +{ + + tlv->type = 0; + /* Fill the Value to 0 */ + memset ((tlv + RI_TLV_HDR_SIZE), 0, RI_TLV_BODY_SIZE (tlv)); + tlv->length = 0; + + return; +} + +static void +initialize_params (struct ospf_router_info *ori) +{ + u_int32_t cap; + struct ospf *top; + + /* + * Initialize default Router Information Capabilities. + */ + cap = 0; + cap = cap | RI_TE_SUPPORT; + + set_router_info_capabilities (&ori->router_cap, cap); + + /* If Area address is not null and exist, retrieve corresponding structure */ + top = ospf_lookup (); + zlog_info ("RI-> Initialize Router Info for %s scope within area %s", + OspfRI.scope == OSPF_OPAQUE_AREA_LSA ? "Area" : "AS", + inet_ntoa (OspfRI.area_id)); + + /* Try to get the Area context at this step. Do it latter if not available */ + if ((OspfRI.scope == OSPF_OPAQUE_AREA_LSA) && (OspfRI.area == NULL)) + OspfRI.area = ospf_area_lookup_by_area_id (top, OspfRI.area_id); + + /* + * Initialize default PCE Information values + */ + /* PCE address == OSPF Router ID */ + set_pce_address (top->router_id, &ori->pce_info); + + /* PCE scope */ + cap = 7; /* Set L, R and Rd bits to one = intra & inter-area path computation */ + set_pce_path_scope (cap, &ori->pce_info); + + /* PCE Capabilities */ + cap = + PCE_CAP_BIDIRECTIONAL | PCE_CAP_DIVERSE_PATH | PCE_CAP_OBJECTIVES | + PCE_CAP_ADDITIVE | PCE_CAP_MULTIPLE_REQ; + set_pce_cap_flag (cap, &ori->pce_info); + + /* Finally compute PCE header */ + set_pce_header (&ori->pce_info); + + return; +} + +static int +is_mandated_params_set (struct ospf_router_info ori) +{ + int rc = 0; + + if (ntohs (ori.router_cap.header.type) == 0) + goto out; + + if ((ntohs (ori.pce_info.pce_header.header.type) == RI_TLV_PCE) + && (ntohs (ori.pce_info.pce_address.header.type) == 0) + && (ntohs (ori.pce_info.pce_cap_flag.header.type) == 0)) + goto out; + + rc = 1; + +out: + return rc; +} + +/*------------------------------------------------------------------------* + * Followings are callback functions against generic Opaque-LSAs handling. + *------------------------------------------------------------------------*/ +static void +ospf_router_info_ism_change (struct ospf_interface *oi, int old_state) +{ + /* So far, nothing to do here. */ + return; + +} + +static void +ospf_router_info_nsm_change (struct ospf_neighbor *nbr, int old_state) +{ + + /* So far, nothing to do here. */ + return; +} + +/*------------------------------------------------------------------------* + * Followings are OSPF protocol processing functions for ROUTER INFORMATION + *------------------------------------------------------------------------*/ + +static void +build_tlv_header (struct stream *s, struct ri_tlv_header *tlvh) +{ + + stream_put (s, tlvh, sizeof (struct ri_tlv_header)); + return; +} + +static void +build_tlv (struct stream *s, struct ri_tlv_header *tlvh) +{ + + if (ntohs (tlvh->type) != 0) + { + build_tlv_header (s, tlvh); + stream_put (s, tlvh + 1, RI_TLV_BODY_SIZE (tlvh)); + } + return; +} + +static void +ospf_router_info_lsa_body_set (struct stream *s) +{ + + struct listnode *node; + struct ri_pce_subtlv_domain *domain; + struct ri_pce_subtlv_neighbor *neighbor; + + /* Build Router Information TLV */ + build_tlv (s, &OspfRI.router_cap.header); + + /* Add RI PCE TLV if it is set */ + /* Compute PCE Info header first */ + if ((set_pce_header (&OspfRI.pce_info)) != 0) + { + + /* Build PCE TLV */ + build_tlv_header (s, &OspfRI.pce_info.pce_header.header); + + /* Build PCE address sub-tlv */ + build_tlv (s, &OspfRI.pce_info.pce_address.header); + + /* Build PCE path scope sub-tlv */ + build_tlv (s, &OspfRI.pce_info.pce_scope.header); + + /* Build PCE domain sub-tlv */ + for (ALL_LIST_ELEMENTS_RO (OspfRI.pce_info.pce_domain, node, domain)) + build_tlv (s, &domain->header); + + /* Build PCE neighbor sub-tlv */ + for (ALL_LIST_ELEMENTS_RO + (OspfRI.pce_info.pce_neighbor, node, neighbor)) + build_tlv (s, &neighbor->header); + + /* Build PCE cap flag sub-tlv */ + build_tlv (s, &OspfRI.pce_info.pce_cap_flag.header); + } + + return; +} + +/* Create new opaque-LSA. */ +static struct ospf_lsa * +ospf_router_info_lsa_new () +{ + struct ospf *top; + struct stream *s; + struct lsa_header *lsah; + struct ospf_lsa *new = NULL; + u_char options, lsa_type; + struct in_addr lsa_id; + u_int32_t tmp; + u_int16_t length; + + /* Create a stream for LSA. */ + if ((s = stream_new (OSPF_MAX_LSA_SIZE)) == NULL) + { + zlog_warn ("ospf_router_info_lsa_new: stream_new() ?"); + goto out; + } + lsah = (struct lsa_header *) STREAM_DATA (s); + + options = OSPF_OPTION_E; /* Enable AS external as we flood RI with Opaque Type 11 */ + options |= OSPF_OPTION_O; /* Don't forget this :-) */ + + lsa_type = OspfRI.scope; + /* LSA ID == 0 for Router Information see RFC 4970 */ + tmp = SET_OPAQUE_LSID (OPAQUE_TYPE_ROUTER_INFORMATION_LSA, 0); + lsa_id.s_addr = htonl (tmp); + + if (IS_DEBUG_OSPF (lsa, LSA_GENERATE)) + zlog_debug + ("LSA[Type%d:%s]: Create an Opaque-LSA/ROUTER INFORMATION instance", + lsa_type, inet_ntoa (lsa_id)); + + top = ospf_lookup (); + + /* Set opaque-LSA header fields. */ + lsa_header_set (s, options, lsa_type, lsa_id, top->router_id); + + /* Set opaque-LSA body fields. */ + ospf_router_info_lsa_body_set (s); + + /* Set length. */ + length = stream_get_endp (s); + lsah->length = htons (length); + + /* Now, create an OSPF LSA instance. */ + if ((new = ospf_lsa_new ()) == NULL) + { + zlog_warn ("ospf_router_info_lsa_new: ospf_lsa_new() ?"); + stream_free (s); + goto out; + } + if ((new->data = ospf_lsa_data_new (length)) == NULL) + { + zlog_warn ("ospf_router_info_lsa_new: ospf_lsa_data_new() ?"); + ospf_lsa_unlock (&new); + new = NULL; + stream_free (s); + goto out; + } + + new->area = OspfRI.area; /* Area must be null if the Opaque type is AS scope, fulfill otherwise */ + + SET_FLAG (new->flags, OSPF_LSA_SELF); + memcpy (new->data, lsah, length); + stream_free (s); + +out:return new; +} + +static int +ospf_router_info_lsa_originate1 (void *arg) +{ + struct ospf_lsa *new; + struct ospf *top; + struct ospf_area *area; + int rc = -1; + + /* First check if the area is known if flooding scope is Area */ + if (OspfRI.scope == OSPF_OPAQUE_AREA_LSA) + { + area = (struct ospf_area *) arg; + if (area->area_id.s_addr != OspfRI.area_id.s_addr) + { + zlog_debug + ("RI -> This is not the Router Information Area. Stop processing"); + goto out; + } + OspfRI.area = area; + } + + /* Create new Opaque-LSA/ROUTER INFORMATION instance. */ + if ((new = ospf_router_info_lsa_new ()) == NULL) + { + zlog_warn + ("ospf_router_info_lsa_originate1: ospf_router_info_lsa_new() ?"); + goto out; + } + + /* Get ospf info */ + top = ospf_lookup (); + + /* Install this LSA into LSDB. */ + if (ospf_lsa_install (top, NULL /*oi */ , new) == NULL) + { + zlog_warn ("ospf_router_info_lsa_originate1: ospf_lsa_install() ?"); + ospf_lsa_unlock (&new); + goto out; + } + + /* Now this Router Info parameter entry has associated LSA. */ + SET_FLAG (OspfRI.flags, RIFLG_LSA_ENGAGED); + + /* Update new LSA origination count. */ + top->lsa_originate_count++; + + /* Flood new LSA through AS. */ + if (OspfRI.scope == OSPF_OPAQUE_AS_LSA) + ospf_flood_through_as (top, NULL /*nbr */ , new); + else + ospf_flood_through_area (OspfRI.area, NULL /*nbr */ , new); + + if (IS_DEBUG_OSPF (lsa, LSA_GENERATE)) + { + zlog_debug ("LSA[Type%d:%s]: Originate Opaque-LSA/ROUTER INFORMATION", + new->data->type, inet_ntoa (new->data->id)); + ospf_lsa_header_dump (new->data); + } + + rc = 0; +out:return rc; +} + +static int +ospf_router_info_lsa_originate (void *arg) +{ + + int rc = -1; + + if (OspfRI.status == disabled) + { + zlog_info + ("ospf_router_info_lsa_originate: ROUTER INFORMATION is disabled now."); + rc = 0; /* This is not an error case. */ + goto out; + } + + /* Check if Router Information LSA is already engaged */ + if (OspfRI.flags & RIFLG_LSA_ENGAGED) + { + if (OspfRI.flags & RIFLG_LSA_FORCED_REFRESH) + { + OspfRI.flags &= ~RIFLG_LSA_FORCED_REFRESH; + ospf_router_info_lsa_schedule (REFRESH_THIS_LSA); + } + } + else + { + if (!is_mandated_params_set (OspfRI)) + zlog_warn + ("ospf_router_info_lsa_originate: lacks mandated ROUTER INFORMATION parameters"); + + /* Ok, let's try to originate an LSA */ + if (ospf_router_info_lsa_originate1 (arg) != 0) + goto out; + } + + rc = 0; +out:return rc; +} + +static struct ospf_lsa * +ospf_router_info_lsa_refresh (struct ospf_lsa *lsa) +{ + struct ospf_lsa *new = NULL; + struct ospf *top; + + if (OspfRI.status == disabled) + { + /* + * This LSA must have flushed before due to ROUTER INFORMATION status change. + * It seems a slip among routers in the routing domain. + */ + zlog_info + ("ospf_router_info_lsa_refresh: ROUTER INFORMATION is disabled now."); + lsa->data->ls_age = htons (OSPF_LSA_MAXAGE); /* Flush it anyway. */ + } + + /* Verify that the Router Information ID is supported */ + if (GET_OPAQUE_ID (ntohl (lsa->data->id.s_addr)) != 0) + { + zlog_warn + ("ospf_router_info_lsa_refresh: Unsupported Router Information ID"); + goto out; + } + + /* If the lsa's age reached to MaxAge, start flushing procedure. */ + if (IS_LSA_MAXAGE (lsa)) + { + OspfRI.flags &= ~RIFLG_LSA_ENGAGED; + ospf_opaque_lsa_flush_schedule (lsa); + goto out; + } + + /* Create new Opaque-LSA/ROUTER INFORMATION instance. */ + if ((new = ospf_router_info_lsa_new ()) == NULL) + { + zlog_warn + ("ospf_router_info_lsa_refresh: ospf_router_info_lsa_new() ?"); + goto out; + } + new->data->ls_seqnum = lsa_seqnum_increment (lsa); + + /* Install this LSA into LSDB. */ + /* Given "lsa" will be freed in the next function. */ + top = ospf_lookup (); + if (ospf_lsa_install (top, NULL /*oi */ , new) == NULL) + { + zlog_warn ("ospf_router_info_lsa_refresh: ospf_lsa_install() ?"); + ospf_lsa_unlock (&new); + goto out; + } + + /* Flood updated LSA through AS or AREA depending of OspfRI.scope. */ + if (OspfRI.scope == OSPF_OPAQUE_AS_LSA) + ospf_flood_through_as (top, NULL /*nbr */ , new); + else + ospf_flood_through_area (OspfRI.area, NULL /*nbr */ , new); + + /* Debug logging. */ + if (IS_DEBUG_OSPF (lsa, LSA_GENERATE)) + { + zlog_debug ("LSA[Type%d:%s]: Refresh Opaque-LSA/ROUTER INFORMATION", + new->data->type, inet_ntoa (new->data->id)); + ospf_lsa_header_dump (new->data); + } + +out:return new; +} + +static void +ospf_router_info_lsa_schedule (opcode_t opcode) +{ + struct ospf_lsa lsa; + struct lsa_header lsah; + struct ospf *top; + u_int32_t tmp; + + memset (&lsa, 0, sizeof (lsa)); + memset (&lsah, 0, sizeof (lsah)); + + zlog_debug ("RI-> LSA schedule %s%s%s", + opcode == REORIGINATE_THIS_LSA ? "Re-Originate" : "", + opcode == REFRESH_THIS_LSA ? "Refresh" : "", + opcode == FLUSH_THIS_LSA ? "Flush" : ""); + + top = ospf_lookup (); + if ((OspfRI.scope == OSPF_OPAQUE_AREA_LSA) && (OspfRI.area == NULL)) + { + zlog_warn + ("ospf_router_info_lsa_schedule(): Router Info is Area scope flooding but area is not set"); + OspfRI.area = ospf_area_lookup_by_area_id (top, OspfRI.area_id); + } + lsa.area = OspfRI.area; + lsa.data = &lsah; + lsah.type = OspfRI.scope; + + /* LSA ID is set to 0 for the Router Information. See RFC 4970 */ + tmp = SET_OPAQUE_LSID (OPAQUE_TYPE_ROUTER_INFORMATION_LSA, 0); + lsah.id.s_addr = htonl (tmp); + + switch (opcode) + { + case REORIGINATE_THIS_LSA: + if (OspfRI.scope == OSPF_OPAQUE_AREA_LSA) + ospf_opaque_lsa_reoriginate_schedule ((void *) OspfRI.area, + OSPF_OPAQUE_AREA_LSA, + OPAQUE_TYPE_ROUTER_INFORMATION_LSA); + else + ospf_opaque_lsa_reoriginate_schedule ((void *) top, + OSPF_OPAQUE_AS_LSA, + OPAQUE_TYPE_ROUTER_INFORMATION_LSA); + break; + case REFRESH_THIS_LSA: + ospf_opaque_lsa_refresh_schedule (&lsa); + break; + case FLUSH_THIS_LSA: + OspfRI.flags &= ~RIFLG_LSA_ENGAGED; + ospf_opaque_lsa_flush_schedule (&lsa); + break; + default: + zlog_warn ("ospf_router_info_lsa_schedule: Unknown opcode (%u)", + opcode); + break; + } + + return; +} + +/*------------------------------------------------------------------------* + * Followings are vty session control functions. + *------------------------------------------------------------------------*/ + +static u_int16_t +show_vty_router_cap (struct vty *vty, struct ri_tlv_header *tlvh) +{ + struct ri_tlv_router_cap *top = (struct ri_tlv_router_cap *) tlvh; + + if (vty != NULL) + vty_out (vty, " Router Capabilities: 0x%x%s", ntohl (top->value), + VTY_NEWLINE); + else + zlog_debug (" Router Capabilities: 0x%x", ntohl (top->value)); + + return RI_TLV_SIZE (tlvh); +} + +static u_int16_t +show_vty_pce_subtlv_address (struct vty *vty, struct ri_tlv_header *tlvh) +{ + struct ri_pce_subtlv_address *top = (struct ri_pce_subtlv_address *) tlvh; + + if (ntohs (top->address.type) == PCE_ADDRESS_TYPE_IPV4) + { + if (vty != NULL) + vty_out (vty, " PCE Address: %s%s", inet_ntoa (top->address.value), + VTY_NEWLINE); + else + zlog_debug (" PCE Address: %s", inet_ntoa (top->address.value)); + } + else + { + /* TODO: Add support to IPv6 with inet_ntop() */ + if (vty != NULL) + vty_out (vty, " PCE Address: 0x%x%s", + ntohl (top->address.value.s_addr), VTY_NEWLINE); + else + zlog_debug (" PCE Address: 0x%x", + ntohl (top->address.value.s_addr)); + } + + return RI_TLV_SIZE (tlvh); +} + +static u_int16_t +show_vty_pce_subtlv_path_scope (struct vty *vty, struct ri_tlv_header *tlvh) +{ + struct ri_pce_subtlv_path_scope *top = + (struct ri_pce_subtlv_path_scope *) tlvh; + + if (vty != NULL) + vty_out (vty, " PCE Path Scope: 0x%x%s", ntohl (top->value), + VTY_NEWLINE); + else + zlog_debug (" PCE Path Scope: 0x%x", ntohl (top->value)); + + return RI_TLV_SIZE (tlvh); +} + +static u_int16_t +show_vty_pce_subtlv_domain (struct vty *vty, struct ri_tlv_header *tlvh) +{ + struct ri_pce_subtlv_domain *top = (struct ri_pce_subtlv_domain *) tlvh; + struct in_addr tmp; + + if (ntohs (top->type) == PCE_DOMAIN_TYPE_AREA) + { + tmp.s_addr = top->value; + if (vty != NULL) + vty_out (vty, " PCE domain Area: %s%s", inet_ntoa (tmp), + VTY_NEWLINE); + else + zlog_debug (" PCE domain Area: %s", inet_ntoa (tmp)); + } + else + { + if (vty != NULL) + vty_out (vty, " PCE domain AS: %d%s", ntohl (top->value), + VTY_NEWLINE); + else + zlog_debug (" PCE domain AS: %d", ntohl (top->value)); + } + return RI_TLV_SIZE (tlvh); +} + +static u_int16_t +show_vty_pce_subtlv_neighbor (struct vty *vty, struct ri_tlv_header *tlvh) +{ + + struct ri_pce_subtlv_neighbor *top = (struct ri_pce_subtlv_neighbor *) tlvh; + struct in_addr tmp; + + if (ntohs (top->type) == PCE_DOMAIN_TYPE_AREA) + { + tmp.s_addr = top->value; + if (vty != NULL) + vty_out (vty, " PCE neighbor Area: %s%s", inet_ntoa (tmp), + VTY_NEWLINE); + else + zlog_debug (" PCE neighbor Area: %s", inet_ntoa (tmp)); + } + else + { + if (vty != NULL) + vty_out (vty, " PCE neighbor AS: %d%s", ntohl (top->value), + VTY_NEWLINE); + else + zlog_debug (" PCE neighbor AS: %d", ntohl (top->value)); + } + return RI_TLV_SIZE (tlvh); +} + +static u_int16_t +show_vty_pce_subtlv_cap_flag (struct vty *vty, struct ri_tlv_header *tlvh) +{ + struct ri_pce_subtlv_cap_flag *top = (struct ri_pce_subtlv_cap_flag *) tlvh; + + if (vty != NULL) + vty_out (vty, " PCE Capabilities Flag: 0x%x%s", ntohl (top->value), + VTY_NEWLINE); + else + zlog_debug (" PCE Capabilities Flag: 0x%x", ntohl (top->value)); + + return RI_TLV_SIZE (tlvh); +} + +static u_int16_t +show_vty_unknown_tlv (struct vty *vty, struct ri_tlv_header *tlvh) +{ + if (vty != NULL) + vty_out (vty, " Unknown TLV: [type(0x%x), length(0x%x)]%s", + ntohs (tlvh->type), ntohs (tlvh->length), VTY_NEWLINE); + else + zlog_debug (" Unknown TLV: [type(0x%x), length(0x%x)]", + ntohs (tlvh->type), ntohs (tlvh->length)); + + return RI_TLV_SIZE (tlvh); +} + +static u_int16_t +show_vty_pce_info (struct vty *vty, struct ri_tlv_header *ri, uint32_t total) +{ + struct ri_tlv_header *tlvh; + u_int16_t sum = 0; + + for (tlvh = ri; sum < total; tlvh = RI_TLV_HDR_NEXT (tlvh)) + { + switch (ntohs (tlvh->type)) + { + case RI_PCE_SUBTLV_ADDRESS: + sum += show_vty_pce_subtlv_address (vty, tlvh); + break; + case RI_PCE_SUBTLV_PATH_SCOPE: + sum += show_vty_pce_subtlv_path_scope (vty, tlvh); + break; + case RI_PCE_SUBTLV_DOMAIN: + sum += show_vty_pce_subtlv_domain (vty, tlvh); + break; + case RI_PCE_SUBTLV_NEIGHBOR: + sum += show_vty_pce_subtlv_neighbor (vty, tlvh); + break; + case RI_PCE_SUBTLV_CAP_FLAG: + sum += show_vty_pce_subtlv_cap_flag (vty, tlvh); + break; + default: + sum += show_vty_unknown_tlv (vty, tlvh); + break; + } + } + return sum; +} + +static void +ospf_router_info_show_info (struct vty *vty, struct ospf_lsa *lsa) +{ + struct lsa_header *lsah = (struct lsa_header *) lsa->data; + struct ri_tlv_header *tlvh; + u_int16_t length = 0, sum = 0; + + /* Initialize TLV browsing */ + length = ntohs (lsah->length) - OSPF_LSA_HEADER_SIZE; + + for (tlvh = RI_TLV_HDR_TOP (lsah); sum < length; + tlvh = RI_TLV_HDR_NEXT (tlvh)) + { + switch (ntohs (tlvh->type)) + { + case RI_TLV_CAPABILITIES: + sum += show_vty_router_cap (vty, tlvh); + break; + case RI_TLV_PCE: + tlvh++; + sum += RI_TLV_HDR_SIZE; + sum += show_vty_pce_info (vty, tlvh, length - sum); + break; + default: + sum += show_vty_unknown_tlv (vty, tlvh); + break; + } + } + + return; +} + +static void +ospf_router_info_config_write_router (struct vty *vty) +{ + struct ospf_pce_info *pce = &OspfRI.pce_info; + struct listnode *node; + struct ri_pce_subtlv_domain *domain; + struct ri_pce_subtlv_neighbor *neighbor; + struct in_addr tmp; + + if (OspfRI.status == enabled) + { + if (OspfRI.scope == OSPF_OPAQUE_AS_LSA) + vty_out (vty, " router-info as%s", VTY_NEWLINE); + else + vty_out (vty, " router-info area %s%s", inet_ntoa (OspfRI.area_id), + VTY_NEWLINE); + + if (pce->pce_address.header.type != 0) + vty_out (vty, " pce address %s%s", + inet_ntoa (pce->pce_address.address.value), VTY_NEWLINE); + + if (pce->pce_cap_flag.header.type != 0) + vty_out (vty, " pce flag 0x%x%s", ntohl (pce->pce_cap_flag.value), + VTY_NEWLINE); + + for (ALL_LIST_ELEMENTS_RO (pce->pce_domain, node, domain)) + { + if (domain->header.type != 0) + { + if (domain->type == PCE_DOMAIN_TYPE_AREA) + { + tmp.s_addr = domain->value; + vty_out (vty, " pce domain area %s%s", inet_ntoa (tmp), + VTY_NEWLINE); + } + else + { + vty_out (vty, " pce domain as %d%s", ntohl (domain->value), + VTY_NEWLINE); + } + } + } + + for (ALL_LIST_ELEMENTS_RO (pce->pce_neighbor, node, neighbor)) + { + if (neighbor->header.type != 0) + { + if (neighbor->type == PCE_DOMAIN_TYPE_AREA) + { + tmp.s_addr = neighbor->value; + vty_out (vty, " pce neighbor area %s%s", inet_ntoa (tmp), + VTY_NEWLINE); + } + else + { + vty_out (vty, " pce neighbor as %d%s", + ntohl (neighbor->value), VTY_NEWLINE); + } + } + } + + if (pce->pce_scope.header.type != 0) + vty_out (vty, " pce scope 0x%x%s", + ntohl (OspfRI.pce_info.pce_scope.value), VTY_NEWLINE); + } + return; +} + +/*------------------------------------------------------------------------* + * Followings are vty command functions. + *------------------------------------------------------------------------*/ + +DEFUN (router_info, + router_info_area_cmd, + "router-info area A.B.C.D", + OSPF_RI_STR + "Enable the Router Information functionality with Area flooding scope\n" + "OSPF area ID in IP format") +{ + + u_int8_t scope; + + if (OspfRI.status == enabled) + return CMD_SUCCESS; + + /* Check and get Area value if present */ + if (argc == 1) + { + if (!inet_aton (argv[0], &OspfRI.area_id)) + { + vty_out (vty, "Please specify Router Info Area by A.B.C.D%s", + VTY_NEWLINE); + return CMD_WARNING; + } + scope = OSPF_OPAQUE_AREA_LSA; + } + else + { + OspfRI.area_id.s_addr = 0; + scope = OSPF_OPAQUE_AS_LSA; + } + + /* First start to register Router Information callbacks */ + if ((ospf_router_info_register (scope)) != 0) + { + zlog_warn ("Enable to register Router Information callbacks. Abort!"); + return CMD_WARNING; + } + + OspfRI.status = enabled; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("RI-> Router Information (%s flooding): OFF -> ON", + OspfRI.scope == OSPF_OPAQUE_AREA_LSA ? "Area" : "AS"); + + /* + * Following code is intended to handle two cases; + * + * 1) Router Information was disabled at startup time, but now become enabled. + * 2) Router Information was once enabled then disabled, and now enabled again. + */ + + initialize_params (&OspfRI); + + /* Refresh RI LSA if already engaged */ + if (OspfRI.flags & RIFLG_LSA_ENGAGED) + { + zlog_debug ("RI-> Initial origination following configuration"); + ospf_router_info_lsa_schedule (REORIGINATE_THIS_LSA); + } + return CMD_SUCCESS; + +} + +ALIAS (router_info, + router_info_as_cmd, + "router-info as", + OSPF_RI_STR + "Enable the Router Information functionality with AS flooding scope\n") + +DEFUN (no_router_info, + no_router_info_cmd, + "no router-info", + NO_STR + "Disable the Router Information functionality\n") +{ + + if (OspfRI.status == disabled) + return CMD_SUCCESS; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("RI-> Router Information: ON -> OFF"); + + if (OspfRI.flags & RIFLG_LSA_ENGAGED) + ospf_router_info_lsa_schedule (FLUSH_THIS_LSA); + + /* Unregister the callbacks */ + ospf_router_info_unregister (); + + OspfRI.status = disabled; + + return CMD_SUCCESS; +} + +DEFUN (pce_address, + pce_address_cmd, + "pce address A.B.C.D", + PCE_STR + "Stable IP address of the PCE\n" + "PCE address in IPv4 address format\n") +{ + struct in_addr value; + struct ospf_pce_info *pi = &OspfRI.pce_info; + + if (!inet_aton (argv[0], &value)) + { + vty_out (vty, "Please specify PCE Address by A.B.C.D%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (ntohs (pi->pce_address.header.type) == 0 + || ntohl (pi->pce_address.address.value.s_addr) != ntohl (value.s_addr)) + { + + set_pce_address (value, pi); + + /* Refresh RI LSA if already engaged */ + if ((OspfRI.status == enabled) && (OspfRI.flags & RIFLG_LSA_ENGAGED)) + ospf_router_info_lsa_schedule (REFRESH_THIS_LSA); + } + + return CMD_SUCCESS; +} + +DEFUN (no_pce_address, + no_pce_address_cmd, + "no pce address", + NO_STR + PCE_STR + "Disable PCE address\n") +{ + + unset_param (&OspfRI.pce_info.pce_address.header); + + /* Refresh RI LSA if already engaged */ + if ((OspfRI.status == enabled) && (OspfRI.flags & RIFLG_LSA_ENGAGED)) + ospf_router_info_lsa_schedule (REFRESH_THIS_LSA); + + return CMD_SUCCESS; +} + +DEFUN (pce_path_scope, + pce_path_scope_cmd, + "pce scope BITPATTERN", + PCE_STR + "Path scope visibilities of the PCE for path computation\n" + "32-bit Hexadecimal value\n") +{ + uint32_t scope; + struct ospf_pce_info *pi = &OspfRI.pce_info; + + if (sscanf (argv[0], "0x%x", &scope) != 1) + { + vty_out (vty, "pce_path_scope: fscanf: %s%s", safe_strerror (errno), + VTY_NEWLINE); + return CMD_WARNING; + } + + if (ntohl (pi->pce_scope.header.type) == 0 || scope != pi->pce_scope.value) + { + set_pce_path_scope (scope, pi); + + /* Refresh RI LSA if already engaged */ + if ((OspfRI.status == enabled) && (OspfRI.flags & RIFLG_LSA_ENGAGED)) + ospf_router_info_lsa_schedule (REFRESH_THIS_LSA); + } + + return CMD_SUCCESS; +} + +DEFUN (no_pce_path_scope, + no_pce_path_scope_cmd, + "no pce scope", + NO_STR + PCE_STR + "Disable PCE path scope\n") +{ + + unset_param (&OspfRI.pce_info.pce_address.header); + + /* Refresh RI LSA if already engaged */ + if ((OspfRI.status == enabled) && (OspfRI.flags & RIFLG_LSA_ENGAGED)) + ospf_router_info_lsa_schedule (REFRESH_THIS_LSA); + + return CMD_SUCCESS; +} + +DEFUN (pce_domain, + pce_domain_cmd, + "pce domain as <0-65535>", + PCE_STR + "Configure PCE domain AS number\n" + "AS number where the PCE as visibilities for path computation\n" + "AS number in decimal <0-65535>\n") +{ + + uint32_t as; + struct ospf_pce_info *pce = &OspfRI.pce_info; + struct listnode *node; + struct ri_pce_subtlv_domain *domain; + + if (sscanf (argv[0], "%d", &as) != 1) + { + vty_out (vty, "pce_domain: fscanf: %s%s", safe_strerror (errno), + VTY_NEWLINE); + return CMD_WARNING; + } + + /* Check if the domain is not already in the domain list */ + for (ALL_LIST_ELEMENTS_RO (pce->pce_domain, node, domain)) + { + if (ntohl (domain->header.type) == 0 && as == domain->value) + goto out; + } + + /* Create new domain if not found */ + set_pce_domain (PCE_DOMAIN_TYPE_AS, as, pce); + + /* Refresh RI LSA if already engaged */ + if ((OspfRI.status == enabled) && (OspfRI.flags & RIFLG_LSA_ENGAGED)) + ospf_router_info_lsa_schedule (REFRESH_THIS_LSA); + +out:return CMD_SUCCESS; +} + +DEFUN (no_pce_domain, + no_pce_domain_cmd, + "no pce domain as <0-65535>", + NO_STR + PCE_STR + "Disable PCE domain AS number\n" + "AS number where the PCE as visibilities for path computation\n" + "AS number in decimal <0-65535>\n") +{ + + uint32_t as; + struct ospf_pce_info *pce = &OspfRI.pce_info; + + if (sscanf (argv[0], "%d", &as) != 1) + { + vty_out (vty, "no_pce_domain: fscanf: %s%s", safe_strerror (errno), + VTY_NEWLINE); + return CMD_WARNING; + } + + /* Unset corresponding PCE domain */ + unset_pce_domain (PCE_DOMAIN_TYPE_AS, as, pce); + + /* Refresh RI LSA if already engaged */ + if ((OspfRI.status == enabled) && (OspfRI.flags & RIFLG_LSA_ENGAGED)) + ospf_router_info_lsa_schedule (REFRESH_THIS_LSA); + + return CMD_SUCCESS; +} + +DEFUN (pce_neigbhor, + pce_neighbor_cmd, + "pce neighbor as <0-65535>", + PCE_STR + "Configure PCE neighbor domain AS number\n" + "AS number of PCE neighbors\n" + "AS number in decimal <0-65535>\n") +{ + + uint32_t as; + struct ospf_pce_info *pce = &OspfRI.pce_info; + struct listnode *node; + struct ri_pce_subtlv_neighbor *neighbor; + + if (sscanf (argv[0], "%d", &as) != 1) + { + vty_out (vty, "pce_neighbor: fscanf: %s%s", safe_strerror (errno), + VTY_NEWLINE); + return CMD_WARNING; + } + + /* Check if the domain is not already in the domain list */ + for (ALL_LIST_ELEMENTS_RO (pce->pce_neighbor, node, neighbor)) + { + if (ntohl (neighbor->header.type) == 0 && as == neighbor->value) + goto out; + } + + /* Create new domain if not found */ + set_pce_neighbor (PCE_DOMAIN_TYPE_AS, as, pce); + + /* Refresh RI LSA if already engaged */ + if ((OspfRI.status == enabled) && (OspfRI.flags & RIFLG_LSA_ENGAGED)) + ospf_router_info_lsa_schedule (REFRESH_THIS_LSA); + +out:return CMD_SUCCESS; +} + +DEFUN (no_pce_neighbor, + no_pce_neighbor_cmd, + "no pce neighbor as <0-65535>", + NO_STR + PCE_STR + "Disable PCE neighbor AS number\n" + "AS number of PCE neighbor\n" + "AS number in decimal <0-65535>\n") +{ + + uint32_t as; + struct ospf_pce_info *pce = &OspfRI.pce_info; + + if (sscanf (argv[0], "%d", &as) != 1) + { + vty_out (vty, "no_pce_neighbor: fscanf: %s%s", safe_strerror (errno), + VTY_NEWLINE); + return CMD_WARNING; + } + + /* Unset corresponding PCE domain */ + unset_pce_neighbor (PCE_DOMAIN_TYPE_AS, as, pce); + + /* Refresh RI LSA if already engaged */ + if ((OspfRI.status == enabled) && (OspfRI.flags & RIFLG_LSA_ENGAGED)) + ospf_router_info_lsa_schedule (REFRESH_THIS_LSA); + + return CMD_SUCCESS; +} + +DEFUN (pce_cap_flag, + pce_cap_flag_cmd, + "pce flag BITPATTERN", + PCE_STR + "Capabilities of the PCE for path computation\n" + "32-bit Hexadecimal value\n") +{ + + uint32_t cap; + struct ospf_pce_info *pce = &OspfRI.pce_info; + + if (sscanf (argv[0], "0x%x", &cap) != 1) + { + vty_out (vty, "pce_cap_flag: fscanf: %s%s", safe_strerror (errno), + VTY_NEWLINE); + return CMD_WARNING; + } + + if (ntohl (pce->pce_cap_flag.header.type) == 0 + || cap != pce->pce_cap_flag.value) + { + set_pce_cap_flag (cap, pce); + + /* Refresh RI LSA if already engaged */ + if ((OspfRI.status == enabled) && (OspfRI.flags & RIFLG_LSA_ENGAGED)) + ospf_router_info_lsa_schedule (REFRESH_THIS_LSA); + } + + return CMD_SUCCESS; +} + +DEFUN (no_pce_cap_flag, + no_pce_cap_flag_cmd, + "no pce flag", + NO_STR + PCE_STR + "Disable PCE capabilities\n") +{ + + unset_param (&OspfRI.pce_info.pce_cap_flag.header); + + /* Refresh RI LSA if already engaged */ + if ((OspfRI.status == enabled) && (OspfRI.flags & RIFLG_LSA_ENGAGED)) + ospf_router_info_lsa_schedule (REFRESH_THIS_LSA); + + return CMD_SUCCESS; +} + +DEFUN (show_ip_ospf_router_info, + show_ip_ospf_router_info_cmd, + "show ip ospf router-info", + SHOW_STR + IP_STR + OSPF_STR + "Router Information\n") +{ + + if (OspfRI.status == enabled) + { + vty_out (vty, "--- Router Information parameters ---%s", VTY_NEWLINE); + show_vty_router_cap (vty, &OspfRI.router_cap.header); + } + else + { + if (vty != NULL) + vty_out (vty, " Router Information is disabled on this router%s", VTY_NEWLINE); + } + return CMD_SUCCESS; +} + +DEFUN (show_ip_opsf_router_info_pce, + show_ip_ospf_router_info_pce_cmd, + "show ip ospf router-info pce", + SHOW_STR + IP_STR + OSPF_STR + "Router Information\n" + "PCE information\n") +{ + + struct ospf_pce_info *pce = &OspfRI.pce_info; + struct listnode *node; + struct ri_pce_subtlv_domain *domain; + struct ri_pce_subtlv_neighbor *neighbor; + + if (OspfRI.status == enabled) + { + vty_out (vty, "--- PCE parameters ---%s", VTY_NEWLINE); + + if (pce->pce_address.header.type != 0) + show_vty_pce_subtlv_address (vty, &pce->pce_address.header); + + if (pce->pce_scope.header.type != 0) + show_vty_pce_subtlv_path_scope (vty, &pce->pce_scope.header); + + for (ALL_LIST_ELEMENTS_RO (pce->pce_domain, node, domain)) + { + if (domain->header.type != 0) + show_vty_pce_subtlv_domain (vty, &domain->header); + } + + for (ALL_LIST_ELEMENTS_RO (pce->pce_neighbor, node, neighbor)) + { + if (neighbor->header.type != 0) + show_vty_pce_subtlv_neighbor (vty, &neighbor->header); + } + + if (pce->pce_cap_flag.header.type != 0) + show_vty_pce_subtlv_cap_flag (vty, &pce->pce_cap_flag.header); + + } + else + { + vty_out (vty, " Router Information is disabled on this router%s", + VTY_NEWLINE); + } + + return CMD_SUCCESS; +} + +/* Install new CLI commands */ +static void +ospf_router_info_register_vty (void) +{ + install_element (VIEW_NODE, &show_ip_ospf_router_info_cmd); + install_element (VIEW_NODE, &show_ip_ospf_router_info_pce_cmd); + + install_element (OSPF_NODE, &router_info_area_cmd); + install_element (OSPF_NODE, &router_info_as_cmd); + install_element (OSPF_NODE, &no_router_info_cmd); + install_element (OSPF_NODE, &pce_address_cmd); + install_element (OSPF_NODE, &pce_path_scope_cmd); + install_element (OSPF_NODE, &pce_domain_cmd); + install_element (OSPF_NODE, &no_pce_domain_cmd); + install_element (OSPF_NODE, &pce_neighbor_cmd); + install_element (OSPF_NODE, &no_pce_neighbor_cmd); + install_element (OSPF_NODE, &pce_cap_flag_cmd); + + return; +} diff --git a/ospfd/ospf_ri.h b/ospfd/ospf_ri.h new file mode 100644 index 000000000..c507434e4 --- /dev/null +++ b/ospfd/ospf_ri.h @@ -0,0 +1,191 @@ +/* + * This is an implementation of RFC4970 Router Information + * with support of RFC5088 PCE Capabilites announcement + * + * Module name: Router Information + * Version: 0.99.22 + * Created: 2012-02-01 by Olivier Dugeon + * Copyright (C) 2012 Orange Labs http://www.orange.com/ + * + * 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_OSPF_ROUTER_INFO_H +#define _ZEBRA_OSPF_ROUTER_INFO_H + +/* + * Opaque LSA's link state ID for Router Information is + * structured as follows. + * + * 24 16 8 0 + * +--------+--------+--------+--------+ + * | 1 | MBZ |........|........| + * +--------+--------+--------+--------+ + * |<-Type->||<-- Instance --->| + * + * + * Type: IANA has assigned '4' for Router Information. + * MBZ: Reserved, must be set to zero. + * Instance: User may select an arbitrary 16-bit value. + * + */ + +/* + * 24 16 8 0 + * +--------+--------+--------+--------+ --- + * | LS age |Options | 9,10,11| A + * +--------+--------+--------+--------+ | + * | 4 | 0 | Instance | | + * +--------+--------+--------+--------+ | + * | Advertising router | | Standard (Opaque) LSA header; + * +--------+--------+--------+--------+ | Type 9,10 or 11 are used. + * | LS sequence number | | + * +--------+--------+--------+--------+ | + * | LS checksum | Length | V + * +--------+--------+--------+--------+ --- + * | Type | Length | A + * +--------+--------+--------+--------+ | TLV part for Router Information; Values might be + * | Values ... | V structured as a set of sub-TLVs. + * +--------+--------+--------+--------+ --- + */ + +/* + * Following section defines TLV (tag, length, value) structures, + * used for Router Information. + */ +struct ri_tlv_header +{ + u_int16_t type; /* RI_TLV_XXX (see below) */ + u_int16_t length; /* Value portion only, in byte */ +}; + +#define RI_TLV_HDR_SIZE (sizeof (struct ri_tlv_header)) +#define RI_TLV_BODY_SIZE(tlvh) (ROUNDUP (ntohs ((tlvh)->length), sizeof (u_int32_t))) +#define RI_TLV_SIZE(tlvh) (RI_TLV_HDR_SIZE + RI_TLV_BODY_SIZE(tlvh)) +#define RI_TLV_HDR_TOP(lsah) (struct ri_tlv_header *)((char *)(lsah) + OSPF_LSA_HEADER_SIZE) +#define RI_TLV_HDR_NEXT(tlvh) (struct ri_tlv_header *)((char *)(tlvh) + RI_TLV_SIZE(tlvh)) + +/* + * Following section defines TLV body parts. + */ + +/* Up to now, 8 code point have been assigned to Router Information */ +/* Only type 1 Router Capabilities and 6 PCE are supported with this code */ +#define RI_IANA_MAX_TYPE 8 + +/* RFC4970: Router Information Capabilities TLV */ /* Mandatory */ +#define RI_TLV_CAPABILITIES 1 + +struct ri_tlv_router_cap +{ + struct ri_tlv_header header; /* Value length is 4 bytes. */ + u_int32_t value; +}; + +#define RI_GRACE_RESTART 0x01 +#define RI_GRACE_HELPER 0x02 +#define RI_STUB_SUPPORT 0x04 +#define RI_TE_SUPPORT 0x08 +#define RI_P2P_OVER_LAN 0x10 +#define RI_TE_EXPERIMENTAL 0x20 + +#define RI_TLV_LENGTH 4 + +/* RFC5088: PCE Capabilities TLV */ /* Optional */ +/* RI PCE TLV */ +#define RI_TLV_PCE 6 + +struct ri_tlv_pce +{ + struct ri_tlv_header header; +/* A set of PCE-sub-TLVs will follow. */ +}; + +/* PCE Address Sub-TLV */ /* Mandatory */ +#define RI_PCE_SUBTLV_ADDRESS 1 +struct ri_pce_subtlv_address +{ + struct ri_tlv_header header; /* Type = 1; Length is 8 (IPv4) or 20 (IPv6) bytes. */ +#define PCE_ADDRESS_LENGTH_IPV4 8 +#define PCE_ADDRESS_LENGTH_IPV6 20 + struct + { + u_int16_t type; /* Address type: 1 = IPv4, 2 = IPv6 */ +#define PCE_ADDRESS_TYPE_IPV4 1 +#define PCE_ADDRESS_TYPE_IPV6 2 + u_int16_t reserved; + struct in_addr value; /* PCE address */ + } address; +}; + +/* PCE Path-Scope Sub-TLV */ /* Mandatory */ +#define RI_PCE_SUBTLV_PATH_SCOPE 2 +struct ri_pce_subtlv_path_scope +{ + struct ri_tlv_header header; /* Type = 2; Length = 4 bytes. */ + u_int32_t value; /* L, R, Rd, S, Sd, Y, PrefL, PrefR, PrefS and PrefY bits see RFC5088 page 9 */ +}; + +/* PCE Domain Sub-TLV */ /* Optional */ +#define RI_PCE_SUBTLV_DOMAIN 3 + +#define PCE_DOMAIN_TYPE_AREA 1 +#define PCE_DOMAIN_TYPE_AS 2 + +struct ri_pce_subtlv_domain +{ + struct ri_tlv_header header; /* Type = 3; Length = 8 bytes. */ + u_int16_t type; /* Domain type: 1 = OSPF Area ID, 2 = AS Number */ + u_int16_t reserved; + u_int32_t value; +}; + +/* PCE Neighbor Sub-TLV */ /* Mandatory if R or S bit is set */ +#define RI_PCE_SUBTLV_NEIGHBOR 4 +struct ri_pce_subtlv_neighbor +{ + struct ri_tlv_header header; /* Type = 4; Length = 8 bytes. */ + u_int16_t type; /* Domain type: 1 = OSPF Area ID, 2 = AS Number */ + u_int16_t reserved; + u_int32_t value; +}; + +/* PCE Capabilities Flags Sub-TLV */ /* Optional */ +#define RI_PCE_SUBTLV_CAP_FLAG 5 + +#define PCE_CAP_GMPLS_LINK 0x0001 +#define PCE_CAP_BIDIRECTIONAL 0x0002 +#define PCE_CAP_DIVERSE_PATH 0x0004 +#define PCE_CAP_LOAD_BALANCE 0x0008 +#define PCE_CAP_SYNCHRONIZED 0x0010 +#define PCE_CAP_OBJECTIVES 0x0020 +#define PCE_CAP_ADDITIVE 0x0040 +#define PCE_CAP_PRIORIZATION 0x0080 +#define PCE_CAP_MULTIPLE_REQ 0x0100 + +struct ri_pce_subtlv_cap_flag +{ + struct ri_tlv_header header; /* Type = 5; Length = n x 4 bytes. */ + u_int32_t value; +}; + +/* Prototypes. */ +extern int ospf_router_info_init (void); +extern void ospf_router_info_term (void); + +#endif /* _ZEBRA_OSPF_ROUTER_INFO_H */ diff --git a/ospfd/ospf_route.c b/ospfd/ospf_route.c index d2e5e1e7d..eb7829acd 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); } } @@ -429,7 +454,7 @@ ospf_intra_add_transit (struct route_table *rt, struct vertex *v, void ospf_intra_add_stub (struct route_table *rt, struct router_lsa_link *link, struct vertex *v, struct ospf_area *area, - int parent_is_root) + int parent_is_root, int lsa_pos) { u_int32_t cost; struct route_node *rn; @@ -577,7 +602,7 @@ ospf_intra_add_stub (struct route_table *rt, struct router_lsa_link *link, if (IS_DEBUG_OSPF_EVENT) zlog_debug ("ospf_intra_add_stub(): this network is on this router"); - if ((oi = ospf_if_lookup_by_prefix (area->ospf, &p))) + if ((oi = ospf_if_lookup_by_lsa_pos (area, lsa_pos))) { if (IS_DEBUG_OSPF_EVENT) zlog_debug ("ospf_intra_add_stub(): the interface is %s", @@ -954,6 +979,10 @@ ospf_add_discard_route (struct route_table *rt, struct ospf_area *area, ospf_route_free (rn->info); } + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_add_discard_route(): " + "adding %s/%d", inet_ntoa (p->prefix), p->prefixlen); + new_or = ospf_route_new (); new_or->type = OSPF_DESTINATION_DISCARD; new_or->id.s_addr = 0; @@ -969,8 +998,52 @@ ospf_add_discard_route (struct route_table *rt, struct ospf_area *area, } void -ospf_delete_discard_route (struct prefix_ipv4 *p) +ospf_delete_discard_route (struct route_table *rt, struct prefix_ipv4 *p) { + struct route_node *rn; + struct ospf_route *or; + + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_delete_discard_route(): " + "deleting %s/%d", inet_ntoa (p->prefix), p->prefixlen); + + rn = route_node_lookup (rt, (struct prefix*)p); + + if (rn == NULL) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug("ospf_delete_discard_route(): no route found"); + return; + } + + or = rn->info; + + if (or->path_type == OSPF_PATH_INTRA_AREA) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_delete_discard_route(): " + "an intra-area route exists"); + return; + } + + if (or->type != OSPF_DESTINATION_DISCARD) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("ospf_delete_discard_route(): " + "not a discard entry"); + return; + } + + /* free the route entry and the route node */ + ospf_route_free (rn->info); + + rn->info = NULL; + route_unlock_node (rn); + route_unlock_node (rn); + + /* remove the discard entry from the rib */ ospf_zebra_delete_discard(p); + + return; } diff --git a/ospfd/ospf_route.h b/ospfd/ospf_route.h index 17ab68e5d..d509e4ae7 100644 --- a/ospfd/ospf_route.h +++ b/ospfd/ospf_route.h @@ -39,7 +39,7 @@ struct ospf_path { struct in_addr nexthop; struct in_addr adv_router; - unsigned int ifindex; + ifindex_t ifindex; }; /* Below is the structure linked to every @@ -141,7 +141,7 @@ extern void ospf_intra_add_transit (struct route_table *, struct vertex *, extern void ospf_intra_add_stub (struct route_table *, struct router_lsa_link *, struct vertex *, struct ospf_area *, - int parent_is_root); + int parent_is_root, int); extern int ospf_route_cmp (struct ospf *, struct ospf_route *, struct ospf_route *); @@ -159,7 +159,7 @@ extern void ospf_prune_unreachable_networks (struct route_table *); extern void ospf_prune_unreachable_routers (struct route_table *); extern int ospf_add_discard_route (struct route_table *, struct ospf_area *, struct prefix_ipv4 *); -extern void ospf_delete_discard_route (struct prefix_ipv4 *); +extern void ospf_delete_discard_route (struct route_table *, struct prefix_ipv4 *); extern int ospf_route_match_same (struct route_table *, struct prefix_ipv4 *, struct ospf_route *); diff --git a/ospfd/ospf_routemap.c b/ospfd/ospf_routemap.c index adf815839..31d7ce2fb 100644 --- a/ospfd/ospf_routemap.c +++ b/ospfd/ospf_routemap.c @@ -108,10 +108,10 @@ ospf_route_match_delete (struct vty *vty, struct route_map_index *index, switch (ret) { case RMAP_RULE_MISSING: - vty_out (vty, "%% Can't find rule.%s", VTY_NEWLINE); + vty_out (vty, "%% OSPF Can't find rule.%s", VTY_NEWLINE); return CMD_WARNING; case RMAP_COMPILE_ERROR: - vty_out (vty, "%% Argument is malformed.%s", VTY_NEWLINE); + vty_out (vty, "%% OSPF Argument is malformed.%s", VTY_NEWLINE); return CMD_WARNING; } } @@ -131,10 +131,10 @@ ospf_route_match_add (struct vty *vty, struct route_map_index *index, switch (ret) { case RMAP_RULE_MISSING: - vty_out (vty, "%% Can't find rule.%s", VTY_NEWLINE); + vty_out (vty, "%% OSPF Can't find rule.%s", VTY_NEWLINE); return CMD_WARNING; case RMAP_COMPILE_ERROR: - vty_out (vty, "%% Argument is malformed.%s", VTY_NEWLINE); + vty_out (vty, "%% OSPF Argument is malformed.%s", VTY_NEWLINE); return CMD_WARNING; } } @@ -154,10 +154,10 @@ ospf_route_set_add (struct vty *vty, struct route_map_index *index, switch (ret) { case RMAP_RULE_MISSING: - vty_out (vty, "%% Can't find rule.%s", VTY_NEWLINE); + vty_out (vty, "%% OSPF Can't find rule.%s", VTY_NEWLINE); return CMD_WARNING; case RMAP_COMPILE_ERROR: - vty_out (vty, "%% Argument is malformed.%s", VTY_NEWLINE); + vty_out (vty, "%% OSPF Argument is malformed.%s", VTY_NEWLINE); return CMD_WARNING; } } @@ -178,10 +178,10 @@ ospf_route_set_delete (struct vty *vty, struct route_map_index *index, switch (ret) { case RMAP_RULE_MISSING: - vty_out (vty, "%% Can't find rule.%s", VTY_NEWLINE); + vty_out (vty, "%% OSPF Can't find rule.%s", VTY_NEWLINE); return CMD_WARNING; case RMAP_COMPILE_ERROR: - vty_out (vty, "%% Argument is malformed.%s", VTY_NEWLINE); + vty_out (vty, "%% OSPF Argument is malformed.%s", VTY_NEWLINE); return CMD_WARNING; } } @@ -417,6 +417,35 @@ struct route_map_rule_cmd route_match_interface_cmd = route_match_interface_free }; +/* Match function return 1 if match is success else return zero. */ +static route_map_result_t +route_match_tag (void *rule, struct prefix *prefix, + route_map_object_t type, void *object) +{ + route_tag_t *tag; + struct external_info *ei; + + if (type == RMAP_OSPF) + { + tag = rule; + ei = object; + + return ((ei->tag == *tag)? RMAP_MATCH : RMAP_NOMATCH); + } + + return RMAP_NOMATCH; +} + +/* Route map commands for tag matching. */ +static struct route_map_rule_cmd route_match_tag_cmd = +{ + "tag", + route_match_tag, + route_map_rule_tag_compile, + route_map_rule_tag_free, +}; + + /* `set metric METRIC' */ /* Set metric to attribute. */ static route_map_result_t @@ -443,12 +472,31 @@ static void * route_set_metric_compile (const char *arg) { u_int32_t *metric; + int32_t ret; + + /* OSPF doesn't support the +/- in + set metric <+/-metric> check + Ignore the +/- component */ + if (! all_digit (arg)) + { + if ((strncmp (arg, "+", 1) == 0 || strncmp (arg, "-", 1) == 0) && + all_digit (arg+1)) + { + zlog_warn ("OSPF does not support 'set metric +/-'"); + arg++; + } + else + return NULL; + } 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; @@ -527,6 +575,34 @@ struct route_map_rule_cmd route_set_metric_type_cmd = route_set_metric_type_free, }; +static route_map_result_t +route_set_tag (void *rule, struct prefix *prefix, + route_map_object_t type, void *object) +{ + route_tag_t *tag; + struct external_info *ei; + + if (type == RMAP_OSPF) + { + tag = rule; + ei = object; + + /* Set tag value */ + ei->tag=*tag; + } + + return RMAP_OKAY; +} + +/* Route map commands for tag set. */ +static struct route_map_rule_cmd route_set_tag_cmd = +{ + "tag", + route_set_tag, + route_map_rule_tag_compile, + route_map_rule_tag_free, +}; + DEFUN (match_ip_nexthop, match_ip_nexthop_cmd, "match ip next-hop (<1-199>|<1300-2699>|WORD)", @@ -712,6 +788,37 @@ ALIAS (no_match_interface, "Match first hop interface of route\n" "Interface name\n") +DEFUN (match_tag, + match_tag_cmd, + "match tag <1-4294967295>", + MATCH_STR + "Match tag of route\n" + "Tag value\n") +{ + return ospf_route_match_add (vty, vty->index, "tag", argv[0]); +} + +DEFUN (no_match_tag, + no_match_tag_cmd, + "no match tag", + NO_STR + MATCH_STR + "Match tag of route\n") +{ + if (argc == 0) + return ospf_route_match_delete (vty, vty->index, "tag", NULL); + + return ospf_route_match_delete (vty, vty->index, "tag", argv[0]); +} + +ALIAS (no_match_tag, + no_match_tag_val_cmd, + "no match tag <1-4294967295>", + NO_STR + MATCH_STR + "Match tag of route\n" + "Tag value\n") + DEFUN (set_metric, set_metric_cmd, "set metric <0-4294967295>", @@ -781,6 +888,37 @@ ALIAS (no_set_metric_type, "OSPF[6] external type 1 metric\n" "OSPF[6] external type 2 metric\n") +DEFUN (set_tag, + set_tag_cmd, + "set tag <1-4294967295>", + SET_STR + "Tag value for routing protocol\n" + "Tag value\n") +{ + return ospf_route_set_add (vty, vty->index, "tag", argv[0]); +} + +DEFUN (no_set_tag, + no_set_tag_cmd, + "no set tag", + NO_STR + SET_STR + "Tag value for routing protocol\n") +{ + if (argc == 0) + ospf_route_set_delete(vty, vty->index, "tag", NULL); + + return ospf_route_set_delete (vty, vty->index, "tag", argv[0]); +} + +ALIAS (no_set_tag, + no_set_tag_val_cmd, + "no set tag <1-4294967295>", + NO_STR + SET_STR + "Tag value for routing protocol\n" + "Tag value\n") + /* Route-map init */ void ospf_route_map_init (void) @@ -797,9 +935,11 @@ ospf_route_map_init (void) route_map_install_match (&route_match_ip_address_cmd); route_map_install_match (&route_match_ip_address_prefix_list_cmd); route_map_install_match (&route_match_interface_cmd); + route_map_install_match (&route_match_tag_cmd); route_map_install_set (&route_set_metric_cmd); route_map_install_set (&route_set_metric_type_cmd); + route_map_install_set (&route_set_tag_cmd); install_element (RMAP_NODE, &match_ip_nexthop_cmd); install_element (RMAP_NODE, &no_match_ip_nexthop_cmd); @@ -816,6 +956,9 @@ ospf_route_map_init (void) install_element (RMAP_NODE, &match_interface_cmd); install_element (RMAP_NODE, &no_match_interface_cmd); install_element (RMAP_NODE, &no_match_interface_val_cmd); + install_element (RMAP_NODE, &match_tag_cmd); + install_element (RMAP_NODE, &no_match_tag_cmd); + install_element (RMAP_NODE, &no_match_tag_val_cmd); install_element (RMAP_NODE, &set_metric_cmd); install_element (RMAP_NODE, &no_set_metric_cmd); @@ -823,4 +966,7 @@ ospf_route_map_init (void) install_element (RMAP_NODE, &set_metric_type_cmd); install_element (RMAP_NODE, &no_set_metric_type_cmd); install_element (RMAP_NODE, &no_set_metric_type_val_cmd); + install_element (RMAP_NODE, &set_tag_cmd); + install_element (RMAP_NODE, &no_set_tag_cmd); + install_element (RMAP_NODE, &no_set_tag_val_cmd); } diff --git a/ospfd/ospf_snmp.c b/ospfd/ospf_snmp.c index cc4974ce6..1f9b85187 100644 --- a/ospfd/ospf_snmp.c +++ b/ospfd/ospf_snmp.c @@ -25,14 +25,8 @@ #include #ifdef HAVE_SNMP -#ifdef HAVE_NETSNMP #include #include -#else -#include -#include -#include -#endif #include "if.h" #include "log.h" @@ -54,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 @@ -210,15 +204,16 @@ #define TIMETICKS ASN_TIMETICKS #define IPADDRESS ASN_IPADDRESS #define STRING ASN_OCTET_STR - + /* Declare static local variables for convenience. */ SNMP_LOCAL_VARIABLES /* OSPF-MIB instances. */ oid ospf_oid [] = { OSPF2MIB }; +oid ospf_trap_oid [] = { OSPF2MIB, 16, 2 }; /* Not reverse mappable! */ /* IP address 0.0.0.0. */ -static struct in_addr ospf_empty_addr = {0}; +static struct in_addr ospf_empty_addr = { .s_addr = 0 }; /* Hook functions. */ static u_char *ospfGeneralGroup (struct variable *, oid *, size_t *, @@ -506,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 @@ -709,6 +704,10 @@ ospfAreaEntry (struct variable *v, oid *name, size_t *length, int exact, struct ospf_area *area; struct in_addr addr; + if (smux_header_table(v, name, length, exact, var_len, write_method) + == MATCH_FAILED) + return NULL; + memset (&addr, 0, sizeof (struct in_addr)); area = ospfAreaLookup (v, name, length, &addr, exact); @@ -852,6 +851,10 @@ ospfStubAreaEntry (struct variable *v, oid *name, size_t *length, struct ospf_area *area; struct in_addr addr; + if (smux_header_table(v, name, length, exact, var_len, write_method) + == MATCH_FAILED) + return NULL; + memset (&addr, 0, sizeof (struct in_addr)); area = ospfStubAreaLookup (v, name, length, &addr, exact); @@ -934,7 +937,7 @@ ospfLsdbLookup (struct variable *v, oid *name, size_t *length, struct ospf *ospf; struct ospf_area *area; struct ospf_lsa *lsa; - unsigned int len; + int len; int type_next; int ls_id_next; int router_id_next; @@ -983,7 +986,7 @@ ospfLsdbLookup (struct variable *v, oid *name, size_t *length, offsetlen = *length - v->namelen; len = offsetlen; - if (len > IN_ADDR_SIZE) + if (len > (int)IN_ADDR_SIZE) len = IN_ADDR_SIZE; oid2in_addr (offset, len, area_id); @@ -992,7 +995,7 @@ ospfLsdbLookup (struct variable *v, oid *name, size_t *length, if (len == IN_ADDR_SIZE) area = ospf_area_lookup_by_area_id (ospf, *area_id); else - area = ospf_area_lookup_next (ospf, area_id, len == 0 ? 1 : 0); + area = ospf_area_lookup_next (ospf, area_id, 1); if (area == NULL) return NULL; @@ -1000,8 +1003,8 @@ ospfLsdbLookup (struct variable *v, oid *name, size_t *length, do { /* Next we lookup type. */ - offset += IN_ADDR_SIZE; - offsetlen -= IN_ADDR_SIZE; + offset += len; + offsetlen -= len; len = offsetlen; if (len <= 0) @@ -1023,7 +1026,7 @@ ospfLsdbLookup (struct variable *v, oid *name, size_t *length, else { ls_id_next = 0; - if (len > IN_ADDR_SIZE) + if (len > (int)IN_ADDR_SIZE) len = IN_ADDR_SIZE; oid2in_addr (offset, len, ls_id); @@ -1039,7 +1042,7 @@ ospfLsdbLookup (struct variable *v, oid *name, size_t *length, else { router_id_next = 0; - if (len > IN_ADDR_SIZE) + if (len > (int)IN_ADDR_SIZE) len = IN_ADDR_SIZE; oid2in_addr (offset, len, router_id); @@ -1083,6 +1086,10 @@ ospfLsdbEntry (struct variable *v, oid *name, size_t *length, int exact, struct in_addr router_id; struct ospf *ospf; + if (smux_header_table(v, name, length, exact, var_len, write_method) + == MATCH_FAILED) + return NULL; + /* INDEX { ospfLsdbAreaId, ospfLsdbType, ospfLsdbLsid, ospfLsdbRouterId } */ @@ -1145,7 +1152,7 @@ ospfAreaRangeLookup (struct variable *v, oid *name, size_t *length, { oid *offset; int offsetlen; - unsigned int len; + int len; struct ospf *ospf; struct ospf_area *area; struct ospf_area_range *range; @@ -1186,7 +1193,7 @@ ospfAreaRangeLookup (struct variable *v, oid *name, size_t *length, offsetlen = *length - v->namelen; len = offsetlen; - if (len > IN_ADDR_SIZE) + if (len > (int)IN_ADDR_SIZE) len = IN_ADDR_SIZE; oid2in_addr (offset, len, area_id); @@ -1208,7 +1215,7 @@ ospfAreaRangeLookup (struct variable *v, oid *name, size_t *length, if (len < 0) len = 0; - if (len > IN_ADDR_SIZE) + if (len > (int)IN_ADDR_SIZE) len = IN_ADDR_SIZE; oid2in_addr (offset, len, range_net); @@ -1245,6 +1252,10 @@ ospfAreaRangeEntry (struct variable *v, oid *name, size_t *length, int exact, struct in_addr mask; struct ospf *ospf; + if (smux_header_table(v, name, length, exact, var_len, write_method) + == MATCH_FAILED) + return NULL; + /* Check OSPF instance. */ ospf = ospf_lookup (); if (ospf == NULL) @@ -1349,6 +1360,10 @@ ospfHostEntry (struct variable *v, oid *name, size_t *length, int exact, struct in_addr addr; struct ospf *ospf; + if (smux_header_table(v, name, length, exact, var_len, write_method) + == MATCH_FAILED) + return NULL; + /* Check OSPF instance. */ ospf = ospf_lookup (); if (ospf == NULL) @@ -1392,26 +1407,26 @@ ospfHostEntry (struct variable *v, oid *name, size_t *length, int exact, } return NULL; } - + struct list *ospf_snmp_iflist; struct ospf_snmp_if { struct in_addr addr; - unsigned int ifindex; + ifindex_t ifindex; struct interface *ifp; }; static struct ospf_snmp_if * ospf_snmp_if_new (void) { - return XCALLOC (0, sizeof (struct ospf_snmp_if)); + return XCALLOC (MTYPE_TMP, sizeof (struct ospf_snmp_if)); } static void ospf_snmp_if_free (struct ospf_snmp_if *osif) { - XFREE (0, osif); + XFREE (MTYPE_TMP, osif); } void @@ -1440,7 +1455,7 @@ ospf_snmp_if_update (struct interface *ifp) struct prefix *p; struct ospf_snmp_if *osif; struct in_addr *addr; - unsigned int ifindex; + ifindex_t ifindex; ospf_snmp_if_delete (ifp); @@ -1515,7 +1530,7 @@ ospf_snmp_is_if_have_addr (struct interface *ifp) } static struct ospf_interface * -ospf_snmp_if_lookup (struct in_addr *ifaddr, unsigned int *ifindex) +ospf_snmp_if_lookup (struct in_addr *ifaddr, ifindex_t *ifindex) { struct listnode *node; struct ospf_snmp_if *osif; @@ -1539,8 +1554,8 @@ ospf_snmp_if_lookup (struct in_addr *ifaddr, unsigned int *ifindex) } static struct ospf_interface * -ospf_snmp_if_lookup_next (struct in_addr *ifaddr, unsigned int *ifindex, - int ifaddr_next, int ifindex_next) +ospf_snmp_if_lookup_next (struct in_addr *ifaddr, ifindex_t *ifindex, + int ifaddr_next, ifindex_t ifindex_next) { struct ospf_snmp_if *osif; struct listnode *nn; @@ -1623,11 +1638,11 @@ ospf_snmp_iftype (struct interface *ifp) static struct ospf_interface * ospfIfLookup (struct variable *v, oid *name, size_t *length, - struct in_addr *ifaddr, unsigned int *ifindex, int exact) + struct in_addr *ifaddr, ifindex_t *ifindex, int exact) { unsigned int len; int ifaddr_next = 0; - int ifindex_next = 0; + ifindex_t ifindex_next = 0; struct ospf_interface *oi; oid *offset; @@ -1679,11 +1694,15 @@ static u_char * ospfIfEntry (struct variable *v, oid *name, size_t *length, int exact, size_t *var_len, WriteMethod **write_method) { - unsigned int ifindex; + ifindex_t ifindex; struct in_addr ifaddr; struct ospf_interface *oi; struct ospf *ospf; + if (smux_header_table(v, name, length, exact, var_len, write_method) + == MATCH_FAILED) + return NULL; + ifindex = 0; memset (&ifaddr, 0, sizeof (struct in_addr)); @@ -1783,11 +1802,11 @@ ospfIfEntry (struct variable *v, oid *name, size_t *length, int exact, static struct ospf_interface * ospfIfMetricLookup (struct variable *v, oid *name, size_t *length, - struct in_addr *ifaddr, unsigned int *ifindex, int exact) + struct in_addr *ifaddr, ifindex_t *ifindex, int exact) { unsigned int len; int ifaddr_next = 0; - int ifindex_next = 0; + ifindex_t ifindex_next = 0; struct ospf_interface *oi; oid *offset; int metric; @@ -1847,11 +1866,15 @@ ospfIfMetricEntry (struct variable *v, oid *name, size_t *length, int exact, size_t *var_len, WriteMethod **write_method) { /* Currently we support metric 1 only. */ - unsigned int ifindex; + ifindex_t ifindex; struct in_addr ifaddr; struct ospf_interface *oi; struct ospf *ospf; + if (smux_header_table(v, name, length, exact, var_len, write_method) + == MATCH_FAILED) + return NULL; + ifindex = 0; memset (&ifaddr, 0, sizeof (struct in_addr)); @@ -1888,7 +1911,7 @@ ospfIfMetricEntry (struct variable *v, oid *name, size_t *length, int exact, } return NULL; } - + struct route_table *ospf_snmp_vl_table; void @@ -1904,6 +1927,9 @@ ospf_snmp_vl_add (struct ospf_vl_data *vl_data) lp.adv_router = vl_data->vl_peer; rn = route_node_get (ospf_snmp_vl_table, (struct prefix *) &lp); + if (rn->info) + route_unlock_node (rn); + rn->info = vl_data; } @@ -2044,6 +2070,10 @@ ospfVirtIfEntry (struct variable *v, oid *name, size_t *length, int exact, struct in_addr area_id; struct in_addr neighbor; + if (smux_header_table(v, name, length, exact, var_len, write_method) + == MATCH_FAILED) + return NULL; + memset (&area_id, 0, sizeof (struct in_addr)); memset (&neighbor, 0, sizeof (struct in_addr)); @@ -2100,10 +2130,10 @@ 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) + ifindex_t *ifindex) { struct listnode *node, *nnode; struct ospf_interface *oi; @@ -2131,7 +2161,7 @@ ospf_snmp_nbr_lookup (struct ospf *ospf, struct in_addr *nbr_addr, } static struct ospf_neighbor * -ospf_snmp_nbr_lookup_next (struct in_addr *nbr_addr, unsigned int *ifindex, +ospf_snmp_nbr_lookup_next (struct in_addr *nbr_addr, ifindex_t *ifindex, int first) { struct listnode *nn; @@ -2178,7 +2208,7 @@ ospf_snmp_nbr_lookup_next (struct in_addr *nbr_addr, unsigned int *ifindex, static struct ospf_neighbor * ospfNbrLookup (struct variable *v, oid *name, size_t *length, - struct in_addr *nbr_addr, unsigned int *ifindex, int exact) + struct in_addr *nbr_addr, ifindex_t *ifindex, int exact) { unsigned int len; int first; @@ -2273,10 +2303,14 @@ ospfNbrEntry (struct variable *v, oid *name, size_t *length, int exact, size_t *var_len, WriteMethod **write_method) { struct in_addr nbr_addr; - unsigned int ifindex; + ifindex_t ifindex; struct ospf_neighbor *nbr; struct ospf_interface *oi; + if (smux_header_table(v, name, length, exact, var_len, write_method) + == MATCH_FAILED) + return NULL; + memset (&nbr_addr, 0, sizeof (struct in_addr)); ifindex = 0; @@ -2329,7 +2363,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) @@ -2339,6 +2373,10 @@ ospfVirtNbrEntry (struct variable *v, oid *name, size_t *length, int exact, struct in_addr neighbor; struct ospf *ospf; + if (smux_header_table(v, name, length, exact, var_len, write_method) + == MATCH_FAILED) + return NULL; + memset (&area_id, 0, sizeof (struct in_addr)); memset (&neighbor, 0, sizeof (struct in_addr)); @@ -2384,7 +2422,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) @@ -2487,6 +2525,10 @@ ospfExtLsdbEntry (struct variable *v, oid *name, size_t *length, int exact, struct in_addr router_id; struct ospf *ospf; + if (smux_header_table(v, name, length, exact, var_len, write_method) + == MATCH_FAILED) + return NULL; + type = OSPF_AS_EXTERNAL_LSA; memset (&ls_id, 0, sizeof (struct in_addr)); memset (&router_id, 0, sizeof (struct in_addr)); @@ -2533,11 +2575,15 @@ 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) { + if (smux_header_table(v, name, length, exact, var_len, write_method) + == MATCH_FAILED) + return NULL; + /* Return the current value of the variable */ switch (v->magic) { @@ -2565,7 +2611,7 @@ ospfAreaAggregateEntry (struct variable *v, oid *name, size_t *length, } return NULL; } - + /* OSPF Traps. */ #define IFSTATECHANGE 16 #define VIRTIFSTATECHANGE 1 @@ -2574,35 +2620,35 @@ ospfAreaAggregateEntry (struct variable *v, oid *name, size_t *length, struct trap_object ospfNbrTrapList[] = { - {ospfGeneralGroup, -2, {1, OSPFROUTERID}}, - {ospfNbrEntry, 3, {10, 1, OSPFNBRIPADDR}}, - {ospfNbrEntry, 3, {10, 1, OSPFNBRRTRID}}, - {ospfNbrEntry, 3, {10, 1, OSPFNBRSTATE}} + {-2, {1, OSPFROUTERID}}, + {3, {10, 1, OSPFNBRIPADDR}}, + {3, {10, 1, OSPFNBRRTRID}}, + {3, {10, 1, OSPFNBRSTATE}} }; struct trap_object ospfVirtNbrTrapList[] = { - {ospfGeneralGroup, -2, {1, 1}}, - {ospfVirtNbrEntry, 3, {11, 1, OSPFVIRTNBRAREA}}, - {ospfVirtNbrEntry, 3, {11, 1, OSPFVIRTNBRRTRID}}, - {ospfVirtNbrEntry, 3, {11, 1, OSPFVIRTNBRSTATE}} + {-2, {1, 1}}, + {3, {11, 1, OSPFVIRTNBRAREA}}, + {3, {11, 1, OSPFVIRTNBRRTRID}}, + {3, {11, 1, OSPFVIRTNBRSTATE}} }; struct trap_object ospfIfTrapList[] = { - {ospfGeneralGroup, -2, {1, OSPFROUTERID}}, - {ospfIfEntry, 3, {7, 1, OSPFIFIPADDRESS}}, - {ospfIfEntry, 3, {7, 1, OSPFADDRESSLESSIF}}, - {ospfIfEntry, 3, {7, 1, OSPFIFSTATE}} + {-2, {1, OSPFROUTERID}}, + {3, {7, 1, OSPFIFIPADDRESS}}, + {3, {7, 1, OSPFADDRESSLESSIF}}, + {3, {7, 1, OSPFIFSTATE}} }; struct trap_object ospfVirtIfTrapList[] = { - {ospfGeneralGroup, -2, {1, OSPFROUTERID}}, - {ospfVirtIfEntry, 3, {9, 1, OSPFVIRTIFAREAID}}, - {ospfVirtIfEntry, 3, {9, 1, OSPFVIRTIFNEIGHBOR}}, - {ospfVirtIfEntry, 3, {9, 1, OSPFVIRTIFSTATE}} + {-2, {1, OSPFROUTERID}}, + {3, {9, 1, OSPFVIRTIFAREAID}}, + {3, {9, 1, OSPFVIRTIFNEIGHBOR}}, + {3, {9, 1, OSPFVIRTIFSTATE}} }; void @@ -2618,11 +2664,13 @@ ospfTrapNbrStateChange (struct ospf_neighbor *on) oid_copy_addr (index, &(on->address.u.prefix4), IN_ADDR_SIZE); index[IN_ADDR_SIZE] = 0; - smux_trap (ospf_oid, sizeof ospf_oid / sizeof (oid), + smux_trap (ospf_variables, sizeof ospf_variables / sizeof (struct variable), + ospf_trap_oid, sizeof ospf_trap_oid / sizeof (oid), + ospf_oid, sizeof ospf_oid / sizeof (oid), index, IN_ADDR_SIZE + 1, ospfNbrTrapList, sizeof ospfNbrTrapList / sizeof (struct trap_object), - time (NULL), NBRSTATECHANGE); + NBRSTATECHANGE); } void @@ -2635,11 +2683,13 @@ ospfTrapVirtNbrStateChange (struct ospf_neighbor *on) oid_copy_addr (index, &(on->address.u.prefix4), IN_ADDR_SIZE); index[IN_ADDR_SIZE] = 0; - smux_trap (ospf_oid, sizeof ospf_oid / sizeof (oid), + smux_trap (ospf_variables, sizeof ospf_variables / sizeof (struct variable), + ospf_trap_oid, sizeof ospf_trap_oid / sizeof (oid), + ospf_oid, sizeof ospf_oid / sizeof (oid), index, IN_ADDR_SIZE + 1, ospfVirtNbrTrapList, sizeof ospfVirtNbrTrapList / sizeof (struct trap_object), - time (NULL), VIRTNBRSTATECHANGE); + VIRTNBRSTATECHANGE); } void @@ -2654,11 +2704,13 @@ ospfTrapIfStateChange (struct ospf_interface *oi) oid_copy_addr (index, &(oi->address->u.prefix4), IN_ADDR_SIZE); index[IN_ADDR_SIZE] = 0; - smux_trap (ospf_oid, sizeof ospf_oid / sizeof (oid), + smux_trap (ospf_variables, sizeof ospf_variables / sizeof (struct variable), + ospf_trap_oid, sizeof ospf_trap_oid / sizeof (oid), + ospf_oid, sizeof ospf_oid / sizeof (oid), index, IN_ADDR_SIZE + 1, ospfIfTrapList, sizeof ospfIfTrapList / sizeof (struct trap_object), - time (NULL), IFSTATECHANGE); + IFSTATECHANGE); } void @@ -2671,11 +2723,13 @@ ospfTrapVirtIfStateChange (struct ospf_interface *oi) oid_copy_addr (index, &(oi->address->u.prefix4), IN_ADDR_SIZE); index[IN_ADDR_SIZE] = 0; - smux_trap (ospf_oid, sizeof ospf_oid / sizeof (oid), + smux_trap (ospf_variables, sizeof ospf_variables / sizeof (struct variable), + ospf_trap_oid, sizeof ospf_trap_oid / sizeof (oid), + ospf_oid, sizeof ospf_oid / sizeof (oid), index, IN_ADDR_SIZE + 1, ospfVirtIfTrapList, sizeof ospfVirtIfTrapList / sizeof (struct trap_object), - time (NULL), VIRTIFSTATECHANGE); + VIRTIFSTATECHANGE); } /* Register OSPF2-MIB. */ void diff --git a/ospfd/ospf_spf.c b/ospfd/ospf_spf.c index d7f9ba27d..e1c290e6d 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) { @@ -422,7 +465,8 @@ ospf_spf_add_parent (struct vertex *v, struct vertex *w, struct vertex_nexthop *newhop, unsigned int distance) { - struct vertex_parent *vp; + struct vertex_parent *vp, *wp; + struct listnode *node; /* we must have a newhop, and a distance */ assert (v && w && newhop); @@ -456,7 +500,19 @@ ospf_spf_add_parent (struct vertex *v, struct vertex *w, w->distance = distance; } - /* new parent is <= existing parents, add it to parent list */ + /* new parent is <= existing parents, add it to parent list (if nexthop + * not on parent list) + */ + for (ALL_LIST_ELEMENTS_RO(w->parents, node, wp)) + { + if (memcmp(newhop, wp->nexthop, sizeof(*newhop)) == 0) + { + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("%s: ... nexthop already on parent list, skipping add", __func__); + return; + } + } + vp = vertex_parent_new (v, ospf_lsa_has_link (w->lsa, v->lsa), newhop); listnode_add (w->parents, vp); @@ -477,13 +533,15 @@ ospf_spf_add_parent (struct vertex *v, struct vertex *w, static unsigned int ospf_nexthop_calculation (struct ospf_area *area, struct vertex *v, struct vertex *w, struct router_lsa_link *l, - unsigned int distance) + unsigned int distance, int lsa_pos) { struct listnode *node, *nnode; struct vertex_nexthop *nh; struct vertex_parent *vp; struct ospf_interface *oi = NULL; unsigned int added = 0; + char buf1[BUFSIZ]; + char buf2[BUFSIZ]; if (IS_DEBUG_OSPF_EVENT) { @@ -502,30 +560,38 @@ ospf_nexthop_calculation (struct ospf_area *area, struct vertex *v, the OSPF interface connecting to the destination network/router. */ + /* we *must* be supplied with the link data */ + assert (l != NULL); + oi = ospf_if_lookup_by_lsa_pos (area, lsa_pos); + if (!oi) + { + zlog_debug("%s: OI not found in LSA: lsa_pos:%d link_id:%s link_data:%s", + __func__, lsa_pos, + inet_ntop (AF_INET, &l->link_id, buf1, BUFSIZ), + inet_ntop (AF_INET, &l->link_data, buf2, BUFSIZ)); + return 0; + } + + if (IS_DEBUG_OSPF_EVENT) + { + zlog_debug("%s: considering link:%s " + "type:%d link_id:%s link_data:%s", + __func__, oi->ifp->name, l->m[0].type, + inet_ntop (AF_INET, &l->link_id, buf1, BUFSIZ), + inet_ntop (AF_INET, &l->link_data, buf2, BUFSIZ)); + } + if (w->type == OSPF_VERTEX_ROUTER) { /* l is a link from v to w * l2 will be link from w to v */ struct router_lsa_link *l2 = NULL; - - /* we *must* be supplied with the link data */ - assert (l != NULL); - - if (IS_DEBUG_OSPF_EVENT) - { - char buf1[BUFSIZ]; - char buf2[BUFSIZ]; - - zlog_debug("ospf_nexthop_calculation(): considering link " - "type %d link_id %s link_data %s", - l->m[0].type, - inet_ntop (AF_INET, &l->link_id, buf1, BUFSIZ), - inet_ntop (AF_INET, &l->link_data, buf2, BUFSIZ)); - } if (l->m[0].type == LSA_LINK_TYPE_POINTOPOINT) { + struct in_addr nexthop = { .s_addr = 0 }; + /* If the destination is a router which connects to the calculating router via a Point-to-MultiPoint network, the destination's next hop IP address(es) @@ -536,68 +602,67 @@ ospf_nexthop_calculation (struct ospf_area *area, struct vertex *v, provides an IP address of the next hop router. At this point l is a link from V to W, and V is the - root ("us"). Find the local interface associated - with l (its address is in l->link_data). If it - is a point-to-multipoint interface, then look through - the links in the opposite direction (W to V). If - any of them have an address that lands within the + root ("us"). If it is a point-to-multipoint interface, + then look through the links in the opposite direction (W to V). + If any of them have an address that lands within the subnet declared by the PtMP link, then that link - is a constituent of the PtMP link, and its address is + is a constituent of the PtMP link, and its address is a nexthop address for V. */ - oi = ospf_if_is_configured (area->ospf, &l->link_data); - if (oi && oi->type == OSPF_IFTYPE_POINTOMULTIPOINT) - { - struct prefix_ipv4 la; - - la.family = AF_INET; - la.prefixlen = oi->address->prefixlen; - - /* V links to W on PtMP interface - - find the interface address on W */ - while ((l2 = ospf_get_next_link (w, v, l2))) - { - la.prefix = l2->link_data; - - if (prefix_cmp ((struct prefix *) &la, - oi->address) == 0) - /* link_data is on our PtMP network */ - break; - } - } /* end l is on point-to-multipoint link */ - else - { - /* l is a regular point-to-point link. - Look for a link from W to V. - */ - while ((l2 = ospf_get_next_link (w, v, l2))) - { - oi = ospf_if_is_configured (area->ospf, - &(l2->link_data)); - - if (oi == NULL) - continue; - - if (!IPV4_ADDR_SAME (&oi->address->u.prefix4, - &l->link_data)) - continue; - - break; - } - } - - if (oi && l2) + if (oi->type == OSPF_IFTYPE_POINTOPOINT) + { + /* 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) + { + struct prefix_ipv4 la; + + la.family = AF_INET; + la.prefixlen = oi->address->prefixlen; + + /* V links to W on PtMP interface + - find the interface address on W */ + while ((l2 = ospf_get_next_link (w, v, l2))) + { + la.prefix = l2->link_data; + + if (prefix_cmp ((struct prefix *) &la, + oi->address) != 0) + continue; + /* link_data is on our PtMP network */ + added = 1; + nexthop = l2->link_data; + break; + } + } + + if (added) { /* found all necessary info to build nexthop */ nh = vertex_nexthop_new (); nh->oi = oi; - nh->router = l2->link_data; + nh->router = nexthop; ospf_spf_add_parent (v, w, nh, distance); return 1; } else - zlog_info("ospf_nexthop_calculation(): " - "could not determine nexthop for link"); + zlog_info("%s: could not determine nexthop for link %s", + __func__, oi->ifp->name); } /* end point-to-point link from V to W */ else if (l->m[0].type == LSA_LINK_TYPE_VIRTUALLINK) { @@ -630,19 +695,13 @@ ospf_nexthop_calculation (struct ospf_area *area, struct vertex *v, else { assert(w->type == OSPF_VERTEX_NETWORK); - oi = ospf_if_is_configured (area->ospf, &(l->link_data)); - if (oi) - { - nh = vertex_nexthop_new (); - nh->oi = oi; - nh->router.s_addr = 0; - ospf_spf_add_parent (v, w, nh, distance); - return 1; - } + + nh = vertex_nexthop_new (); + nh->oi = oi; + nh->router.s_addr = 0; /* Nexthop not required */ + ospf_spf_add_parent (v, w, nh, distance); + return 1; } - zlog_info("ospf_nexthop_calculation(): " - "Unknown attached link"); - return 0; } /* end V is the root */ /* Check if W's parent is a network connected to root. */ else if (v->type == OSPF_VERTEX_NETWORK) @@ -673,20 +732,34 @@ ospf_nexthop_calculation (struct ospf_area *area, struct vertex *v, added = 1; ospf_spf_add_parent (v, w, nh, distance); } - } + /* Note lack of return is deliberate. See next comment. */ + } } /* NB: This code is non-trivial. * * E.g. it is not enough to know that V connects to the root. It is * also important that the while above, looping through all links from * W->V found at least one link, so that we know there is - * bi-directional connectivity between V and W. Otherwise, if we - * /always/ return here, but don't check that W->V exists then we - * we will prevent SPF from finding/using higher cost paths.. + * bi-directional connectivity between V and W (which need not be the + * case, e.g. when OSPF has not yet converged fully). Otherwise, if + * we /always/ return here, without having checked that root->V->-W + * actually resulted in a valid nexthop being created, then we we will + * prevent SPF from finding/using higher cost paths. + * + * It is important, if root->V->W has not been added, that we continue + * through to the intervening-router nexthop code below. So as to + * ensure other paths to V may be used. This avoids unnecessary + * blackholes while OSPF is convergening. + * + * I.e. we may have arrived at this function, examining V -> W, via + * workable paths other than root -> V, and it's important to avoid + * getting "confused" by non-working root->V->W path - it's important + * to *not* lose the working non-root paths, just because of a + * non-viable root->V->W. * - * See also bug #330, and also: + * See also bug #330 (required reading!), and: * - * http://blogs.sun.com/paulj/entry/the_difference_a_line_makes + * http://blogs.oracle.com/paulj/entry/the_difference_a_line_makes */ if (added) return added; @@ -723,7 +796,7 @@ ospf_spf_next (struct vertex *v, struct ospf_area *area, u_char *lim; struct router_lsa_link *l = NULL; struct in_addr *r; - int type = 0; + int type = 0, lsa_pos=-1, lsa_pos_next=0; /* If this is a router-LSA, and bit V of the router-LSA (see Section A.4.2:RFC2328) is set, set Area A's TransitCapability to TRUE. */ @@ -752,6 +825,8 @@ ospf_spf_next (struct vertex *v, struct ospf_area *area, { l = (struct router_lsa_link *) p; + lsa_pos = lsa_pos_next; /* LSA link position */ + lsa_pos_next++; p += (OSPF_ROUTER_LSA_LINK_SIZE + (l->m[0].tos_count * OSPF_ROUTER_LSA_TOS_SIZE)); @@ -873,7 +948,7 @@ ospf_spf_next (struct vertex *v, struct ospf_area *area, w = ospf_vertex_new (w_lsa); /* Calculate nexthop to W. */ - if (ospf_nexthop_calculation (area, v, w, l, distance)) + if (ospf_nexthop_calculation (area, v, w, l, distance, lsa_pos)) pqueue_enqueue (w, candidate); else if (IS_DEBUG_OSPF_EVENT) zlog_debug ("Nexthop Calc failed"); @@ -893,7 +968,7 @@ ospf_spf_next (struct vertex *v, struct ospf_area *area, { /* Found an equal-cost path to W. * Calculate nexthop of to W from V. */ - ospf_nexthop_calculation (area, v, w, l, distance); + ospf_nexthop_calculation (area, v, w, l, distance, lsa_pos); } /* less than. */ else @@ -903,7 +978,7 @@ ospf_spf_next (struct vertex *v, struct ospf_area *area, * valid nexthop it will call spf_add_parents, which * will flush the old parents */ - if (ospf_nexthop_calculation (area, v, w, l, distance)) + if (ospf_nexthop_calculation (area, v, w, l, distance, lsa_pos)) /* Decrease the key of the node in the heap. * trickle-sort it up towards root, just in case this * node should now be the new root due the cost change. @@ -939,7 +1014,7 @@ ospf_spf_dump (struct vertex *v, int i) for (ALL_LIST_ELEMENTS_RO (v->parents, nnode, parent)) { zlog_debug (" nexthop %p %s %s", - parent->nexthop, + (void *)parent->nexthop, inet_ntoa (parent->nexthop->router), parent->nexthop->oi ? IF_NAME(parent->nexthop->oi) : "NULL"); @@ -969,6 +1044,7 @@ ospf_spf_process_stubs (struct ospf_area *area, struct vertex *v, u_char *lim; struct router_lsa_link *l; struct router_lsa *rlsa; + int lsa_pos = 0; if (IS_DEBUG_OSPF_EVENT) zlog_debug ("ospf_process_stubs():processing router LSA, id: %s", @@ -990,7 +1066,8 @@ ospf_spf_process_stubs (struct ospf_area *area, struct vertex *v, (l->m[0].tos_count * OSPF_ROUTER_LSA_TOS_SIZE)); if (l->m[0].type == LSA_LINK_TYPE_STUB) - ospf_intra_add_stub (rt, l, v, area, parent_is_root); + ospf_intra_add_stub (rt, l, v, area, parent_is_root, lsa_pos); + lsa_pos++; } } @@ -1198,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", + zlog_debug ("ospf_spf_calculate: Stop. %zd 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) @@ -1229,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 (); @@ -1249,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, @@ -1273,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) { @@ -1287,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; } @@ -1299,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; @@ -1311,12 +1437,14 @@ 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) { if (IS_DEBUG_OSPF_EVENT) zlog_debug ("SPF: calculation timer is already scheduled: %p", - ospf->t_spf_calc); + (void *)ospf->t_spf_calc); return; } @@ -1355,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 c1316e4cc..e33b3e5f5 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 24e81052f..47771a155 100644 --- a/ospfd/ospf_te.c +++ b/ospfd/ospf_te.c @@ -1,8 +1,11 @@ /* - * This is an implementation of draft-katz-yeung-ospf-traffic-06.txt + * This is an implementation of RFC3630 * Copyright (C) 2001 KDD R&D Laboratories, Inc. * http://www.kddlabs.co.jp/ * + * Copyright (C) 2012 Orange Labs + * http://www.orange.com + * * This file is part of GNU Zebra. * * GNU Zebra is free software; you can redistribute it and/or modify it @@ -21,15 +24,11 @@ * 02111-1307, USA. */ -/***** MTYPE definition is not reflected to "memory.h" yet. *****/ -#define MTYPE_OSPF_MPLS_TE_LINKPARAMS 0 +/* Add support of RFC7471 */ +/* Add support of RFC5392, RFC6827 */ #include - -#ifdef HAVE_OSPF_TE -#ifndef HAVE_OPAQUE_LSA -#error "Wrong configure option" -#endif /* HAVE_OPAQUE_LSA */ +#include #include "linklist.h" #include "prefix.h" @@ -43,6 +42,7 @@ #include "thread.h" #include "hash.h" #include "sockunion.h" /* for inet_aton() */ +#include "network.h" #include "ospfd/ospfd.h" #include "ospfd/ospf_interface.h" @@ -60,65 +60,19 @@ #include "ospfd/ospf_ase.h" #include "ospfd/ospf_zebra.h" #include "ospfd/ospf_te.h" - -/* Following structure are internal use only. */ -struct ospf_mpls_te -{ - enum { disabled, enabled } status; - - /* List elements are zebra-interfaces (ifp), not ospf-interfaces (oi). */ - struct list *iflist; - - /* Store Router-TLV in network byte order. */ - struct te_tlv_router_addr router_addr; -}; - -struct mpls_te_link -{ - /* - * According to MPLS-TE (draft) specification, 24-bit Opaque-ID field - * is subdivided into 8-bit "unused" field and 16-bit "instance" field. - * In this implementation, each Link-TLV has its own instance. - */ - u_int32_t instance; - - /* Reference pointer to a Zebra-interface. */ - struct interface *ifp; - - /* Area info in which this MPLS-TE link belongs to. */ - struct ospf_area *area; - - /* Flags to manage this link parameters. */ - u_int32_t flags; -#define LPFLG_LOOKUP_DONE 0x1 -#define LPFLG_LSA_ENGAGED 0x2 -#define LPFLG_LSA_FORCED_REFRESH 0x4 - - /* Store Link-TLV in network byte order. */ - struct te_tlv_link link_header; - struct te_link_subtlv_link_type link_type; - struct te_link_subtlv_link_id link_id; - struct te_link_subtlv_lclif_ipaddr *lclif_ipaddr; - struct te_link_subtlv_rmtif_ipaddr *rmtif_ipaddr; - struct te_link_subtlv_te_metric te_metric; - struct te_link_subtlv_max_bw max_bw; - struct te_link_subtlv_max_rsv_bw max_rsv_bw; - struct te_link_subtlv_unrsv_bw unrsv_bw; - struct te_link_subtlv_rsc_clsclr rsc_clsclr; -}; +#include "ospfd/ospf_vty.h" /* * Global variable to manage Opaque-LSA/MPLS-TE on this node. * Note that all parameter values are stored in network byte order. */ -static struct ospf_mpls_te OspfMplsTE; +struct ospf_mpls_te OspfMplsTE; -enum oifstate { - OI_ANY, OI_DOWN, OI_UP -}; +const char *mode2text[] = { "Disable", "AS", "Area", "Emulate" }; -enum sched_opcode { - REORIGINATE_PER_AREA, REFRESH_THIS_LSA, FLUSH_THIS_LSA +enum oifstate +{ + OI_ANY, OI_DOWN, OI_UP }; /*------------------------------------------------------------------------* @@ -127,14 +81,14 @@ enum sched_opcode { static int ospf_mpls_te_new_if (struct interface *ifp); static int ospf_mpls_te_del_if (struct interface *ifp); -static void ospf_mpls_te_ism_change (struct ospf_interface *oi, int old_status); +static void ospf_mpls_te_ism_change (struct ospf_interface *oi, + int old_status); static void ospf_mpls_te_nsm_change (struct ospf_neighbor *nbr, int old_status); static void ospf_mpls_te_config_write_router (struct vty *vty); -static void ospf_mpls_te_config_write_if (struct vty *vty, struct interface *ifp); static void ospf_mpls_te_show_info (struct vty *vty, struct ospf_lsa *lsa); -static int ospf_mpls_te_lsa_originate (void *arg); +static int ospf_mpls_te_lsa_originate_area (void *arg); +static int ospf_mpls_te_lsa_originate_as (void *arg); static struct ospf_lsa *ospf_mpls_te_lsa_refresh (struct ospf_lsa *lsa); -static void ospf_mpls_te_lsa_schedule (struct mpls_te_link *lp, enum sched_opcode); static void del_mpls_te_link (void *val); static void ospf_mpls_te_register_vty (void); @@ -152,21 +106,22 @@ ospf_mpls_te_init (void) ospf_mpls_te_ism_change, ospf_mpls_te_nsm_change, ospf_mpls_te_config_write_router, - ospf_mpls_te_config_write_if, + NULL,/*ospf_mpls_te_config_write_if */ NULL,/* ospf_mpls_te_config_write_debug */ ospf_mpls_te_show_info, - ospf_mpls_te_lsa_originate, + ospf_mpls_te_lsa_originate_area, ospf_mpls_te_lsa_refresh, NULL,/* ospf_mpls_te_new_lsa_hook */ NULL /* ospf_mpls_te_del_lsa_hook */); if (rc != 0) { - zlog_warn ("ospf_mpls_te_init: Failed to register functions"); + zlog_warn ("ospf_mpls_te_init: Failed to register Traffic Engineering functions"); goto out; } memset (&OspfMplsTE, 0, sizeof (struct ospf_mpls_te)); OspfMplsTE.status = disabled; + OspfMplsTE.inter_as = Disable; OspfMplsTE.iflist = list_new (); OspfMplsTE.iflist->del = del_mpls_te_link; @@ -176,16 +131,75 @@ ospf_mpls_te_init (void) return rc; } +/* Additional register for RFC5392 support */ +static int +ospf_mpls_te_register (enum inter_as_mode mode) +{ + int rc; + u_int8_t scope; + + if (OspfMplsTE.inter_as != Disable) + return 0; + + if (mode == AS) + scope = OSPF_OPAQUE_AS_LSA; + else + scope = OSPF_OPAQUE_AREA_LSA; + + rc = ospf_register_opaque_functab (scope, + OPAQUE_TYPE_INTER_AS_LSA, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + ospf_mpls_te_show_info, + ospf_mpls_te_lsa_originate_as, + ospf_mpls_te_lsa_refresh, NULL, NULL); + + if (rc != 0) + { + zlog_warn ("ospf_router_info_init: Failed to register Inter-AS functions"); + return rc; + } + + return 0; +} + +static int +ospf_mpls_te_unregister () +{ + u_int8_t scope; + + if (OspfMplsTE.inter_as == Disable) + return 0; + + if (OspfMplsTE.inter_as == AS) + scope = OSPF_OPAQUE_AS_LSA; + else + scope = OSPF_OPAQUE_AREA_LSA; + + ospf_delete_opaque_functab (scope, OPAQUE_TYPE_INTER_AS_LSA); + + return 0; + +} + void ospf_mpls_te_term (void) { list_delete (OspfMplsTE.iflist); - OspfMplsTE.iflist = NULL; - OspfMplsTE.status = disabled; ospf_delete_opaque_functab (OSPF_OPAQUE_AREA_LSA, OPAQUE_TYPE_TRAFFIC_ENGINEERING_LSA); + OspfMplsTE.status = disabled; + + ospf_mpls_te_unregister (); + OspfMplsTE.inter_as = Disable; + return; } @@ -196,16 +210,16 @@ ospf_mpls_te_term (void) static void del_mpls_te_link (void *val) { - XFREE (MTYPE_OSPF_MPLS_TE_LINKPARAMS, val); + XFREE (MTYPE_OSPF_MPLS_TE, val); return; } -static u_int32_t +u_int32_t 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. */ @@ -278,9 +292,9 @@ lookup_linkparams_by_instance (struct ospf_lsa *lsa) } static void -ospf_mpls_te_foreach_area ( - void (*func)(struct mpls_te_link *lp, enum sched_opcode), - enum sched_opcode sched_opcode) +ospf_mpls_te_foreach_area (void (*func) + (struct mpls_te_link * lp, opcode_t sched_opcode), + opcode_t sched_opcode) { struct listnode *node, *nnode; struct listnode *node2; @@ -289,10 +303,12 @@ ospf_mpls_te_foreach_area ( for (ALL_LIST_ELEMENTS (OspfMplsTE.iflist, node, nnode, lp)) { - if ((area = lp->area) == NULL) + /* Skip Inter-AS TEv2 Links */ + if (IS_INTER_AS (lp->type)) continue; - if (lp->flags & LPFLG_LOOKUP_DONE) + if ((area = lp->area) == NULL) continue; + if CHECK_FLAG (lp->flags, LPFLG_LOOKUP_DONE) continue; if (func != NULL) (* func)(lp, sched_opcode); @@ -301,12 +317,12 @@ ospf_mpls_te_foreach_area ( if ((lp = listgetdata (node2)) != NULL) if (lp->area != NULL) if (IPV4_ADDR_SAME (&lp->area->area_id, &area->area_id)) - lp->flags |= LPFLG_LOOKUP_DONE; + SET_FLAG (lp->flags, LPFLG_LOOKUP_DONE); } for (ALL_LIST_ELEMENTS_RO (OspfMplsTE.iflist, node, lp)) if (lp->area != NULL) - lp->flags &= ~LPFLG_LOOKUP_DONE; + UNSET_FLAG (lp->flags, LPFLG_LOOKUP_DONE); return; } @@ -315,7 +331,7 @@ static void set_mpls_te_router_addr (struct in_addr ipv4) { OspfMplsTE.router_addr.header.type = htons (TE_TLV_ROUTER_ADDR); - OspfMplsTE.router_addr.header.length = htons (sizeof (ipv4)); + OspfMplsTE.router_addr.header.length = htons (TE_LINK_SUBTLV_DEF_SIZE); OspfMplsTE.router_addr.value = ipv4; return; } @@ -323,7 +339,6 @@ set_mpls_te_router_addr (struct in_addr ipv4) static void set_linkparams_link_header (struct mpls_te_link *lp) { - struct te_tlv_header *tlvh; u_int16_t length = 0; /* TE_LINK_SUBTLV_LINK_TYPE */ @@ -335,14 +350,12 @@ set_linkparams_link_header (struct mpls_te_link *lp) length += TLV_SIZE (&lp->link_id.header); /* TE_LINK_SUBTLV_LCLIF_IPADDR */ - if ((tlvh = (struct te_tlv_header *) lp->lclif_ipaddr) != NULL - && ntohs (tlvh->type) != 0) - length += TLV_SIZE (tlvh); + if (lp->lclif_ipaddr.header.type != 0) + length += TLV_SIZE (&lp->lclif_ipaddr.header); /* TE_LINK_SUBTLV_RMTIF_IPADDR */ - if ((tlvh = (struct te_tlv_header *) lp->rmtif_ipaddr) != NULL - && ntohs (tlvh->type) != 0) - length += TLV_SIZE (tlvh); + if (lp->rmtif_ipaddr.header.type != 0) + length += TLV_SIZE (&lp->rmtif_ipaddr.header); /* TE_LINK_SUBTLV_TE_METRIC */ if (ntohs (lp->te_metric.header.type) != 0) @@ -364,6 +377,50 @@ set_linkparams_link_header (struct mpls_te_link *lp) if (ntohs (lp->rsc_clsclr.header.type) != 0) length += TLV_SIZE (&lp->rsc_clsclr.header); + /* TE_LINK_SUBTLV_LLRI */ + if (ntohs (lp->llri.header.type) != 0) + length += TLV_SIZE (&lp->llri.header); + + /* TE_LINK_SUBTLV_RIP */ + if (ntohs (lp->rip.header.type) != 0) + length += TLV_SIZE (&lp->rip.header); + + /* TE_LINK_SUBTLV_RAS */ + if (ntohs (lp->ras.header.type) != 0) + length += TLV_SIZE (&lp->ras.header); + + /* TE_LINK_SUBTLV_LRRID */ + if (ntohs (lp->lrrid.header.type) != 0) + length += TLV_SIZE (&lp->lrrid.header); + + /* TE_LINK_SUBTLV_AV_DELAY */ + if (ntohs (lp->av_delay.header.type) != 0) + length += TLV_SIZE (&lp->av_delay.header); + + /* TE_LINK_SUBTLV_MM_DELAY */ + if (ntohs (lp->mm_delay.header.type) != 0) + length += TLV_SIZE (&lp->mm_delay.header); + + /* TE_LINK_SUBTLV_DELAY_VAR */ + if (ntohs (lp->delay_var.header.type) != 0) + length += TLV_SIZE (&lp->delay_var.header); + + /* TE_LINK_SUBTLV_PKT_LOSS */ + if (ntohs (lp->pkt_loss.header.type) != 0) + length += TLV_SIZE (&lp->pkt_loss.header); + + /* TE_LINK_SUBTLV_RES_BW */ + if (ntohs (lp->res_bw.header.type) != 0) + length += TLV_SIZE (&lp->res_bw.header); + + /* TE_LINK_SUBTLV_AVA_BW */ + if (ntohs (lp->ava_bw.header.type) != 0) + length += TLV_SIZE (&lp->ava_bw.header); + + /* TE_LINK_SUBTLV_USE_BW */ + if (ntohs (lp->use_bw.header.type) != 0) + length += TLV_SIZE (&lp->use_bw.header); + lp->link_header.header.type = htons (TE_TLV_LINK); lp->link_header.header.length = htons (length); @@ -374,7 +431,7 @@ static void set_linkparams_link_type (struct ospf_interface *oi, struct mpls_te_link *lp) { lp->link_type.header.type = htons (TE_LINK_SUBTLV_LINK_TYPE); - lp->link_type.header.length = htons (sizeof (lp->link_type.link_type.value)); + lp->link_type.header.length = htons (TE_LINK_SUBTLV_TYPE_SIZE); switch (oi->type) { @@ -400,7 +457,7 @@ set_linkparams_link_id (struct ospf_interface *oi, struct mpls_te_link *lp) int done = 0; lp->link_id.header.type = htons (TE_LINK_SUBTLV_LINK_ID); - lp->link_id.header.length = htons (sizeof (lp->link_id.value)); + lp->link_id.header.length = htons (TE_LINK_SUBTLV_DEF_SIZE); /* * The Link ID is identical to the contents of the Link ID field @@ -410,8 +467,7 @@ set_linkparams_link_id (struct ospf_interface *oi, struct mpls_te_link *lp) { case OSPF_IFTYPE_POINTOPOINT: /* Take the router ID of the neighbor. */ - if ((nbr = ospf_nbr_lookup_ptop (oi)) - && nbr->state == NSM_Full) + if ((nbr = ospf_nbr_lookup_ptop (oi)) && nbr->state == NSM_Full) { lp->link_id.value = nbr->router_id; done = 1; @@ -446,40 +502,60 @@ set_linkparams_link_id (struct ospf_interface *oi, struct mpls_te_link *lp) return; } +static void +set_linkparams_lclif_ipaddr (struct mpls_te_link *lp, struct in_addr lclif) +{ + + lp->lclif_ipaddr.header.type = htons (TE_LINK_SUBTLV_LCLIF_IPADDR); + lp->lclif_ipaddr.header.length = htons (TE_LINK_SUBTLV_DEF_SIZE); + lp->lclif_ipaddr.value[0] = lclif; + return; +} + +static void +set_linkparams_rmtif_ipaddr (struct mpls_te_link *lp, struct in_addr rmtif) +{ + + lp->rmtif_ipaddr.header.type = htons (TE_LINK_SUBTLV_RMTIF_IPADDR); + lp->rmtif_ipaddr.header.length = htons (TE_LINK_SUBTLV_DEF_SIZE); + lp->rmtif_ipaddr.value[0] = rmtif; + return; +} + static void set_linkparams_te_metric (struct mpls_te_link *lp, u_int32_t te_metric) { lp->te_metric.header.type = htons (TE_LINK_SUBTLV_TE_METRIC); - lp->te_metric.header.length = htons (sizeof (lp->te_metric.value)); + lp->te_metric.header.length = htons (TE_LINK_SUBTLV_DEF_SIZE); lp->te_metric.value = htonl (te_metric); return; } static void -set_linkparams_max_bw (struct mpls_te_link *lp, float *fp) +set_linkparams_max_bw (struct mpls_te_link *lp, float fp) { lp->max_bw.header.type = htons (TE_LINK_SUBTLV_MAX_BW); - lp->max_bw.header.length = htons (sizeof (lp->max_bw.value)); - htonf (fp, &lp->max_bw.value); + lp->max_bw.header.length = htons (TE_LINK_SUBTLV_DEF_SIZE); + lp->max_bw.value = htonf (fp); return; } static void -set_linkparams_max_rsv_bw (struct mpls_te_link *lp, float *fp) +set_linkparams_max_rsv_bw (struct mpls_te_link *lp, float fp) { lp->max_rsv_bw.header.type = htons (TE_LINK_SUBTLV_MAX_RSV_BW); - lp->max_rsv_bw.header.length = htons (sizeof (lp->max_rsv_bw.value)); - htonf (fp, &lp->max_rsv_bw.value); + lp->max_rsv_bw.header.length = htons (TE_LINK_SUBTLV_DEF_SIZE); + lp->max_rsv_bw.value = htonf (fp); return; } static void -set_linkparams_unrsv_bw (struct mpls_te_link *lp, int priority, float *fp) +set_linkparams_unrsv_bw (struct mpls_te_link *lp, int priority, float fp) { /* Note that TLV-length field is the size of array. */ lp->unrsv_bw.header.type = htons (TE_LINK_SUBTLV_UNRSV_BW); - lp->unrsv_bw.header.length = htons (sizeof (lp->unrsv_bw.value)); - htonf (fp, &lp->unrsv_bw.value [priority]); + lp->unrsv_bw.header.length = htons (TE_LINK_SUBTLV_UNRSV_SIZE); + lp->unrsv_bw.value [priority] = htonf (fp); return; } @@ -487,21 +563,283 @@ static void set_linkparams_rsc_clsclr (struct mpls_te_link *lp, u_int32_t classcolor) { lp->rsc_clsclr.header.type = htons (TE_LINK_SUBTLV_RSC_CLSCLR); - lp->rsc_clsclr.header.length = htons (sizeof (lp->rsc_clsclr.value)); + lp->rsc_clsclr.header.length = htons (TE_LINK_SUBTLV_DEF_SIZE); lp->rsc_clsclr.value = htonl (classcolor); return; } +static void +set_linkparams_inter_as (struct mpls_te_link *lp, struct in_addr addr, + u_int32_t as) +{ + + /* Set the Remote ASBR IP address and then the associated AS number */ + lp->rip.header.type = htons (TE_LINK_SUBTLV_RIP); + lp->rip.header.length = htons (TE_LINK_SUBTLV_DEF_SIZE); + lp->rip.value = addr; + + lp->ras.header.type = htons (TE_LINK_SUBTLV_RAS); + lp->ras.header.length = htons (TE_LINK_SUBTLV_DEF_SIZE); + lp->ras.value = htonl (as); +} + +static void +unset_linkparams_inter_as (struct mpls_te_link *lp) +{ + + /* Reset the Remote ASBR IP address and then the associated AS number */ + lp->rip.header.type = htons (0); + lp->rip.header.length = htons (0); + lp->rip.value.s_addr = htonl (0); + + lp->ras.header.type = htons (0); + lp->ras.header.length = htons (0); + lp->ras.value = htonl (0); +} + +void +set_linkparams_llri (struct mpls_te_link *lp, u_int32_t local, + u_int32_t remote) +{ + + lp->llri.header.type = htons (TE_LINK_SUBTLV_LLRI); + lp->llri.header.length = htons (TE_LINK_SUBTLV_LLRI_SIZE); + lp->llri.local = htonl (local); + lp->llri.remote = htonl (remote); +} + +void +set_linkparams_lrrid (struct mpls_te_link *lp, struct in_addr local, + struct in_addr remote) +{ + + lp->lrrid.header.type = htons (TE_LINK_SUBTLV_LRRID); + lp->lrrid.header.length = htons (TE_LINK_SUBTLV_LRRID_SIZE); + lp->lrrid.local.s_addr = local.s_addr; + lp->lrrid.remote.s_addr = remote.s_addr; +} + +static void +set_linkparams_av_delay (struct mpls_te_link *lp, u_int32_t delay, u_char anormal) +{ + u_int32_t tmp; + /* Note that TLV-length field is the size of array. */ + lp->av_delay.header.type = htons (TE_LINK_SUBTLV_AV_DELAY); + lp->av_delay.header.length = htons (TE_LINK_SUBTLV_DEF_SIZE); + tmp = delay & TE_EXT_MASK; + if (anormal) + tmp |= TE_EXT_ANORMAL; + lp->av_delay.value = htonl (tmp); + return; +} + +static void +set_linkparams_mm_delay (struct mpls_te_link *lp, u_int32_t low, u_int32_t high, u_char anormal) +{ + u_int32_t tmp; + /* Note that TLV-length field is the size of array. */ + lp->mm_delay.header.type = htons (TE_LINK_SUBTLV_MM_DELAY); + lp->mm_delay.header.length = htons (TE_LINK_SUBTLV_MM_DELAY_SIZE); + tmp = low & TE_EXT_MASK; + if (anormal) + tmp |= TE_EXT_ANORMAL; + lp->mm_delay.low = htonl (tmp); + lp->mm_delay.high = htonl (high); + return; +} + +static void +set_linkparams_delay_var (struct mpls_te_link *lp, u_int32_t jitter) +{ + /* Note that TLV-length field is the size of array. */ + lp->delay_var.header.type = htons (TE_LINK_SUBTLV_DELAY_VAR); + lp->delay_var.header.length = htons (TE_LINK_SUBTLV_DEF_SIZE); + lp->delay_var.value = htonl (jitter & TE_EXT_MASK); + return; +} + +static void +set_linkparams_pkt_loss (struct mpls_te_link *lp, u_int32_t loss, u_char anormal) +{ + u_int32_t tmp; + /* Note that TLV-length field is the size of array. */ + lp->pkt_loss.header.type = htons (TE_LINK_SUBTLV_PKT_LOSS); + lp->pkt_loss.header.length = htons (TE_LINK_SUBTLV_DEF_SIZE); + tmp = loss & TE_EXT_MASK; + if (anormal) + tmp |= TE_EXT_ANORMAL; + lp->pkt_loss.value = htonl (tmp); + return; +} + +static void +set_linkparams_res_bw (struct mpls_te_link *lp, float fp) +{ + /* Note that TLV-length field is the size of array. */ + lp->res_bw.header.type = htons (TE_LINK_SUBTLV_RES_BW); + lp->res_bw.header.length = htons (TE_LINK_SUBTLV_DEF_SIZE); + lp->res_bw.value = htonf (fp); + return; +} + +static void +set_linkparams_ava_bw (struct mpls_te_link *lp, float fp) +{ + /* Note that TLV-length field is the size of array. */ + lp->ava_bw.header.type = htons (TE_LINK_SUBTLV_AVA_BW); + lp->ava_bw.header.length = htons (TE_LINK_SUBTLV_DEF_SIZE); + lp->ava_bw.value = htonf (fp); + return; +} + +static void +set_linkparams_use_bw (struct mpls_te_link *lp, float fp) +{ + /* Note that TLV-length field is the size of array. */ + lp->use_bw.header.type = htons (TE_LINK_SUBTLV_USE_BW); + lp->use_bw.header.length = htons (TE_LINK_SUBTLV_DEF_SIZE); + lp->use_bw.value = htonf (fp); + return; +} + +/* Update TE parameters from Interface */ +static void +update_linkparams(struct mpls_te_link *lp) +{ + int i; + struct interface *ifp; + + /* Get the Interface structure */ + if ((ifp = lp->ifp) == NULL) + { + zlog_warn("OSPF MPLS-TE: Abort update TE parameters: no interface associated to Link Parameters"); + return; + } + if (!HAS_LINK_PARAMS(ifp)) + { + zlog_warn("OSPF MPLS-TE: Abort update TE parameters: no Link Parameters for interface"); + return; + } + + /* RFC3630 metrics */ + if (IS_PARAM_SET(ifp->link_params, LP_ADM_GRP)) + set_linkparams_rsc_clsclr (lp, ifp->link_params->admin_grp); + else + TLV_TYPE(lp->rsc_clsclr) = 0; + + if (IS_PARAM_SET(ifp->link_params, LP_MAX_BW)) + set_linkparams_max_bw (lp, ifp->link_params->max_bw); + else + TLV_TYPE(lp->max_bw) = 0; + + if (IS_PARAM_SET(ifp->link_params, LP_MAX_RSV_BW)) + set_linkparams_max_rsv_bw (lp, ifp->link_params->max_rsv_bw); + else + TLV_TYPE(lp->max_rsv_bw) = 0; + + if (IS_PARAM_SET(ifp->link_params, LP_UNRSV_BW)) + for (i = 0; i < MAX_CLASS_TYPE; i++) + set_linkparams_unrsv_bw (lp, i, ifp->link_params->unrsv_bw[i]); + else + TLV_TYPE(lp->unrsv_bw) = 0; + + if (IS_PARAM_SET(ifp->link_params, LP_TE)) + set_linkparams_te_metric(lp, ifp->link_params->te_metric); + else + TLV_TYPE(lp->te_metric) = 0; + + /* TE metric Extensions */ + if (IS_PARAM_SET(ifp->link_params, LP_DELAY)) + set_linkparams_av_delay(lp, ifp->link_params->av_delay, 0); + else + TLV_TYPE(lp->av_delay) = 0; + + if (IS_PARAM_SET(ifp->link_params, LP_MM_DELAY)) + set_linkparams_mm_delay(lp, ifp->link_params->min_delay, ifp->link_params->max_delay, 0); + else + TLV_TYPE(lp->mm_delay) = 0; + + if (IS_PARAM_SET(ifp->link_params, LP_DELAY_VAR)) + set_linkparams_delay_var(lp, ifp->link_params->delay_var); + else + TLV_TYPE(lp->delay_var) = 0; + + if (IS_PARAM_SET(ifp->link_params, LP_PKT_LOSS)) + set_linkparams_pkt_loss(lp, ifp->link_params->pkt_loss, 0); + else + TLV_TYPE(lp->pkt_loss) = 0; + + if (IS_PARAM_SET(ifp->link_params, LP_RES_BW)) + set_linkparams_res_bw(lp, ifp->link_params->res_bw); + else + TLV_TYPE(lp->res_bw) = 0; + + if (IS_PARAM_SET(ifp->link_params, LP_AVA_BW)) + set_linkparams_ava_bw(lp, ifp->link_params->ava_bw); + else + TLV_TYPE(lp->ava_bw) = 0; + + if (IS_PARAM_SET(ifp->link_params, LP_USE_BW)) + set_linkparams_use_bw(lp, ifp->link_params->use_bw); + else + TLV_TYPE(lp->use_bw) = 0; + + /* RFC5392 */ + if (IS_PARAM_SET(ifp->link_params, LP_RMT_AS)) + { + /* Flush LSA if it engaged and was previously a STD_TE one */ + if (IS_STD_TE(lp->type) && CHECK_FLAG (lp->flags, LPFLG_LSA_ENGAGED)) + { + if (IS_DEBUG_OSPF_TE) + zlog_debug("OSPF MPLS-TE Update IF: Switch from Standard LSA to INTER-AS for %s[%d/%d]", + ifp->name, lp->flags, lp->type); + + ospf_mpls_te_lsa_schedule (lp, FLUSH_THIS_LSA); + /* Then, switch it to INTER-AS */ + if (OspfMplsTE.inter_as == AS) + lp->flags = INTER_AS | FLOOD_AS; + else + { + lp->flags = INTER_AS | FLOOD_AREA; + lp->area = ospf_area_lookup_by_area_id (ospf_lookup(), OspfMplsTE.interas_areaid); + } + } + set_linkparams_inter_as(lp, ifp->link_params->rmt_ip, ifp->link_params->rmt_as); + } + else + { + if (IS_DEBUG_OSPF_TE) + zlog_debug("OSPF MPLS-TE Update IF: Switch from INTER-AS LSA to Standard for %s[%d/%d]", + ifp->name, lp->flags, lp->type); + + /* reset inter-as TE params */ + /* Flush LSA if it engaged and was previously an INTER_AS one */ + if (IS_INTER_AS(lp->type) && CHECK_FLAG (lp->flags, LPFLG_LSA_ENGAGED)) + { + ospf_mpls_te_lsa_schedule (lp, FLUSH_THIS_LSA); + /* Then, switch it to Standard TE */ + lp->flags = STD_TE | FLOOD_AREA; + } + unset_linkparams_inter_as (lp); + } +} + static void initialize_linkparams (struct mpls_te_link *lp) { struct interface *ifp = lp->ifp; struct ospf_interface *oi; - float fval; - int i; + + if (IS_DEBUG_OSPF_TE) + zlog_debug("MPLS-TE(initialize_linkparams) Initialize Link Parameters for interface %s", + ifp->name); if ((oi = lookup_oi_by_ifp (ifp, NULL, OI_ANY)) == NULL) - return; + { + zlog_warn("MPLS-TE(initialize_linkparams) Could not find corresponding OSPF Interface for %s", + ifp->name); + return; + } /* * Try to set initial values those can be derived from @@ -509,18 +847,19 @@ initialize_linkparams (struct mpls_te_link *lp) */ set_linkparams_link_type (oi, lp); - /* - * Linux and *BSD kernel holds bandwidth parameter as an "int" type. - * We may have to reconsider, if "ifp->bandwidth" type changes to float. - */ - fval = (float)((ifp->bandwidth ? ifp->bandwidth - : OSPF_DEFAULT_BANDWIDTH) * 1000 / 8); + /* Set local IP addr */ + set_linkparams_lclif_ipaddr (lp, oi->address->u.prefix4); - set_linkparams_max_bw (lp, &fval); - set_linkparams_max_rsv_bw (lp, &fval); + /* Set Remote IP addr if Point to Point Interface */ + if (oi->type == LINK_TYPE_SUBTLV_VALUE_PTP) + { + struct prefix *pref = CONNECTED_PREFIX(oi->connected); + if (pref != NULL) + set_linkparams_rmtif_ipaddr(lp, pref->u.prefix4); + } - for (i = 0; i < 8; i++) - set_linkparams_unrsv_bw (lp, i, &fval); + /* Keep Area information in combination with link parameters. */ + lp->area = oi->area; return; } @@ -531,13 +870,22 @@ is_mandated_params_set (struct mpls_te_link *lp) int rc = 0; if (ntohs (OspfMplsTE.router_addr.header.type) == 0) + { + zlog_warn ("MPLS-TE(is_mandated_params_set) Missing Router Address"); goto out; + } if (ntohs (lp->link_type.header.type) == 0) + { + zlog_warn ("MPLS-TE(is_mandated_params_set) Missing Link Type"); goto out; + } - if (ntohs (lp->link_id.header.type) == 0) + if (!IS_INTER_AS (lp->type) && (ntohs (lp->link_id.header.type) == 0)) + { + zlog_warn ("MPLS-TE(is_mandated_params_set) Missing Link ID"); goto out; + } rc = 1; out: @@ -554,30 +902,44 @@ ospf_mpls_te_new_if (struct interface *ifp) struct mpls_te_link *new; int rc = -1; + if (IS_DEBUG_OSPF_TE) + zlog_debug ("MPLS-TE(ospf_mpls_te_new_if) Add new %s interface %s to MPLS-TE list", + ifp->link_params ? "Active" : "Inactive", ifp->name); + if (lookup_linkparams_by_ifp (ifp) != NULL) { - zlog_warn ("ospf_mpls_te_new_if: ifp(%p) already in use?", ifp); + zlog_warn ("ospf_mpls_te_new_if: ifp(%p) already in use?", (void *)ifp); rc = 0; /* Do nothing here. */ goto out; } - new = XCALLOC (MTYPE_OSPF_MPLS_TE_LINKPARAMS, - sizeof (struct mpls_te_link)); + new = XCALLOC (MTYPE_OSPF_MPLS_TE, sizeof (struct mpls_te_link)); if (new == NULL) { zlog_warn ("ospf_mpls_te_new_if: XMALLOC: %s", safe_strerror (errno)); goto out; } - new->area = NULL; - new->flags = 0; new->instance = get_mpls_te_instance_value (); new->ifp = ifp; + /* By default TE-Link is RFC3630 compatible flooding in Area and not active */ + /* This default behavior will be adapted with call to ospf_mpls_te_update_if() */ + new->type = STD_TE | FLOOD_AREA; + new->flags = LPFLG_LSA_INACTIVE; - initialize_linkparams (new); + /* Initialize Link Parameters from Interface */ + initialize_linkparams(new); + /* Set TE Parameters from Interface */ + update_linkparams(new); + + /* Add Link Parameters structure to the list */ listnode_add (OspfMplsTE.iflist, new); + if (IS_DEBUG_OSPF_TE) + zlog_debug("OSPF MPLS-TE New IF: Add new LP context for %s[%d/%d]", + ifp->name, new->flags, new->type); + /* Schedule Opaque-LSA refresh. *//* XXX */ rc = 0; @@ -602,7 +964,7 @@ ospf_mpls_te_del_if (struct interface *ifp) if (listcount (iflist) == 0) iflist->head = iflist->tail = NULL; - XFREE (MTYPE_OSPF_MPLS_TE_LINKPARAMS, lp); + XFREE (MTYPE_OSPF_MPLS_TE, lp); } /* Schedule Opaque-LSA refresh. *//* XXX */ @@ -612,6 +974,56 @@ ospf_mpls_te_del_if (struct interface *ifp) return rc; } +/* Main initialization / update function of the MPLS TE Link context */ + +/* Call when interface TE Link parameters are modified */ +void +ospf_mpls_te_update_if (struct interface *ifp) +{ + struct mpls_te_link *lp; + + if (IS_DEBUG_OSPF_TE) + zlog_debug ("OSPF MPLS-TE: Update LSA parameters for interface %s [%s]", + ifp->name, HAS_LINK_PARAMS(ifp) ? "ON" : "OFF"); + + /* Get Link context from interface */ + if ((lp = lookup_linkparams_by_ifp(ifp)) == NULL) + { + zlog_warn ("OSPF MPLS-TE Update: Did not find Link Parameters context for interface %s", ifp->name); + return; + } + + /* Fulfill MPLS-TE Link TLV from Interface TE Link parameters */ + if (HAS_LINK_PARAMS(ifp)) + { + SET_FLAG (lp->flags, LPFLG_LSA_ACTIVE); + + /* Update TE parameters */ + update_linkparams(lp); + + /* Finally Re-Originate or Refresh Opaque LSA if MPLS_TE is enabled */ + if (OspfMplsTE.status == enabled) + if (lp->area != NULL) + { + if CHECK_FLAG (lp->flags, LPFLG_LSA_ENGAGED) + ospf_mpls_te_lsa_schedule (lp, REFRESH_THIS_LSA); + else + ospf_mpls_te_lsa_schedule (lp, REORIGINATE_THIS_LSA); + } + } + else + { + /* If MPLS TE is disable on this interface, flush LSA if it is already engaged */ + if CHECK_FLAG (lp->flags, LPFLG_LSA_ENGAGED) + ospf_mpls_te_lsa_schedule (lp, FLUSH_THIS_LSA); + else + /* Reset Activity flag */ + lp->flags = LPFLG_LSA_INACTIVE; + } + + return; +} + static void ospf_mpls_te_ism_change (struct ospf_interface *oi, int old_state) { @@ -624,10 +1036,10 @@ ospf_mpls_te_ism_change (struct ospf_interface *oi, int old_state) zlog_warn ("ospf_mpls_te_ism_change: Cannot get linkparams from OI(%s)?", IF_NAME (oi)); goto out; } + if (oi->area == NULL || oi->area->ospf == NULL) { - zlog_warn ("ospf_mpls_te_ism_change: Cannot refer to OSPF from OI(%s)?", -IF_NAME (oi)); + zlog_warn ("ospf_mpls_te_ism_change: Cannot refer to OSPF from OI(%s)?", IF_NAME (oi)); goto out; } #ifdef notyet @@ -636,13 +1048,17 @@ IF_NAME (oi)); || (lp->area != NULL && oi->area == NULL)) { /* How should we consider this case? */ - zlog_warn ("MPLS-TE: Area for OI(%s) has changed to [%s], flush previous LSAs", IF_NAME (oi), oi->area ? inet_ntoa (oi->area->area_id) : "N/A"); + zlog_warn ("MPLS-TE: Area for OI(%s) has changed to [%s], flush previous LSAs", + IF_NAME (oi), oi->area ? inet_ntoa (oi->area->area_id) : "N/A"); ospf_mpls_te_lsa_schedule (lp, FLUSH_THIS_LSA); } #endif - /* Keep Area information in conbination with linkparams. */ + /* Keep Area information in combination with linkparams. */ lp->area = oi->area; + /* Keep interface MPLS-TE status */ + lp->flags = HAS_LINK_PARAMS(oi->ifp); + switch (oi->state) { case ISM_PointToPoint: @@ -652,37 +1068,53 @@ IF_NAME (oi)); old_type = lp->link_type; old_id = lp->link_id; + /* Set Link type, Link ID, Local and Remote IP addr */ set_linkparams_link_type (oi, lp); set_linkparams_link_id (oi, lp); + set_linkparams_lclif_ipaddr (lp, oi->address->u.prefix4); + + if (oi->type == LINK_TYPE_SUBTLV_VALUE_PTP) + { + struct prefix *pref = CONNECTED_PREFIX(oi->connected); + if (pref != NULL) + set_linkparams_rmtif_ipaddr(lp, pref->u.prefix4); + } + + /* Update TE parameters */ + update_linkparams(lp); + /* Try to Schedule LSA */ if ((ntohs (old_type.header.type) != ntohs (lp->link_type.header.type) || old_type.link_type.value != lp->link_type.link_type.value) || (ntohs (old_id.header.type) != ntohs (lp->link_id.header.type) - || ntohl (old_id.value.s_addr) != ntohl (lp->link_id.value.s_addr))) + || ntohl (old_id.value.s_addr) != + ntohl (lp->link_id.value.s_addr))) { - if (lp->flags & LPFLG_LSA_ENGAGED) + if CHECK_FLAG (lp->flags, LPFLG_LSA_ENGAGED) ospf_mpls_te_lsa_schedule (lp, REFRESH_THIS_LSA); else - ospf_mpls_te_lsa_schedule (lp, REORIGINATE_PER_AREA); + ospf_mpls_te_lsa_schedule (lp, REORIGINATE_THIS_LSA); + } break; default: lp->link_type.header.type = htons (0); lp->link_id.header.type = htons (0); - if (lp->flags & LPFLG_LSA_ENGAGED) + if CHECK_FLAG (lp->flags, LPFLG_LSA_ENGAGED) ospf_mpls_te_lsa_schedule (lp, FLUSH_THIS_LSA); break; } out: return; + } static void ospf_mpls_te_nsm_change (struct ospf_neighbor *nbr, int old_state) { - /* So far, nothing to do here. */ + /* Nothing to do here */ return; } @@ -710,22 +1142,10 @@ build_router_tlv (struct stream *s) } static void -build_link_subtlv_link_type (struct stream *s, struct mpls_te_link *lp) -{ - struct te_tlv_header *tlvh = &lp->link_type.header; - if (ntohs (tlvh->type) != 0) +build_link_subtlv (struct stream *s, struct te_tlv_header *tlvh) { - build_tlv_header (s, tlvh); - stream_put (s, tlvh+1, TLV_BODY_SIZE (tlvh)); - } - return; -} -static void -build_link_subtlv_link_id (struct stream *s, struct mpls_te_link *lp) -{ - struct te_tlv_header *tlvh = &lp->link_id.header; - if (ntohs (tlvh->type) != 0) + if ((tlvh != NULL) && (ntohs (tlvh->type) != 0)) { build_tlv_header (s, tlvh); stream_put (s, tlvh+1, TLV_BODY_SIZE (tlvh)); @@ -734,104 +1154,32 @@ build_link_subtlv_link_id (struct stream *s, struct mpls_te_link *lp) } static void -build_link_subtlv_lclif_ipaddr (struct stream *s, struct mpls_te_link *lp) -{ - struct te_tlv_header *tlvh = (struct te_tlv_header *) lp->lclif_ipaddr; - if (tlvh != NULL && ntohs (tlvh->type) != 0) - { - build_tlv_header (s, tlvh); - stream_put (s, tlvh+1, TLV_BODY_SIZE (tlvh)); - } - return; -} - -static void -build_link_subtlv_rmtif_ipaddr (struct stream *s, struct mpls_te_link *lp) -{ - struct te_tlv_header *tlvh = (struct te_tlv_header *) lp->rmtif_ipaddr; - if (tlvh != NULL && ntohs (tlvh->type) != 0) - { - build_tlv_header (s, tlvh); - stream_put (s, tlvh+1, TLV_BODY_SIZE (tlvh)); - } - return; -} - -static void -build_link_subtlv_te_metric (struct stream *s, struct mpls_te_link *lp) -{ - struct te_tlv_header *tlvh = &lp->te_metric.header; - if (ntohs (tlvh->type) != 0) - { - build_tlv_header (s, tlvh); - stream_put (s, tlvh+1, TLV_BODY_SIZE (tlvh)); - } - return; -} - -static void -build_link_subtlv_max_bw (struct stream *s, struct mpls_te_link *lp) -{ - struct te_tlv_header *tlvh = &lp->max_bw.header; - if (ntohs (tlvh->type) != 0) - { - build_tlv_header (s, tlvh); - stream_put (s, tlvh+1, TLV_BODY_SIZE (tlvh)); - } - return; -} - -static void -build_link_subtlv_max_rsv_bw (struct stream *s, struct mpls_te_link *lp) -{ - struct te_tlv_header *tlvh = &lp->max_rsv_bw.header; - if (ntohs (tlvh->type) != 0) - { - build_tlv_header (s, tlvh); - stream_put (s, tlvh+1, TLV_BODY_SIZE (tlvh)); - } - return; -} - -static void -build_link_subtlv_unrsv_bw (struct stream *s, struct mpls_te_link *lp) -{ - struct te_tlv_header *tlvh = &lp->unrsv_bw.header; - if (ntohs (tlvh->type) != 0) - { - build_tlv_header (s, tlvh); - stream_put (s, tlvh+1, TLV_BODY_SIZE (tlvh)); - } - return; -} - -static void -build_link_subtlv_rsc_clsclr (struct stream *s, struct mpls_te_link *lp) -{ - struct te_tlv_header *tlvh = &lp->rsc_clsclr.header; - if (ntohs (tlvh->type) != 0) - { - build_tlv_header (s, tlvh); - stream_put (s, tlvh+1, TLV_BODY_SIZE (tlvh)); - } - return; -} - -static void -build_link_tlv (struct stream *s, struct mpls_te_link *lp) +build_link_tlv (struct stream *s, struct mpls_te_link *lp) { set_linkparams_link_header (lp); build_tlv_header (s, &lp->link_header.header); - build_link_subtlv_link_type (s, lp); - build_link_subtlv_link_id (s, lp); - build_link_subtlv_lclif_ipaddr (s, lp); - build_link_subtlv_rmtif_ipaddr (s, lp); - build_link_subtlv_te_metric (s, lp); - build_link_subtlv_max_bw (s, lp); - build_link_subtlv_max_rsv_bw (s, lp); - build_link_subtlv_unrsv_bw (s, lp); - build_link_subtlv_rsc_clsclr (s, lp); + build_link_subtlv (s, &lp->link_type.header); + build_link_subtlv (s, &lp->link_id.header); + build_link_subtlv (s, &lp->lclif_ipaddr.header); + build_link_subtlv (s, &lp->rmtif_ipaddr.header); + build_link_subtlv (s, &lp->te_metric.header); + build_link_subtlv (s, &lp->max_bw.header); + build_link_subtlv (s, &lp->max_rsv_bw.header); + build_link_subtlv (s, &lp->unrsv_bw.header); + build_link_subtlv (s, &lp->rsc_clsclr.header); + build_link_subtlv (s, &lp->lrrid.header); + build_link_subtlv (s, &lp->llri.header); + build_link_subtlv (s, &lp->rip.header); + build_link_subtlv (s, &lp->ras.header); + build_link_subtlv (s, &lp->av_delay.header); + build_link_subtlv (s, &lp->mm_delay.header); + build_link_subtlv (s, &lp->delay_var.header); + build_link_subtlv (s, &lp->pkt_loss.header); + build_link_subtlv (s, &lp->res_bw.header); + build_link_subtlv (s, &lp->ava_bw.header); + build_link_subtlv (s, &lp->res_bw.header); + return; } @@ -860,7 +1208,7 @@ ospf_mpls_te_lsa_new (struct ospf_area *area, struct mpls_te_link *lp) struct stream *s; struct lsa_header *lsah; struct ospf_lsa *new = NULL; - u_char options, lsa_type; + u_char options, lsa_type = 0; struct in_addr lsa_id; u_int32_t tmp; u_int16_t length; @@ -873,19 +1221,42 @@ ospf_mpls_te_lsa_new (struct ospf_area *area, struct mpls_te_link *lp) } lsah = (struct lsa_header *) STREAM_DATA (s); - options = LSA_OPTIONS_GET (area); - options |= LSA_OPTIONS_NSSA_GET (area); - options |= OSPF_OPTION_O; /* Don't forget this :-) */ + options = OSPF_OPTION_O; /* Don't forget this :-) */ - lsa_type = OSPF_OPAQUE_AREA_LSA; - tmp = SET_OPAQUE_LSID (OPAQUE_TYPE_TRAFFIC_ENGINEERING_LSA, lp->instance); - lsa_id.s_addr = htonl (tmp); + /* Set opaque-LSA header fields depending of the type of RFC */ + if (IS_INTER_AS (lp->type)) + { + if IS_FLOOD_AS (lp->type) + { + options |= OSPF_OPTION_E; /* Enable AS external as we flood Inter-AS with Opaque Type 11 */ + lsa_type = OSPF_OPAQUE_AS_LSA; + } + else + { + options |= LSA_OPTIONS_GET (area); /* Get area default option */ + options |= LSA_OPTIONS_NSSA_GET (area); + lsa_type = OSPF_OPAQUE_AREA_LSA; + } + tmp = SET_OPAQUE_LSID (OPAQUE_TYPE_INTER_AS_LSA, lp->instance); + lsa_id.s_addr = htonl (tmp); - if (IS_DEBUG_OSPF (lsa, LSA_GENERATE)) - zlog_debug ("LSA[Type%d:%s]: Create an Opaque-LSA/MPLS-TE instance", lsa_type, inet_ntoa (lsa_id)); + struct ospf *top = ospf_lookup (); - /* Set opaque-LSA header fields. */ - lsa_header_set (s, options, lsa_type, lsa_id, area->ospf->router_id); + lsa_header_set (s, options, lsa_type, lsa_id, top->router_id); + } + else + { + options |= LSA_OPTIONS_GET (area); /* Get area default option */ + options |= LSA_OPTIONS_NSSA_GET (area); + lsa_type = OSPF_OPAQUE_AREA_LSA; + tmp = SET_OPAQUE_LSID (OPAQUE_TYPE_TRAFFIC_ENGINEERING_LSA, lp->instance); + lsa_id.s_addr = htonl (tmp); + lsa_header_set (s, options, lsa_type, lsa_id, area->ospf->router_id); + } + + if (IS_DEBUG_OSPF (lsa, LSA_GENERATE)) + zlog_debug ("LSA[Type%d:%s]: Create an Opaque-LSA/MPLS-TE instance", + lsa_type, inet_ntoa (lsa_id)); /* Set opaque-LSA body fields. */ ospf_mpls_te_lsa_body_set (s, lp); @@ -940,9 +1311,8 @@ ospf_mpls_te_lsa_originate1 (struct ospf_area *area, struct mpls_te_link *lp) goto out; } - /* Now this linkparameter entry has associated LSA. */ - lp->flags |= LPFLG_LSA_ENGAGED; - + /* Now this link-parameter entry has associated LSA. */ + SET_FLAG (lp->flags, LPFLG_LSA_ENGAGED); /* Update new LSA origination count. */ area->ospf->lsa_originate_count++; @@ -953,7 +1323,8 @@ ospf_mpls_te_lsa_originate1 (struct ospf_area *area, struct mpls_te_link *lp) { char area_id[INET_ADDRSTRLEN]; strcpy (area_id, inet_ntoa (area->area_id)); - zlog_debug ("LSA[Type%d:%s]: Originate Opaque-LSA/MPLS-TE: Area(%s), Link(%s)", new->data->type, inet_ntoa (new->data->id), area_id, lp->ifp->name); + zlog_debug ("LSA[Type%d:%s]: Originate Opaque-LSA/MPLS-TE: Area(%s), Link(%s)", + new->data->type, inet_ntoa (new->data->id), area_id, lp->ifp->name); ospf_lsa_header_dump (new->data); } @@ -963,7 +1334,7 @@ ospf_mpls_te_lsa_originate1 (struct ospf_area *area, struct mpls_te_link *lp) } static int -ospf_mpls_te_lsa_originate (void *arg) +ospf_mpls_te_lsa_originate_area (void *arg) { struct ospf_area *area = (struct ospf_area *) arg; struct listnode *node, *nnode; @@ -972,34 +1343,44 @@ ospf_mpls_te_lsa_originate (void *arg) if (OspfMplsTE.status == disabled) { - zlog_info ("ospf_mpls_te_lsa_originate: MPLS-TE is disabled now."); + zlog_info ("ospf_mpls_te_lsa_originate_area: MPLS-TE is disabled now."); rc = 0; /* This is not an error case. */ goto out; } for (ALL_LIST_ELEMENTS (OspfMplsTE.iflist, node, nnode, lp)) { + /* Process only enabled LSA with area scope flooding */ + if (!CHECK_FLAG (lp->flags, LPFLG_LSA_ACTIVE) || IS_FLOOD_AS (lp->type)) + continue; + if (lp->area == NULL) continue; + if (! IPV4_ADDR_SAME (&lp->area->area_id, &area->area_id)) continue; - if (lp->flags & LPFLG_LSA_ENGAGED) + if CHECK_FLAG (lp->flags, LPFLG_LSA_ENGAGED) { - if (lp->flags & LPFLG_LSA_FORCED_REFRESH) + if CHECK_FLAG (lp->flags, LPFLG_LSA_FORCED_REFRESH) { - lp->flags &= ~LPFLG_LSA_FORCED_REFRESH; + UNSET_FLAG (lp->flags, LPFLG_LSA_FORCED_REFRESH); + zlog_warn ("OSPF MPLS-TE (ospf_mpls_te_lsa_originate_area): Refresh instead of Originate"); ospf_mpls_te_lsa_schedule (lp, REFRESH_THIS_LSA); } continue; } if (! is_mandated_params_set (lp)) { - zlog_warn ("ospf_mpls_te_lsa_originate: Link(%s) lacks some mandated MPLS-TE parameters.", lp->ifp ? lp->ifp->name : "?"); + zlog_warn ("ospf_mpls_te_lsa_originate_area: Link(%s) lacks some mandated MPLS-TE parameters.", + lp->ifp ? lp->ifp->name : "?"); continue; } /* Ok, let's try to originate an LSA for this area and Link. */ + if (IS_DEBUG_OSPF_TE) + zlog_debug ("MPLS-TE(ospf_mpls_te_lsa_originate_area) Let's finally reoriginate the LSA %d through the Area %s for Link %s", + lp->instance, inet_ntoa (area->area_id), lp->ifp ? lp->ifp->name : "?"); if (ospf_mpls_te_lsa_originate1 (area, lp) != 0) goto out; } @@ -1009,11 +1390,112 @@ ospf_mpls_te_lsa_originate (void *arg) return rc; } +static int +ospf_mpls_te_lsa_originate2 (struct ospf *top, struct mpls_te_link *lp) +{ + struct ospf_lsa *new; + int rc = -1; + + /* Create new Opaque-LSA/Inter-AS instance. */ + if ((new = ospf_mpls_te_lsa_new (NULL, lp)) == NULL) + { + zlog_warn ("ospf_mpls_te_lsa_originate2: ospf_router_info_lsa_new() ?"); + goto out; + } + + /* Install this LSA into LSDB. */ + if (ospf_lsa_install (top, NULL /*oi */ , new) == NULL) + { + zlog_warn ("ospf_mpls_te_lsa_originate2: ospf_lsa_install() ?"); + ospf_lsa_unlock (&new); + goto out; + } + + /* Now this Router Info parameter entry has associated LSA. */ + SET_FLAG (lp->flags, LPFLG_LSA_ENGAGED); + /* Update new LSA origination count. */ + top->lsa_originate_count++; + + /* Flood new LSA through AS. */ + ospf_flood_through_as (top, NULL /*nbr */ , new); + + if (IS_DEBUG_OSPF (lsa, LSA_GENERATE)) + { + zlog_debug ("LSA[Type%d:%s]: Originate Opaque-LSA/MPLS-TE Inter-AS", + new->data->type, inet_ntoa (new->data->id)); + ospf_lsa_header_dump (new->data); + } + + rc = 0; +out:return rc; +} + +static int +ospf_mpls_te_lsa_originate_as (void *arg) +{ + struct ospf *top; + struct ospf_area *area; + struct listnode *node, *nnode; + struct mpls_te_link *lp; + int rc = -1; + + if ((OspfMplsTE.status == disabled) || (OspfMplsTE.inter_as == Disable)) + { + zlog_info + ("ospf_mpls_te_lsa_originate_as: MPLS-TE Inter-AS is disabled for now."); + rc = 0; /* This is not an error case. */ + goto out; + } + + for (ALL_LIST_ELEMENTS (OspfMplsTE.iflist, node, nnode, lp)) + { + /* Process only enabled INTER_AS Links or Pseudo-Links */ + if (!CHECK_FLAG (lp->flags, LPFLG_LSA_ACTIVE) || !IS_INTER_AS (lp->type)) + continue; + + if CHECK_FLAG (lp->flags, LPFLG_LSA_ENGAGED) + { + if CHECK_FLAG (lp->flags, LPFLG_LSA_FORCED_REFRESH) + { + UNSET_FLAG (lp->flags, LPFLG_LSA_FORCED_REFRESH); + ospf_mpls_te_lsa_schedule (lp, REFRESH_THIS_LSA); + } + continue; + } + if (!is_mandated_params_set (lp)) + { + zlog_warn ("ospf_mpls_te_lsa_originate_as: Link(%s) lacks some mandated MPLS-TE parameters.", + lp->ifp ? lp->ifp->name : "?"); + continue; + } + + /* Ok, let's try to originate an LSA for this AS and Link. */ + if (IS_DEBUG_OSPF_TE) + zlog_debug ("MPLS-TE(ospf_mpls_te_lsa_originate_as) Let's finally re-originate the Inter-AS LSA %d through the %s for Link %s", + lp->instance, IS_FLOOD_AS (lp->type) ? "AS" : "Area", lp->ifp ? lp->ifp->name : "Unknown"); + + if (IS_FLOOD_AS (lp->type)) + { + top = (struct ospf *) arg; + rc = ospf_mpls_te_lsa_originate2 (top, lp); + } + else + { + area = (struct ospf_area *) arg; + rc = ospf_mpls_te_lsa_originate1 (area, lp); + } + } + + rc = 0; +out:return rc; +} + static struct ospf_lsa * ospf_mpls_te_lsa_refresh (struct ospf_lsa *lsa) { struct mpls_te_link *lp; struct ospf_area *area = lsa->area; + struct ospf *top; struct ospf_lsa *new = NULL; if (OspfMplsTE.status == disabled) @@ -1033,10 +1515,18 @@ ospf_mpls_te_lsa_refresh (struct ospf_lsa *lsa) lsa->data->ls_age = htons (OSPF_LSA_MAXAGE); /* Flush it anyway. */ } + /* Check if lp was not disable in the interval */ + if (!CHECK_FLAG (lp->flags, LPFLG_LSA_ACTIVE)) + { + zlog_warn ("ospf_mpls_te_lsa_refresh: lp was disabled: Flush it!"); + lsa->data->ls_age = htons (OSPF_LSA_MAXAGE); /* Flush it anyway. */ + } + /* If the lsa's age reached to MaxAge, start flushing procedure. */ if (IS_LSA_MAXAGE (lsa)) { - lp->flags &= ~LPFLG_LSA_ENGAGED; + if (lp) + UNSET_FLAG (lp->flags, LPFLG_LSA_ENGAGED); ospf_opaque_lsa_flush_schedule (lsa); goto out; } @@ -1051,15 +1541,24 @@ ospf_mpls_te_lsa_refresh (struct ospf_lsa *lsa) /* Install this LSA into LSDB. */ /* Given "lsa" will be freed in the next function. */ - if (ospf_lsa_install (area->ospf, NULL/*oi*/, new) == NULL) + /* As area could be NULL i.e. when using OPAQUE_LSA_AS, we prefer to use ospf_lookup() to get ospf instance */ + if (area) + top = area->ospf; + else + top = ospf_lookup (); + + if (ospf_lsa_install (top, NULL /*oi */ , new) == NULL) { zlog_warn ("ospf_mpls_te_lsa_refresh: ospf_lsa_install() ?"); ospf_lsa_unlock (&new); goto out; } - /* Flood updated LSA through area. */ - ospf_flood_through_area (area, NULL/*nbr*/, new); + /* Flood updated LSA through AS or Area depending of the RFC of the link */ + if (IS_FLOOD_AS (lp->type)) + ospf_flood_through_as (top, NULL, new); + else + ospf_flood_through_area (area, NULL/*nbr*/, new); /* Debug logging. */ if (IS_DEBUG_OSPF (lsa, LSA_GENERATE)) @@ -1073,34 +1572,80 @@ ospf_mpls_te_lsa_refresh (struct ospf_lsa *lsa) return new; } -static void -ospf_mpls_te_lsa_schedule (struct mpls_te_link *lp, - enum sched_opcode opcode) +void +ospf_mpls_te_lsa_schedule (struct mpls_te_link *lp, opcode_t opcode) { struct ospf_lsa lsa; struct lsa_header lsah; + struct ospf *top; u_int32_t tmp; memset (&lsa, 0, sizeof (lsa)); memset (&lsah, 0, sizeof (lsah)); + top = ospf_lookup (); + + /* Check if the pseudo link is ready to flood */ + if (!(CHECK_FLAG (lp->flags, LPFLG_LSA_ACTIVE)) + || !(IS_FLOOD_AREA (lp->type) || IS_FLOOD_AS (lp->type))) { + return; + } lsa.area = lp->area; lsa.data = &lsah; - lsah.type = OSPF_OPAQUE_AREA_LSA; - tmp = SET_OPAQUE_LSID (OPAQUE_TYPE_TRAFFIC_ENGINEERING_LSA, lp->instance); - lsah.id.s_addr = htonl (tmp); + if (IS_FLOOD_AS (lp->type)) + { + lsah.type = OSPF_OPAQUE_AS_LSA; + tmp = SET_OPAQUE_LSID (OPAQUE_TYPE_INTER_AS_LSA, lp->instance); + lsah.id.s_addr = htonl (tmp); + } + else + { + lsah.type = OSPF_OPAQUE_AREA_LSA; + if (IS_INTER_AS (lp->type)) + { + /* Set the area context if not know */ + if (lp->area == NULL) + lp->area = ospf_area_lookup_by_area_id (top, OspfMplsTE.interas_areaid); + /* Unable to set the area context. Abort! */ + if (lp->area == NULL) + { + zlog_warn ("MPLS-TE(ospf_mpls_te_lsa_schedule) Area context is null. Abort !"); + return; + } + tmp = SET_OPAQUE_LSID (OPAQUE_TYPE_INTER_AS_LSA, lp->instance); + } + else + tmp = SET_OPAQUE_LSID (OPAQUE_TYPE_TRAFFIC_ENGINEERING_LSA, lp->instance); + lsah.id.s_addr = htonl (tmp); + } switch (opcode) { - case REORIGINATE_PER_AREA: - ospf_opaque_lsa_reoriginate_schedule ((void *) lp->area, - OSPF_OPAQUE_AREA_LSA, OPAQUE_TYPE_TRAFFIC_ENGINEERING_LSA); + case REORIGINATE_THIS_LSA: + if (IS_FLOOD_AS (lp->type)) + { + ospf_opaque_lsa_reoriginate_schedule ((void *) top, OSPF_OPAQUE_AS_LSA, + OPAQUE_TYPE_INTER_AS_LSA); + break; + } + + if (IS_FLOOD_AREA (lp->type)) + { + if (IS_INTER_AS (lp->type)) + ospf_opaque_lsa_reoriginate_schedule ((void *) lp->area, OSPF_OPAQUE_AREA_LSA, + OPAQUE_TYPE_INTER_AS_LSA); + else + ospf_opaque_lsa_reoriginate_schedule ((void *) lp->area, OSPF_OPAQUE_AREA_LSA, + OPAQUE_TYPE_TRAFFIC_ENGINEERING_LSA); + break; + } break; case REFRESH_THIS_LSA: ospf_opaque_lsa_refresh_schedule (&lsa); break; case FLUSH_THIS_LSA: - lp->flags &= ~LPFLG_LSA_ENGAGED; + /* Reset Activity flag */ + lp->flags = LPFLG_LSA_INACTIVE; ospf_opaque_lsa_flush_schedule (&lsa); break; default: @@ -1111,6 +1656,7 @@ ospf_mpls_te_lsa_schedule (struct mpls_te_link *lp, return; } + /*------------------------------------------------------------------------* * Followings are vty session control functions. *------------------------------------------------------------------------*/ @@ -1121,7 +1667,8 @@ show_vty_router_addr (struct vty *vty, struct te_tlv_header *tlvh) struct te_tlv_router_addr *top = (struct te_tlv_router_addr *) tlvh; if (vty != NULL) - vty_out (vty, " Router-Address: %s%s", inet_ntoa (top->value), VTY_NEWLINE); + vty_out (vty, " Router-Address: %s%s", inet_ntoa (top->value), + VTY_NEWLINE); else zlog_debug (" Router-Address: %s", inet_ntoa (top->value)); @@ -1134,7 +1681,8 @@ show_vty_link_header (struct vty *vty, struct te_tlv_header *tlvh) struct te_tlv_link *top = (struct te_tlv_link *) tlvh; if (vty != NULL) - vty_out (vty, " Link: %u octets of data%s", ntohs (top->header.length), VTY_NEWLINE); + vty_out (vty, " Link: %u octets of data%s", ntohs (top->header.length), + VTY_NEWLINE); else zlog_debug (" Link: %u octets of data", ntohs (top->header.length)); @@ -1161,7 +1709,8 @@ show_vty_link_subtlv_link_type (struct vty *vty, struct te_tlv_header *tlvh) } if (vty != NULL) - vty_out (vty, " Link-Type: %s (%u)%s", cp, top->link_type.value, VTY_NEWLINE); + vty_out (vty, " Link-Type: %s (%u)%s", cp, top->link_type.value, + VTY_NEWLINE); else zlog_debug (" Link-Type: %s (%u)", cp, top->link_type.value); @@ -1183,7 +1732,8 @@ show_vty_link_subtlv_link_id (struct vty *vty, struct te_tlv_header *tlvh) } static u_int16_t -show_vty_link_subtlv_lclif_ipaddr (struct vty *vty, struct te_tlv_header *tlvh) +show_vty_link_subtlv_lclif_ipaddr (struct vty *vty, + struct te_tlv_header *tlvh) { struct te_link_subtlv_lclif_ipaddr *top; int i, n; @@ -1199,7 +1749,8 @@ show_vty_link_subtlv_lclif_ipaddr (struct vty *vty, struct te_tlv_header *tlvh) for (i = 0; i < n; i++) { if (vty != NULL) - vty_out (vty, " #%d: %s%s", i, inet_ntoa (top->value[i]), VTY_NEWLINE); + vty_out (vty, " #%d: %s%s", i, inet_ntoa (top->value[i]), + VTY_NEWLINE); else zlog_debug (" #%d: %s", i, inet_ntoa (top->value[i])); } @@ -1207,7 +1758,8 @@ show_vty_link_subtlv_lclif_ipaddr (struct vty *vty, struct te_tlv_header *tlvh) } static u_int16_t -show_vty_link_subtlv_rmtif_ipaddr (struct vty *vty, struct te_tlv_header *tlvh) +show_vty_link_subtlv_rmtif_ipaddr (struct vty *vty, + struct te_tlv_header *tlvh) { struct te_link_subtlv_rmtif_ipaddr *top; int i, n; @@ -1222,7 +1774,8 @@ show_vty_link_subtlv_rmtif_ipaddr (struct vty *vty, struct te_tlv_header *tlvh) for (i = 0; i < n; i++) { if (vty != NULL) - vty_out (vty, " #%d: %s%s", i, inet_ntoa (top->value[i]), VTY_NEWLINE); + vty_out (vty, " #%d: %s%s", i, inet_ntoa (top->value[i]), + VTY_NEWLINE); else zlog_debug (" #%d: %s", i, inet_ntoa (top->value[i])); } @@ -1236,9 +1789,11 @@ show_vty_link_subtlv_te_metric (struct vty *vty, struct te_tlv_header *tlvh) top = (struct te_link_subtlv_te_metric *) tlvh; if (vty != NULL) - vty_out (vty, " Traffic Engineering Metric: %u%s", (u_int32_t) ntohl (top->value), VTY_NEWLINE); + vty_out (vty, " Traffic Engineering Metric: %u%s", + (u_int32_t) ntohl (top->value), VTY_NEWLINE); else - zlog_debug (" Traffic Engineering Metric: %u", (u_int32_t) ntohl (top->value)); + zlog_debug (" Traffic Engineering Metric: %u", + (u_int32_t) ntohl (top->value)); return TLV_SIZE (tlvh); } @@ -1250,7 +1805,7 @@ show_vty_link_subtlv_max_bw (struct vty *vty, struct te_tlv_header *tlvh) float fval; top = (struct te_link_subtlv_max_bw *) tlvh; - ntohf (&top->value, &fval); + fval = ntohf (top->value); if (vty != NULL) vty_out (vty, " Maximum Bandwidth: %g (Bytes/sec)%s", fval, VTY_NEWLINE); @@ -1267,10 +1822,11 @@ show_vty_link_subtlv_max_rsv_bw (struct vty *vty, struct te_tlv_header *tlvh) float fval; top = (struct te_link_subtlv_max_rsv_bw *) tlvh; - ntohf (&top->value, &fval); + fval = ntohf (top->value); if (vty != NULL) - vty_out (vty, " Maximum Reservable Bandwidth: %g (Bytes/sec)%s", fval, VTY_NEWLINE); + vty_out (vty, " Maximum Reservable Bandwidth: %g (Bytes/sec)%s", fval, + VTY_NEWLINE); else zlog_debug (" Maximum Reservable Bandwidth: %g (Bytes/sec)", fval); @@ -1281,17 +1837,25 @@ static u_int16_t show_vty_link_subtlv_unrsv_bw (struct vty *vty, struct te_tlv_header *tlvh) { struct te_link_subtlv_unrsv_bw *top; - float fval; + float fval1, fval2; int i; top = (struct te_link_subtlv_unrsv_bw *) tlvh; - for (i = 0; i < 8; i++) + if (vty != NULL) + vty_out (vty, " Unreserved Bandwidth per Class Type in Byte/s:%s", VTY_NEWLINE); + else + zlog_debug (" Unreserved Bandwidth per Class Type in Byte/s:"); + for (i = 0; i < MAX_CLASS_TYPE; i+=2) { - ntohf (&top->value[i], &fval); + fval1 = ntohf (top->value[i]); + fval2 = ntohf (top->value[i+1]); + if (vty != NULL) - vty_out (vty, " Unreserved Bandwidth (pri %d): %g (Bytes/sec)%s", i, fval, VTY_NEWLINE); + vty_out(vty, " [%d]: %g (Bytes/sec),\t[%d]: %g (Bytes/sec)%s", + i, fval1, i+1, fval2, VTY_NEWLINE); else - zlog_debug (" Unreserved Bandwidth (pri %d): %g (Bytes/sec)", i, fval); + zlog_debug (" [%d]: %g (Bytes/sec),\t[%d]: %g (Bytes/sec)", + i, fval1, i+1, fval2); } return TLV_SIZE (tlvh); @@ -1304,9 +1868,230 @@ show_vty_link_subtlv_rsc_clsclr (struct vty *vty, struct te_tlv_header *tlvh) top = (struct te_link_subtlv_rsc_clsclr *) tlvh; if (vty != NULL) - vty_out (vty, " Resource class/color: 0x%x%s", (u_int32_t) ntohl (top->value), VTY_NEWLINE); + vty_out (vty, " Resource class/color: 0x%x%s", + (u_int32_t) ntohl (top->value), VTY_NEWLINE); + else + zlog_debug (" Resource Class/Color: 0x%x", + (u_int32_t) ntohl (top->value)); + + return TLV_SIZE (tlvh); +} + +static u_int16_t +show_vty_link_subtlv_lrrid (struct vty *vty, struct te_tlv_header *tlvh) +{ + struct te_link_subtlv_lrrid *top; + + top = (struct te_link_subtlv_lrrid *) tlvh; + + if (vty != NULL) + { + vty_out (vty, " Local TE Router ID: %s%s", inet_ntoa (top->local), + VTY_NEWLINE); + vty_out (vty, " Remote TE Router ID: %s%s", inet_ntoa (top->remote), + VTY_NEWLINE); + } + else + { + zlog_debug (" Local TE Router ID: %s", inet_ntoa (top->local)); + zlog_debug (" Remote TE Router ID: %s", inet_ntoa (top->remote)); + } + + return TLV_SIZE (tlvh); +} + +static u_int16_t +show_vty_link_subtlv_llri (struct vty *vty, struct te_tlv_header *tlvh) +{ + struct te_link_subtlv_llri *top; + + top = (struct te_link_subtlv_llri *) tlvh; + + if (vty != NULL) + { + vty_out (vty, " Link Local ID: %d%s", (u_int32_t) ntohl (top->local), + VTY_NEWLINE); + vty_out (vty, " Link Remote ID: %d%s", (u_int32_t) ntohl (top->remote), + VTY_NEWLINE); + } + else + { + zlog_debug (" Link Local ID: %d", (u_int32_t) ntohl (top->local)); + zlog_debug (" Link Remote ID: %d", (u_int32_t) ntohl (top->remote)); + } + + return TLV_SIZE (tlvh); +} + +static u_int16_t +show_vty_link_subtlv_rip (struct vty *vty, struct te_tlv_header *tlvh) +{ + struct te_link_subtlv_rip *top; + + top = (struct te_link_subtlv_rip *) tlvh; + + if (vty != NULL) + vty_out (vty, " Inter-AS TE Remote ASBR IP address: %s%s", + inet_ntoa (top->value), VTY_NEWLINE); else - zlog_debug (" Resource Class/Color: 0x%x", (u_int32_t) ntohl (top->value)); + zlog_debug (" Inter-AS TE Remote ASBR IP address: %s", + inet_ntoa (top->value)); + + return TLV_SIZE (tlvh); +} + +static u_int16_t +show_vty_link_subtlv_ras (struct vty *vty, struct te_tlv_header *tlvh) +{ + struct te_link_subtlv_ras *top; + + top = (struct te_link_subtlv_ras *) tlvh; + + if (vty != NULL) + vty_out (vty, " Inter-AS TE Remote AS number: %u%s", ntohl (top->value), + VTY_NEWLINE); + else + zlog_debug (" Inter-AS TE Remote AS number: %u", ntohl (top->value)); + + return TLV_SIZE (tlvh); +} + +static u_int16_t +show_vty_link_subtlv_av_delay (struct vty *vty, struct te_tlv_header *tlvh) +{ + struct te_link_subtlv_av_delay *top; + u_int32_t delay; + u_int32_t anomalous; + + top = (struct te_link_subtlv_av_delay *) tlvh; + delay = (u_int32_t) ntohl (top->value) & TE_EXT_MASK; + anomalous = (u_int32_t) ntohl (top->value) & TE_EXT_ANORMAL; + + if (vty != NULL) + vty_out (vty, " %s Average Link Delay: %d (micro-sec)%s", + anomalous ? "Anomalous" : "Normal", delay, VTY_NEWLINE); + else + zlog_debug (" %s Average Link Delay: %d (micro-sec)", + anomalous ? "Anomalous" : "Normal", delay); + + return TLV_SIZE (tlvh); +} + +static u_int16_t +show_vty_link_subtlv_mm_delay (struct vty *vty, struct te_tlv_header *tlvh) +{ + struct te_link_subtlv_mm_delay *top; + u_int32_t low, high; + u_int32_t anomalous; + + top = (struct te_link_subtlv_mm_delay *) tlvh; + low = (u_int32_t) ntohl (top->low) & TE_EXT_MASK; + anomalous = (u_int32_t) ntohl (top->low) & TE_EXT_ANORMAL; + high = (u_int32_t) ntohl (top->high); + + if (vty != NULL) + vty_out (vty, " %s Min/Max Link Delay: %d/%d (micro-sec)%s", + anomalous ? "Anomalous" : "Normal", low, high, VTY_NEWLINE); + else + zlog_debug (" %s Min/Max Link Delay: %d/%d (micro-sec)", + anomalous ? "Anomalous" : "Normal", low, high); + + return TLV_SIZE (tlvh); +} + +static u_int16_t +show_vty_link_subtlv_delay_var (struct vty *vty, struct te_tlv_header *tlvh) +{ + struct te_link_subtlv_delay_var *top; + u_int32_t jitter; + + top = (struct te_link_subtlv_delay_var *) tlvh; + jitter = (u_int32_t) ntohl (top->value) & TE_EXT_MASK; + + if (vty != NULL) + vty_out (vty, " Delay Variation: %d (micro-sec)%s", jitter, VTY_NEWLINE); + else + zlog_debug (" Delay Variation: %d (micro-sec)", jitter); + + return TLV_SIZE (tlvh); +} + +static u_int16_t +show_vty_link_subtlv_pkt_loss (struct vty *vty, struct te_tlv_header *tlvh) +{ + struct te_link_subtlv_pkt_loss *top; + u_int32_t loss; + u_int32_t anomalous; + float fval; + + top = (struct te_link_subtlv_pkt_loss *) tlvh; + loss = (u_int32_t) ntohl (top->value) & TE_EXT_MASK; + fval = (float) (loss * LOSS_PRECISION); + anomalous = (u_int32_t) ntohl (top->value) & TE_EXT_ANORMAL; + + if (vty != NULL) + vty_out (vty, " %s Link Loss: %g (%%)%s", anomalous ? "Anomalous" : "Normal", + fval, VTY_NEWLINE); + else + zlog_debug (" %s Link Loss: %g (%%)", anomalous ? "Anomalous" : "Normal", + fval); + + return TLV_SIZE (tlvh); +} + +static u_int16_t +show_vty_link_subtlv_res_bw (struct vty *vty, struct te_tlv_header *tlvh) +{ + struct te_link_subtlv_res_bw *top; + float fval; + + top = (struct te_link_subtlv_res_bw *) tlvh; + fval = ntohf (top->value); + + if (vty != NULL) + vty_out (vty, " Unidirectional Residual Bandwidth: %g (Bytes/sec)%s", + fval, VTY_NEWLINE); + else + zlog_debug (" Unidirectional Residual Bandwidth: %g (Bytes/sec)", + fval); + + return TLV_SIZE (tlvh); +} + +static u_int16_t +show_vty_link_subtlv_ava_bw (struct vty *vty, struct te_tlv_header *tlvh) +{ + struct te_link_subtlv_ava_bw *top; + float fval; + + top = (struct te_link_subtlv_ava_bw *) tlvh; + fval = ntohf (top->value); + + if (vty != NULL) + vty_out (vty, " Unidirectional Available Bandwidth: %g (Bytes/sec)%s", + fval, VTY_NEWLINE); + else + zlog_debug (" Unidirectional Available Bandwidth: %g (Bytes/sec)", + fval); + + return TLV_SIZE (tlvh); +} + +static u_int16_t +show_vty_link_subtlv_use_bw (struct vty *vty, struct te_tlv_header *tlvh) +{ + struct te_link_subtlv_use_bw *top; + float fval; + + top = (struct te_link_subtlv_use_bw *) tlvh; + fval = ntohf (top->value); + + if (vty != NULL) + vty_out (vty, " Unidirectional Utilized Bandwidth: %g (Bytes/sec)%s", + fval, VTY_NEWLINE); + else + zlog_debug (" Unidirectional Utilized Bandwidth: %g (Bytes/sec)", + fval); return TLV_SIZE (tlvh); } @@ -1315,9 +2100,11 @@ static u_int16_t show_vty_unknown_tlv (struct vty *vty, struct te_tlv_header *tlvh) { if (vty != NULL) - vty_out (vty, " Unknown TLV: [type(0x%x), length(0x%x)]%s", ntohs (tlvh->type), ntohs (tlvh->length), VTY_NEWLINE); + vty_out (vty, " Unknown TLV: [type(0x%x), length(0x%x)]%s", + ntohs (tlvh->type), ntohs (tlvh->length), VTY_NEWLINE); else - zlog_debug (" Unknown TLV: [type(0x%x), length(0x%x)]", ntohs (tlvh->type), ntohs (tlvh->length)); + zlog_debug (" Unknown TLV: [type(0x%x), length(0x%x)]", + ntohs (tlvh->type), ntohs (tlvh->length)); return TLV_SIZE (tlvh); } @@ -1361,6 +2148,39 @@ ospf_mpls_te_show_link_subtlv (struct vty *vty, struct te_tlv_header *tlvh0, case TE_LINK_SUBTLV_RSC_CLSCLR: sum += show_vty_link_subtlv_rsc_clsclr (vty, tlvh); break; + case TE_LINK_SUBTLV_LRRID: + sum += show_vty_link_subtlv_lrrid (vty, tlvh); + break; + case TE_LINK_SUBTLV_LLRI: + sum += show_vty_link_subtlv_llri (vty, tlvh); + break; + case TE_LINK_SUBTLV_RIP: + sum += show_vty_link_subtlv_rip (vty, tlvh); + break; + case TE_LINK_SUBTLV_RAS: + sum += show_vty_link_subtlv_ras (vty, tlvh); + break; + case TE_LINK_SUBTLV_AV_DELAY: + sum += show_vty_link_subtlv_av_delay (vty, tlvh); + break; + case TE_LINK_SUBTLV_MM_DELAY: + sum += show_vty_link_subtlv_mm_delay (vty, tlvh); + break; + case TE_LINK_SUBTLV_DELAY_VAR: + sum += show_vty_link_subtlv_delay_var (vty, tlvh); + break; + case TE_LINK_SUBTLV_PKT_LOSS: + sum += show_vty_link_subtlv_pkt_loss (vty, tlvh); + break; + case TE_LINK_SUBTLV_RES_BW: + sum += show_vty_link_subtlv_res_bw (vty, tlvh); + break; + case TE_LINK_SUBTLV_AVA_BW: + sum += show_vty_link_subtlv_ava_bw (vty, tlvh); + break; + case TE_LINK_SUBTLV_USE_BW: + sum += show_vty_link_subtlv_use_bw (vty, tlvh); + break; default: sum += show_vty_unknown_tlv (vty, tlvh); break; @@ -1414,49 +2234,20 @@ ospf_mpls_te_show_info (struct vty *vty, struct ospf_lsa *lsa) static void ospf_mpls_te_config_write_router (struct vty *vty) { + if (OspfMplsTE.status == enabled) { - vty_out (vty, " mpls-te%s", VTY_NEWLINE); + vty_out (vty, " mpls-te on%s", VTY_NEWLINE); vty_out (vty, " mpls-te router-address %s%s", inet_ntoa (OspfMplsTE.router_addr.value), VTY_NEWLINE); } - return; -} - -static void -ospf_mpls_te_config_write_if (struct vty *vty, struct interface *ifp) -{ - struct mpls_te_link *lp; - - if ((OspfMplsTE.status == enabled) - && (! if_is_loopback (ifp) && if_is_up (ifp) && ospf_oi_count (ifp) > 0) - && ((lp = lookup_linkparams_by_ifp (ifp)) != NULL)) - { - float fval; - int i; - - vty_out (vty, " mpls-te link metric %u%s", - (u_int32_t) ntohl (lp->te_metric.value), VTY_NEWLINE); - - ntohf (&lp->max_bw.value, &fval); - if (fval >= MPLS_TE_MINIMUM_BANDWIDTH) - vty_out (vty, " mpls-te link max-bw %g%s", fval, VTY_NEWLINE); - - ntohf (&lp->max_rsv_bw.value, &fval); - if (fval >= MPLS_TE_MINIMUM_BANDWIDTH) - vty_out (vty, " mpls-te link max-rsv-bw %g%s", fval, VTY_NEWLINE); - for (i = 0; i < 8; i++) - { - ntohf (&lp->unrsv_bw.value[i], &fval); - if (fval >= MPLS_TE_MINIMUM_BANDWIDTH) - vty_out (vty, " mpls-te link unrsv-bw %d %g%s", - i, fval, VTY_NEWLINE); - } + if (OspfMplsTE.inter_as == AS) + vty_out (vty, " mpls-te inter-as as%s", VTY_NEWLINE); + if (OspfMplsTE.inter_as == Area) + vty_out (vty, " mpls-te inter-as area %s %s", + inet_ntoa (OspfMplsTE.interas_areaid), VTY_NEWLINE); - vty_out (vty, " mpls-te link rsc-clsclr 0x%x%s", - (u_int32_t) ntohl (lp->rsc_clsclr.value), VTY_NEWLINE); - } return; } @@ -1464,13 +2255,13 @@ ospf_mpls_te_config_write_if (struct vty *vty, struct interface *ifp) * Followings are vty command functions. *------------------------------------------------------------------------*/ -DEFUN (mpls_te, - mpls_te_cmd, - "mpls-te", - "Configure MPLS-TE parameters\n" +DEFUN (ospf_mpls_te_on, + ospf_mpls_te_on_cmd, + "mpls-te on", + MPLS_TE_STR "Enable the MPLS-TE functionality\n") { - struct listnode *node, *nnode; + struct listnode *node; struct mpls_te_link *lp; if (OspfMplsTE.status == enabled) @@ -1481,31 +2272,28 @@ DEFUN (mpls_te, OspfMplsTE.status = enabled; - /* - * Following code is intended to handle two cases; - * - * 1) MPLS-TE was disabled at startup time, but now become enabled. - * 2) MPLS-TE was once enabled then disabled, and now enabled again. - */ - for (ALL_LIST_ELEMENTS (OspfMplsTE.iflist, node, nnode, lp)) - initialize_linkparams (lp); + /* Reoriginate RFC3630 & RFC6827 Links */ + ospf_mpls_te_foreach_area (ospf_mpls_te_lsa_schedule, REORIGINATE_THIS_LSA); - ospf_mpls_te_foreach_area (ospf_mpls_te_lsa_schedule, REORIGINATE_PER_AREA); + /* Reoriginate LSA if INTER-AS is always on */ + if (OspfMplsTE.inter_as != Disable) + { + for (ALL_LIST_ELEMENTS_RO (OspfMplsTE.iflist, node, lp)) + { + if (IS_INTER_AS (lp->type)) + { + ospf_mpls_te_lsa_schedule (lp, REORIGINATE_THIS_LSA); + } + } + } return CMD_SUCCESS; } -ALIAS (mpls_te, - mpls_te_on_cmd, - "mpls-te on", - "Configure MPLS-TE parameters\n" - "Enable the MPLS-TE functionality\n") - -DEFUN (no_mpls_te, - no_mpls_te_cmd, +DEFUN (no_ospf_mpls_te, + no_ospf_mpls_te_cmd, "no mpls-te", NO_STR - "Configure MPLS-TE parameters\n" "Disable the MPLS-TE functionality\n") { struct listnode *node, *nnode; @@ -1520,17 +2308,16 @@ DEFUN (no_mpls_te, OspfMplsTE.status = disabled; for (ALL_LIST_ELEMENTS (OspfMplsTE.iflist, node, nnode, lp)) - if (lp->area != NULL) - if (lp->flags & LPFLG_LSA_ENGAGED) + if CHECK_FLAG (lp->flags, LPFLG_LSA_ENGAGED) ospf_mpls_te_lsa_schedule (lp, FLUSH_THIS_LSA); return CMD_SUCCESS; } -DEFUN (mpls_te_router_addr, - mpls_te_router_addr_cmd, +DEFUN (ospf_mpls_te_router_addr, + ospf_mpls_te_router_addr_cmd, "mpls-te router-address A.B.C.D", - "MPLS-TE specific commands\n" + MPLS_TE_STR "Stable IP address of the advertising router\n" "MPLS-TE router address in IPv4 address format\n") { @@ -1557,10 +2344,10 @@ DEFUN (mpls_te_router_addr, for (ALL_LIST_ELEMENTS (OspfMplsTE.iflist, node, nnode, lp)) { - if (lp->area == NULL) + if ((lp->area == NULL) || IS_FLOOD_AS (lp->type)) continue; - if ((lp->flags & LPFLG_LSA_ENGAGED) == 0) + if (!CHECK_FLAG (lp->flags, LPFLG_LSA_ENGAGED)) { need_to_reoriginate = 1; break; @@ -1569,247 +2356,151 @@ DEFUN (mpls_te_router_addr, for (ALL_LIST_ELEMENTS (OspfMplsTE.iflist, node, nnode, lp)) { - if (lp->area == NULL) + if ((lp->area == NULL) || IS_FLOOD_AS (lp->type)) continue; if (need_to_reoriginate) - lp->flags |= LPFLG_LSA_FORCED_REFRESH; + SET_FLAG (lp->flags, LPFLG_LSA_FORCED_REFRESH); else ospf_mpls_te_lsa_schedule (lp, REFRESH_THIS_LSA); } if (need_to_reoriginate) - ospf_mpls_te_foreach_area ( - ospf_mpls_te_lsa_schedule, REORIGINATE_PER_AREA); + ospf_mpls_te_foreach_area (ospf_mpls_te_lsa_schedule, REORIGINATE_THIS_LSA); } out: return CMD_SUCCESS; } -DEFUN (mpls_te_link_metric, - mpls_te_link_metric_cmd, - "mpls-te link metric <0-4294967295>", - "MPLS-TE specific commands\n" - "Configure MPLS-TE link parameters\n" - "Link metric for MPLS-TE purpose\n" - "Metric\n") +static int +set_inter_as_mode (struct vty *vty, const char *mode_name, + const char *area_id) { - struct interface *ifp = (struct interface *) vty->index; + enum inter_as_mode mode; + struct listnode *node; struct mpls_te_link *lp; - u_int32_t value; - - if ((lp = lookup_linkparams_by_ifp (ifp)) == NULL) - { - vty_out (vty, "mpls_te_link_metric: Something wrong!%s", VTY_NEWLINE); - return CMD_WARNING; - } - - value = strtoul (argv[0], NULL, 10); + int format; - if (ntohs (lp->te_metric.header.type) == 0 - || ntohl (lp->te_metric.value) != value) + if (OspfMplsTE.status == enabled) { - set_linkparams_te_metric (lp, value); - - if (OspfMplsTE.status == enabled) - if (lp->area != NULL) - { - if (lp->flags & LPFLG_LSA_ENGAGED) - ospf_mpls_te_lsa_schedule (lp, REFRESH_THIS_LSA); - else - ospf_mpls_te_lsa_schedule (lp, REORIGINATE_PER_AREA); - } - } - return CMD_SUCCESS; -} - -DEFUN (mpls_te_link_maxbw, - mpls_te_link_maxbw_cmd, - "mpls-te link max-bw BANDWIDTH", - "MPLS-TE specific commands\n" - "Configure MPLS-TE link parameters\n" - "Maximum bandwidth that can be used\n" - "Bytes/second (IEEE floating point format)\n") -{ - struct interface *ifp = (struct interface *) vty->index; - struct mpls_te_link *lp; - float f1, f2; - if ((lp = lookup_linkparams_by_ifp (ifp)) == NULL) - { - vty_out (vty, "mpls_te_link_maxbw: Something wrong!%s", VTY_NEWLINE); - return CMD_WARNING; - } + /* Read and Check inter_as mode */ + if (strcmp (mode_name, "as") == 0) + mode = AS; + else if (strcmp (mode_name, "area") == 0) + { + mode = Area; + VTY_GET_OSPF_AREA_ID (OspfMplsTE.interas_areaid, format, area_id); + } + else + { + vty_out (vty, "Unknown mode. Please choose between as or area%s", + VTY_NEWLINE); + return CMD_WARNING; + } - ntohf (&lp->max_bw.value, &f1); - if (sscanf (argv[0], "%g", &f2) != 1) - { - vty_out (vty, "mpls_te_link_maxbw: fscanf: %s%s", safe_strerror (errno), VTY_NEWLINE); - return CMD_WARNING; - } + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("MPLS-TE: Inter-AS enable with %s flooding support", + mode2text[mode]); - if (ntohs (lp->max_bw.header.type) == 0 - || f1 != f2) - { - set_linkparams_max_bw (lp, &f2); + /* Register new callbacks regarding the flooding scope (AS or Area) */ + if (ospf_mpls_te_register (mode) < 0) + { + vty_out (vty, "Internal error: Unable to register Inter-AS functions%s", + VTY_NEWLINE); + return CMD_WARNING; + } - if (OspfMplsTE.status == enabled) - if (lp->area != NULL) - { - if (lp->flags & LPFLG_LSA_ENGAGED) - ospf_mpls_te_lsa_schedule (lp, REFRESH_THIS_LSA); - else - ospf_mpls_te_lsa_schedule (lp, REORIGINATE_PER_AREA); - } + /* Enable mode and re-originate LSA if needed */ + if ((OspfMplsTE.inter_as == Disable) && (mode != OspfMplsTE.inter_as)) + { + OspfMplsTE.inter_as = mode; + /* Re-originate all InterAS-TEv2 LSA */ + for (ALL_LIST_ELEMENTS_RO (OspfMplsTE.iflist, node, lp)) + { + if (IS_INTER_AS (lp->type)) + { + if (mode == AS) + lp->type |= FLOOD_AS; + else + lp->type |= FLOOD_AREA; + ospf_mpls_te_lsa_schedule (lp, REORIGINATE_THIS_LSA); + } + } + } + else + { + vty_out (vty, "Please change Inter-AS support to disable first before going to mode %s%s", + mode2text[mode], VTY_NEWLINE); + return CMD_WARNING; + } } return CMD_SUCCESS; } -DEFUN (mpls_te_link_max_rsv_bw, - mpls_te_link_max_rsv_bw_cmd, - "mpls-te link max-rsv-bw BANDWIDTH", - "MPLS-TE specific commands\n" - "Configure MPLS-TE link parameters\n" - "Maximum bandwidth that may be reserved\n" - "Bytes/second (IEEE floating point format)\n") +DEFUN (ospf_mpls_te_inter_as_as, + ospf_mpls_te_inter_as_cmd, + "mpls-te inter-as as", + MPLS_TE_STR + "Configure MPLS-TE Inter-AS support\n" + "AS native mode self originate INTER_AS LSA with Type 11 (as flooding scope)\n") { - struct interface *ifp = (struct interface *) vty->index; - struct mpls_te_link *lp; - float f1, f2; - - if ((lp = lookup_linkparams_by_ifp (ifp)) == NULL) - { - vty_out (vty, "mpls_te_link_max_rsv_bw: Something wrong!%s", VTY_NEWLINE); - return CMD_WARNING; - } - - ntohf (&lp->max_rsv_bw.value, &f1); - if (sscanf (argv[0], "%g", &f2) != 1) - { - vty_out (vty, "mpls_te_link_max_rsv_bw: fscanf: %s%s", safe_strerror (errno), VTY_NEWLINE); - return CMD_WARNING; - } - - if (ntohs (lp->max_rsv_bw.header.type) == 0 - || f1 != f2) - { - set_linkparams_max_rsv_bw (lp, &f2); - - if (OspfMplsTE.status == enabled) - if (lp->area != NULL) - { - if (lp->flags & LPFLG_LSA_ENGAGED) - ospf_mpls_te_lsa_schedule (lp, REFRESH_THIS_LSA); - else - ospf_mpls_te_lsa_schedule (lp, REORIGINATE_PER_AREA); - } - } - return CMD_SUCCESS; + return set_inter_as_mode (vty, "as", ""); } -DEFUN (mpls_te_link_unrsv_bw, - mpls_te_link_unrsv_bw_cmd, - "mpls-te link unrsv-bw <0-7> BANDWIDTH", - "MPLS-TE specific commands\n" - "Configure MPLS-TE link parameters\n" - "Unreserved bandwidth at each priority level\n" - "Priority\n" - "Bytes/second (IEEE floating point format)\n") +DEFUN (ospf_mpls_te_inter_as_area, + ospf_mpls_te_inter_as_area_cmd, + "mpls-te inter-as area (A.B.C.D|<0-4294967295>)", + MPLS_TE_STR + "Configure MPLS-TE Inter-AS support\n" + "AREA native mode self originate INTER_AS LSA with Type 10 (area flooding scope)\n" + "OSPF area ID in IP format\n" + "OSPF area ID as decimal value\n") { - struct interface *ifp = (struct interface *) vty->index; - struct mpls_te_link *lp; - int priority; - float f1, f2; - - if ((lp = lookup_linkparams_by_ifp (ifp)) == NULL) - { - vty_out (vty, "mpls_te_link_unrsv_bw: Something wrong!%s", VTY_NEWLINE); - return CMD_WARNING; - } - - /* We don't have to consider about range check here. */ - if (sscanf (argv[0], "%d", &priority) != 1) - { - vty_out (vty, "mpls_te_link_unrsv_bw: fscanf: %s%s", safe_strerror (errno), VTY_NEWLINE); - return CMD_WARNING; - } - - ntohf (&lp->unrsv_bw.value [priority], &f1); - if (sscanf (argv[1], "%g", &f2) != 1) - { - vty_out (vty, "mpls_te_link_unrsv_bw: fscanf: %s%s", safe_strerror (errno), VTY_NEWLINE); - return CMD_WARNING; - } - - if (ntohs (lp->unrsv_bw.header.type) == 0 - || f1 != f2) - { - set_linkparams_unrsv_bw (lp, priority, &f2); - - if (OspfMplsTE.status == enabled) - if (lp->area != NULL) - { - if (lp->flags & LPFLG_LSA_ENGAGED) - ospf_mpls_te_lsa_schedule (lp, REFRESH_THIS_LSA); - else - ospf_mpls_te_lsa_schedule (lp, REORIGINATE_PER_AREA); - } - } - return CMD_SUCCESS; + return set_inter_as_mode (vty, "area", argv[0]); } -DEFUN (mpls_te_link_rsc_clsclr, - mpls_te_link_rsc_clsclr_cmd, - "mpls-te link rsc-clsclr BITPATTERN", - "MPLS-TE specific commands\n" - "Configure MPLS-TE link parameters\n" - "Administrative group membership\n" - "32-bit Hexadecimal value (ex. 0xa1)\n") +DEFUN (no_ospf_mpls_te_inter_as, + no_ospf_mpls_te_inter_as_cmd, + "no mpls-te inter-as", + NO_STR + MPLS_TE_STR + "Disable MPLS-TE Inter-AS support\n") { - struct interface *ifp = (struct interface *) vty->index; + + struct listnode *node, *nnode; struct mpls_te_link *lp; - unsigned long value; - if ((lp = lookup_linkparams_by_ifp (ifp)) == NULL) - { - vty_out (vty, "mpls_te_link_rsc_clsclr: Something wrong!%s", VTY_NEWLINE); - return CMD_WARNING; - } + if (IS_DEBUG_OSPF_EVENT) + zlog_debug ("MPLS-TE: Inter-AS support OFF"); - if (sscanf (argv[0], "0x%lx", &value) != 1) + if ((OspfMplsTE.status == enabled) && (OspfMplsTE.inter_as != Disable)) { - vty_out (vty, "mpls_te_link_rsc_clsclr: fscanf: %s%s", safe_strerror (errno), VTY_NEWLINE); - return CMD_WARNING; + OspfMplsTE.inter_as = Disable; + /* Flush all Inter-AS LSA */ + for (ALL_LIST_ELEMENTS (OspfMplsTE.iflist, node, nnode, lp)) + if (IS_INTER_AS (lp->type) && CHECK_FLAG (lp->flags, LPFLG_LSA_ENGAGED)) + ospf_mpls_te_lsa_schedule (lp, FLUSH_THIS_LSA); } - if (ntohs (lp->rsc_clsclr.header.type) == 0 - || ntohl (lp->rsc_clsclr.value) != value) - { - set_linkparams_rsc_clsclr (lp, value); + /* Deregister the Callbacks for Inter-AS suport */ + ospf_mpls_te_unregister (); - if (OspfMplsTE.status == enabled) - if (lp->area != NULL) - { - if (lp->flags & LPFLG_LSA_ENGAGED) - ospf_mpls_te_lsa_schedule (lp, REFRESH_THIS_LSA); - else - ospf_mpls_te_lsa_schedule (lp, REORIGINATE_PER_AREA); - } - } return CMD_SUCCESS; } -DEFUN (show_mpls_te_router, - show_mpls_te_router_cmd, - "show mpls-te router", +DEFUN (show_ip_ospf_mpls_te_router, + show_ip_ospf_mpls_te_router_cmd, + "show ip ospf mpls-te router", SHOW_STR + IP_STR + OSPF_STR "MPLS-TE information\n" - "Router information\n") + "MPLS-TE Router parameters\n") { if (OspfMplsTE.status == enabled) { - vty_out (vty, "--- MPLS-TE router parameters ---%s", - VTY_NEWLINE); + vty_out (vty, "--- MPLS-TE router parameters ---%s", VTY_NEWLINE); if (ntohs (OspfMplsTE.router_addr.header.type) != 0) show_vty_router_addr (vty, &OspfMplsTE.router_addr.header); @@ -1823,29 +2514,73 @@ static void show_mpls_te_link_sub (struct vty *vty, struct interface *ifp) { struct mpls_te_link *lp; - struct te_tlv_header *tlvh; - if ((OspfMplsTE.status == enabled) - && (! if_is_loopback (ifp) && if_is_up (ifp) && ospf_oi_count (ifp) > 0) - && ((lp = lookup_linkparams_by_ifp (ifp)) != NULL)) + if ((OspfMplsTE.status == enabled) + && HAS_LINK_PARAMS(ifp) + && !if_is_loopback (ifp) + && if_is_up (ifp) + && ((lp = lookup_linkparams_by_ifp (ifp)) != NULL)) { + /* Continue only if interface is not passive or support Inter-AS TEv2 */ + if (!(ospf_oi_count (ifp) > 0)) + { + if (IS_INTER_AS (lp->type)) + { + vty_out (vty, "-- Inter-AS TEv2 link parameters for %s --%s", + ifp->name, VTY_NEWLINE); + } + else + { + /* MPLS-TE is not activate on this interface */ + /* or this interface is passive and Inter-AS TEv2 is not activate */ + vty_out (vty, " %s: MPLS-TE is disabled on this interface%s", + ifp->name, VTY_NEWLINE); + return; + } + } + else + { vty_out (vty, "-- MPLS-TE link parameters for %s --%s", ifp->name, VTY_NEWLINE); + } - show_vty_link_subtlv_link_type (vty, &lp->link_type.header); - show_vty_link_subtlv_link_id (vty, &lp->link_id.header); - - if ((tlvh = (struct te_tlv_header *) lp->lclif_ipaddr) != NULL) - show_vty_link_subtlv_lclif_ipaddr (vty, tlvh); - if ((tlvh = (struct te_tlv_header *) lp->rmtif_ipaddr) != NULL) - show_vty_link_subtlv_rmtif_ipaddr (vty, tlvh); - - show_vty_link_subtlv_te_metric (vty, &lp->te_metric.header); - - show_vty_link_subtlv_max_bw (vty, &lp->max_bw.header); - show_vty_link_subtlv_max_rsv_bw (vty, &lp->max_rsv_bw.header); - show_vty_link_subtlv_unrsv_bw (vty, &lp->unrsv_bw.header); - show_vty_link_subtlv_rsc_clsclr (vty, &lp->rsc_clsclr.header); + if (TLV_TYPE(lp->link_type) != 0) + show_vty_link_subtlv_link_type (vty, &lp->link_type.header); + if (TLV_TYPE(lp->link_id) != 0) + show_vty_link_subtlv_link_id (vty, &lp->link_id.header); + if (TLV_TYPE(lp->lclif_ipaddr) != 0) + show_vty_link_subtlv_lclif_ipaddr (vty, &lp->lclif_ipaddr.header); + if (TLV_TYPE(lp->rmtif_ipaddr) != 0) + show_vty_link_subtlv_rmtif_ipaddr (vty, &lp->rmtif_ipaddr.header); + if (TLV_TYPE(lp->rip) != 0) + show_vty_link_subtlv_rip (vty, &lp->rip.header); + if (TLV_TYPE(lp->ras) != 0) + show_vty_link_subtlv_ras (vty, &lp->ras.header); + if (TLV_TYPE(lp->te_metric) != 0) + show_vty_link_subtlv_te_metric (vty, &lp->te_metric.header); + if (TLV_TYPE(lp->max_bw) != 0) + show_vty_link_subtlv_max_bw (vty, &lp->max_bw.header); + if (TLV_TYPE(lp->max_rsv_bw) != 0) + show_vty_link_subtlv_max_rsv_bw (vty, &lp->max_rsv_bw.header); + if (TLV_TYPE(lp->unrsv_bw) != 0) + show_vty_link_subtlv_unrsv_bw (vty, &lp->unrsv_bw.header); + if (TLV_TYPE(lp->rsc_clsclr) != 0) + show_vty_link_subtlv_rsc_clsclr (vty, &lp->rsc_clsclr.header); + if (TLV_TYPE(lp->av_delay) != 0) + show_vty_link_subtlv_av_delay (vty, &lp->av_delay.header); + if (TLV_TYPE(lp->mm_delay) != 0) + show_vty_link_subtlv_mm_delay (vty, &lp->mm_delay.header); + if (TLV_TYPE(lp->delay_var) != 0) + show_vty_link_subtlv_delay_var (vty, &lp->delay_var.header); + if (TLV_TYPE(lp->pkt_loss) != 0) + show_vty_link_subtlv_pkt_loss (vty, &lp->pkt_loss.header); + if (TLV_TYPE(lp->res_bw) != 0) + show_vty_link_subtlv_res_bw (vty, &lp->res_bw.header); + if (TLV_TYPE(lp->ava_bw) != 0) + show_vty_link_subtlv_ava_bw (vty, &lp->ava_bw.header); + if (TLV_TYPE(lp->use_bw) != 0) + show_vty_link_subtlv_use_bw (vty, &lp->use_bw.header); + vty_out (vty, "---------------%s%s", VTY_NEWLINE, VTY_NEWLINE); } else { @@ -1856,10 +2591,12 @@ show_mpls_te_link_sub (struct vty *vty, struct interface *ifp) return; } -DEFUN (show_mpls_te_link, - show_mpls_te_link_cmd, - "show mpls-te interface [INTERFACE]", +DEFUN (show_ip_ospf_mpls_te_link, + show_ip_ospf_mpls_te_link_cmd, + "show ip ospf mpls-te interface [INTERFACE]", SHOW_STR + IP_STR + OSPF_STR "MPLS-TE information\n" "Interface information\n" "Interface name\n") @@ -1888,23 +2625,15 @@ DEFUN (show_mpls_te_link, static void ospf_mpls_te_register_vty (void) { - install_element (VIEW_NODE, &show_mpls_te_router_cmd); - install_element (VIEW_NODE, &show_mpls_te_link_cmd); - install_element (ENABLE_NODE, &show_mpls_te_router_cmd); - install_element (ENABLE_NODE, &show_mpls_te_link_cmd); - - install_element (OSPF_NODE, &mpls_te_cmd); - install_element (OSPF_NODE, &no_mpls_te_cmd); - install_element (OSPF_NODE, &mpls_te_on_cmd); - install_element (OSPF_NODE, &mpls_te_router_addr_cmd); + install_element (VIEW_NODE, &show_ip_ospf_mpls_te_router_cmd); + install_element (VIEW_NODE, &show_ip_ospf_mpls_te_link_cmd); - install_element (INTERFACE_NODE, &mpls_te_link_metric_cmd); - install_element (INTERFACE_NODE, &mpls_te_link_maxbw_cmd); - install_element (INTERFACE_NODE, &mpls_te_link_max_rsv_bw_cmd); - install_element (INTERFACE_NODE, &mpls_te_link_unrsv_bw_cmd); - install_element (INTERFACE_NODE, &mpls_te_link_rsc_clsclr_cmd); + install_element (OSPF_NODE, &ospf_mpls_te_on_cmd); + install_element (OSPF_NODE, &no_ospf_mpls_te_cmd); + install_element (OSPF_NODE, &ospf_mpls_te_router_addr_cmd); + install_element (OSPF_NODE, &ospf_mpls_te_inter_as_cmd); + install_element (OSPF_NODE, &ospf_mpls_te_inter_as_area_cmd); + install_element (OSPF_NODE, &no_ospf_mpls_te_inter_as_cmd); return; } - -#endif /* HAVE_OSPF_TE */ diff --git a/ospfd/ospf_te.h b/ospfd/ospf_te.h index e8511cdfb..8bb77c40c 100644 --- a/ospfd/ospf_te.h +++ b/ospfd/ospf_te.h @@ -1,8 +1,11 @@ /* - * This is an implementation of draft-katz-yeung-ospf-traffic-06.txt + * This is an implementation of RFC3630, RFC5392 & RFC6827 * Copyright (C) 2001 KDD R&D Laboratories, Inc. * http://www.kddlabs.co.jp/ * + * Copyright (C) 2012 Orange Labs + * http://www.orange.com + * * This file is part of GNU Zebra. * * GNU Zebra is free software; you can redistribute it and/or modify it @@ -21,6 +24,10 @@ * 02111-1307, USA. */ +/* Add support of RFC7471 */ +/* Add support of RFC5392 */ +/* Add support of RFC6827 (partial) */ + #ifndef _ZEBRA_OSPF_MPLS_TE_H #define _ZEBRA_OSPF_MPLS_TE_H @@ -41,7 +48,8 @@ * */ -#define LEGAL_TE_INSTANCE_RANGE(i) (0 <= (i) && (i) <= 0xffff) +#define MAX_LEGAL_TE_INSTANCE_NUM (0xffff) +#define LEGAL_TE_INSTANCE_RANGE(i) (0 <= (i) && (i) <= 0xffff) /* * 24 16 8 0 @@ -62,6 +70,31 @@ * +--------+--------+--------+--------+ --- */ +/* Following define the type of TE link regarding the various RFC */ +#define STD_TE 0x01 +#define GMPLS 0x02 +#define INTER_AS 0x04 +#define PSEUDO_TE 0x08 +#define FLOOD_AREA 0x10 +#define FLOOD_AS 0x20 +#define EMULATED 0x80 + +#define IS_STD_TE(x) (x & STD_TE) +#define IS_PSEUDO_TE(x) (x & PSEUDO_TE) +#define IS_INTER_AS(x) (x & INTER_AS) +#define IS_EMULATED(x) (x & EMULATED) +#define IS_FLOOD_AREA(x) (x & FLOOD_AREA) +#define IS_FLOOD_AS(x) (x & FLOOD_AS) +#define IS_INTER_AS_EMU(x) (x & INTER_AS & EMULATED) +#define IS_INTER_AS_AS(x) (x & INTER_AS & FLOOD_AS) + +/* Flags to manage TE Link LSA */ +#define LPFLG_LSA_INACTIVE 0x0 +#define LPFLG_LSA_ACTIVE 0x1 +#define LPFLG_LSA_ENGAGED 0x2 +#define LPFLG_LOOKUP_DONE 0x4 +#define LPFLG_LSA_FORCED_REFRESH 0x8 + /* * Following section defines TLV (tag, length, value) structures, * used for Traffic Engineering. @@ -87,10 +120,18 @@ struct te_tlv_header #define TLV_HDR_NEXT(tlvh) \ (struct te_tlv_header *)((char *)(tlvh) + TLV_SIZE(tlvh)) +#define TLV_HDR_SUBTLV(tlvh) \ + (struct te_tlv_header *)((char *)(tlvh) + TLV_HDR_SIZE) + +#define TLV_TYPE(tlvh) tlvh.header.type +#define TLV_LEN(tlvh) tlvh.header.length +#define TLV_HDR(tlvh) tlvh.header + + /* * Following section defines TLV body parts. */ -/* Router Address TLV *//* Mandatory */ +/* Router Address TLV */ /* Mandatory */ #define TE_TLV_ROUTER_ADDR 1 struct te_tlv_router_addr { @@ -106,12 +147,16 @@ struct te_tlv_link /* A set of link-sub-TLVs will follow. */ }; -/* Link Type Sub-TLV *//* Mandatory */ -#define TE_LINK_SUBTLV_LINK_TYPE 1 +#define TE_LINK_SUBTLV_DEF_SIZE 4 + +/* Link Type Sub-TLV */ /* Mandatory */ +#define TE_LINK_SUBTLV_LINK_TYPE 1 +#define TE_LINK_SUBTLV_TYPE_SIZE 1 struct te_link_subtlv_link_type { struct te_tlv_header header; /* Value length is 1 octet. */ - struct { + struct + { #define LINK_TYPE_SUBTLV_VALUE_PTP 1 #define LINK_TYPE_SUBTLV_VALUE_MA 2 u_char value; @@ -119,75 +164,303 @@ struct te_link_subtlv_link_type } link_type; }; -/* Link Sub-TLV: Link ID *//* Mandatory */ -#define TE_LINK_SUBTLV_LINK_ID 2 +/* Link Sub-TLV: Link ID */ /* Mandatory */ +#define TE_LINK_SUBTLV_LINK_ID 2 struct te_link_subtlv_link_id { struct te_tlv_header header; /* Value length is 4 octets. */ struct in_addr value; /* Same as router-lsa's link-id. */ }; -/* Link Sub-TLV: Local Interface IP Address *//* Optional */ -#define TE_LINK_SUBTLV_LCLIF_IPADDR 3 +/* Link Sub-TLV: Local Interface IP Address */ /* Optional */ +#define TE_LINK_SUBTLV_LCLIF_IPADDR 3 struct te_link_subtlv_lclif_ipaddr { struct te_tlv_header header; /* Value length is 4 x N octets. */ struct in_addr value[1]; /* Local IP address(es). */ }; -/* Link Sub-TLV: Remote Interface IP Address *//* Optional */ -#define TE_LINK_SUBTLV_RMTIF_IPADDR 4 +/* Link Sub-TLV: Remote Interface IP Address */ /* Optional */ +#define TE_LINK_SUBTLV_RMTIF_IPADDR 4 struct te_link_subtlv_rmtif_ipaddr { struct te_tlv_header header; /* Value length is 4 x N octets. */ struct in_addr value[1]; /* Neighbor's IP address(es). */ }; -/* Link Sub-TLV: Traffic Engineering Metric *//* Optional */ -#define TE_LINK_SUBTLV_TE_METRIC 5 +/* Link Sub-TLV: Traffic Engineering Metric */ /* Optional */ +#define TE_LINK_SUBTLV_TE_METRIC 5 struct te_link_subtlv_te_metric { struct te_tlv_header header; /* Value length is 4 octets. */ u_int32_t value; /* Link metric for TE purpose. */ }; -/* Link Sub-TLV: Maximum Bandwidth *//* Optional */ -#define TE_LINK_SUBTLV_MAX_BW 6 +/* Link Sub-TLV: Maximum Bandwidth */ /* Optional */ +#define TE_LINK_SUBTLV_MAX_BW 6 struct te_link_subtlv_max_bw { struct te_tlv_header header; /* Value length is 4 octets. */ float value; /* bytes/sec */ }; -/* Link Sub-TLV: Maximum Reservable Bandwidth *//* Optional */ -#define TE_LINK_SUBTLV_MAX_RSV_BW 7 +/* Link Sub-TLV: Maximum Reservable Bandwidth */ /* Optional */ +#define TE_LINK_SUBTLV_MAX_RSV_BW 7 struct te_link_subtlv_max_rsv_bw { struct te_tlv_header header; /* Value length is 4 octets. */ float value; /* bytes/sec */ }; -/* Link Sub-TLV: Unreserved Bandwidth *//* Optional */ -#define TE_LINK_SUBTLV_UNRSV_BW 8 +/* Link Sub-TLV: Unreserved Bandwidth */ /* Optional */ +#define TE_LINK_SUBTLV_UNRSV_BW 8 +#define TE_LINK_SUBTLV_UNRSV_SIZE 32 struct te_link_subtlv_unrsv_bw { struct te_tlv_header header; /* Value length is 32 octets. */ - float value[8]; /* One for each priority level. */ + float value[MAX_CLASS_TYPE]; /* One for each priority level. */ }; -/* Link Sub-TLV: Resource Class/Color *//* Optional */ -#define TE_LINK_SUBTLV_RSC_CLSCLR 9 +/* Link Sub-TLV: Resource Class/Color */ /* Optional */ +#define TE_LINK_SUBTLV_RSC_CLSCLR 9 struct te_link_subtlv_rsc_clsclr { struct te_tlv_header header; /* Value length is 4 octets. */ u_int32_t value; /* Admin. group membership. */ }; -/* Here are "non-official" architechtual constants. */ +/* For RFC6827 */ +/* Local and Remote TE Router ID */ +#define TE_LINK_SUBTLV_LRRID 10 +#define TE_LINK_SUBTLV_LRRID_SIZE 8 +struct te_link_subtlv_lrrid +{ + struct te_tlv_header header; /* Value length is 8 octets. */ + struct in_addr local; /* Local TE Router Identifier */ + struct in_addr remote; /* Remote TE Router Identifier */ +}; + +/* RFC4203: Link Local/Remote Identifiers */ +#define TE_LINK_SUBTLV_LLRI 11 +#define TE_LINK_SUBTLV_LLRI_SIZE 8 +struct te_link_subtlv_llri +{ + struct te_tlv_header header; /* Value length is 8 octets. */ + u_int32_t local; /* Link Local Identifier */ + u_int32_t remote; /* Link Remote Identifier */ +}; + +/* Inter-RA Export Upward sub-TLV (12) and Inter-RA Export Downward sub-TLV (13) (RFC6827bis) are not yet supported */ +/* SUBTLV 14-16 (RFC4203) are not yet supported */ +/* Bandwidth Constraints sub-TLV (17) (RFC4124) is not yet supported */ +/* SUBLV 18-20 are for OSPFv6 TE (RFC5329). see ospf6d */ + +/* For RFC 5392 */ +/* Remote AS Number sub-TLV */ +#define TE_LINK_SUBTLV_RAS 21 +struct te_link_subtlv_ras +{ + struct te_tlv_header header; /* Value length is 4 octets. */ + u_int32_t value; /* Remote AS number */ +}; + +/* IPv4 Remote ASBR ID Sub-TLV */ +#define TE_LINK_SUBTLV_RIP 22 +struct te_link_subtlv_rip +{ + struct te_tlv_header header; /* Value length is 4 octets. */ + struct in_addr value; /* Remote ASBR IP address */ +}; + +/* SUBTLV 24 is IPv6 Remote ASBR ID (RFC5392). see ospf6d */ + +/* SUBTLV 23 (RFC5330) and 25 (RFC6001) are not yet supported */ + +/* SUBTLV 26 (RFC7308) is not yet supported */ + +/* RFC7471 */ +/* Link Sub-TLV: Average Link Delay */ /* Optional */ +#define TE_LINK_SUBTLV_AV_DELAY 27 +struct te_link_subtlv_av_delay +{ + struct te_tlv_header header; /* Value length is 4 bytes. */ + u_int32_t value; /* delay in micro-seconds only 24 bits => 0 ... 16777215 + with Anomalous Bit as Upper most bit */ +}; + +/* Link Sub-TLV: Low/High Link Delay */ +#define TE_LINK_SUBTLV_MM_DELAY 28 +#define TE_LINK_SUBTLV_MM_DELAY_SIZE 8 +struct te_link_subtlv_mm_delay +{ + struct te_tlv_header header; /* Value length is 8 bytes. */ + u_int32_t low; /* low delay in micro-seconds only 24 bits => 0 ... 16777215 + with Anomalous Bit (A) as Upper most bit */ + u_int32_t high; /* high delay in micro-seconds only 24 bits => 0 ... 16777215 */ +}; + +/* Link Sub-TLV: Link Delay Variation i.e. Jitter */ +#define TE_LINK_SUBTLV_DELAY_VAR 29 +struct te_link_subtlv_delay_var +{ + struct te_tlv_header header; /* Value length is 4 bytes. */ + u_int32_t value; /* interval in micro-seconds only 24 bits => 0 ... 16777215 */ +}; + +/* Link Sub-TLV: Routine Unidirectional Link Packet Loss */ +#define TE_LINK_SUBTLV_PKT_LOSS 30 +struct te_link_subtlv_pkt_loss +{ + struct te_tlv_header header; /* Value length is 4 bytes. */ + u_int32_t value; /* in percentage of total traffic only 24 bits (2^24 - 2) + with Anomalous Bit as Upper most bit */ +}; + +/* Link Sub-TLV: Unidirectional Residual Bandwidth */ /* Optional */ +#define TE_LINK_SUBTLV_RES_BW 31 +struct te_link_subtlv_res_bw +{ + struct te_tlv_header header; /* Value length is 4 bytes. */ + float value; /* bandwidth in IEEE floating point format with units in bytes per second */ +}; + +/* Link Sub-TLV: Unidirectional Available Bandwidth */ /* Optional */ +#define TE_LINK_SUBTLV_AVA_BW 32 +struct te_link_subtlv_ava_bw +{ + struct te_tlv_header header; /* Value length is 4 octets. */ + float value; /* bandwidth in IEEE floating point format with units in bytes per second */ +}; + +/* Link Sub-TLV: Unidirectional Utilized Bandwidth */ /* Optional */ +#define TE_LINK_SUBTLV_USE_BW 33 +struct te_link_subtlv_use_bw +{ + struct te_tlv_header header; /* Value length is 4 octets. */ + float value; /* bandwidth in IEEE floating point format with units in bytes per second */ +}; + +#define TE_LINK_SUBTLV_MAX 34 /* Last SUBTLV + 1 */ + +/* Here are "non-official" architectural constants. */ #define MPLS_TE_MINIMUM_BANDWIDTH 1.0 /* Reasonable? *//* XXX */ +/* Following declaration concerns the MPLS-TE and LINk-TE management */ +typedef enum _opcode_t +{ REORIGINATE_THIS_LSA, REFRESH_THIS_LSA, FLUSH_THIS_LSA } opcode_t; + +typedef enum _status_t +{ disabled, enabled } status_t; + +/* Mode for Inter-AS Opaque-LSA */ +enum inter_as_mode { Disable, AS, Area }; + +struct te_link_subtlv +{ + struct te_tlv_header header; + union + { + u_int32_t link_type; + struct in_addr link_id; + struct in_addr lclif; + struct in_addr rmtif; + u_int32_t te_metric; + float max_bw; + float max_rsv_bw; + float unrsv[8]; + u_int32_t rsc_clsclr; + u_int32_t llri[2]; + u_int32_t ras; + struct in_addr rip; + struct in_addr lrrid[2]; + u_int32_t av_delay; + u_int32_t mm_delay; + u_int32_t delay_var; + u_int32_t pkt_loss; + float res_bw; + float ava_bw; + float use_bw; + } value; +}; + +/* Following structure are internal use only. */ +struct ospf_mpls_te +{ + /* Status of MPLS-TE: enable or disbale */ + status_t status; + + /* RFC5392 */ + enum inter_as_mode inter_as; + struct in_addr interas_areaid; + + /* List elements are zebra-interfaces (ifp), not ospf-interfaces (oi). */ + struct list *iflist; + + /* Store Router-TLV in network byte order. */ + struct te_tlv_router_addr router_addr; +}; + +struct mpls_te_link +{ + /* + * According to MPLS-TE (draft) specification, 24-bit Opaque-ID field + * is subdivided into 8-bit "unused" field and 16-bit "instance" field. + * In this implementation, each Link-TLV has its own instance. + */ + u_int32_t instance; + + /* Reference pointer to a Zebra-interface. */ + struct interface *ifp; + + /* Area info in which this MPLS-TE link belongs to. */ + struct ospf_area *area; + + /* Flags to manage this link parameters. */ + u_int32_t flags; + + /* Type of MPLS-TE link: RFC3630, RFC5392, RFC5392 emulated, RFC6827 */ + u_int8_t type; + + /* Store Link-TLV in network byte order. */ + /* RFC3630 & RFC6827 / RFC 6827 */ + struct te_tlv_link link_header; + struct te_link_subtlv_link_type link_type; + struct te_link_subtlv_link_id link_id; + struct te_link_subtlv_lclif_ipaddr lclif_ipaddr; + struct te_link_subtlv_rmtif_ipaddr rmtif_ipaddr; + struct te_link_subtlv_te_metric te_metric; + struct te_link_subtlv_max_bw max_bw; + struct te_link_subtlv_max_rsv_bw max_rsv_bw; + struct te_link_subtlv_unrsv_bw unrsv_bw; + struct te_link_subtlv_rsc_clsclr rsc_clsclr; + /* RFC4203 */ + struct te_link_subtlv_llri llri; + /* RFC5392 */ + struct te_link_subtlv_ras ras; + struct te_link_subtlv_rip rip; + /* RFC6827 */ + struct te_link_subtlv_lrrid lrrid; + /* RFC7471 */ + struct te_link_subtlv_av_delay av_delay; + struct te_link_subtlv_mm_delay mm_delay; + struct te_link_subtlv_delay_var delay_var; + struct te_link_subtlv_pkt_loss pkt_loss; + struct te_link_subtlv_res_bw res_bw; + struct te_link_subtlv_ava_bw ava_bw; + struct te_link_subtlv_use_bw use_bw; + + struct in_addr adv_router; + struct in_addr id; +}; + /* Prototypes. */ extern int ospf_mpls_te_init (void); extern void ospf_mpls_te_term (void); +extern struct ospf_mpls_te *get_ospf_mpls_te (void); +extern void ospf_mpls_te_update_if (struct interface *); +extern void ospf_mpls_te_lsa_schedule (struct mpls_te_link *, opcode_t); +extern u_int32_t get_mpls_te_instance_value (void); +extern void set_linkparams_llri (struct mpls_te_link *, u_int32_t, u_int32_t); +extern void set_linkparams_lrrid (struct mpls_te_link *, struct in_addr, struct in_addr); #endif /* _ZEBRA_OSPF_MPLS_TE_H */ diff --git a/ospfd/ospf_vty.c b/ospfd/ospf_vty.c index 97c8e8d62..478d4ffd9 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,9 +61,9 @@ static const char *ospf_network_type_str[] = "LOOPBACK" }; - + /* Utility functions. */ -static int +int ospf_str2area_id (const char *str, struct in_addr *area_id, int *format) { char *endptr = NULL; @@ -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", @@ -254,9 +254,9 @@ ospf_passive_interface_default (struct ospf *ospf, u_char newval) } static void -ospf_passive_interface_update (struct ospf *ospf, struct interface *ifp, - struct in_addr addr, - struct ospf_if_params *params, u_char value) +ospf_passive_interface_update_addr (struct ospf *ospf, struct interface *ifp, + struct ospf_if_params *params, u_char value, + struct in_addr addr) { u_char dflt; @@ -276,7 +276,14 @@ ospf_passive_interface_update (struct ospf *ospf, struct interface *ifp, ospf_free_if_params (ifp, addr); ospf_if_update_params (ifp, addr); } - else +} + +static void +ospf_passive_interface_update (struct ospf *ospf, struct interface *ifp, + struct ospf_if_params *params, u_char value) +{ + params->passive_interface = value; + if (params == IF_DEF_PARAMS (ifp)) { if (value != ospf->passive_interface_default) SET_IF_PARAM (params, passive_interface); @@ -285,6 +292,41 @@ ospf_passive_interface_update (struct ospf *ospf, struct interface *ifp, } } +/* get the appropriate ospf parameters structure, checking if + * there's a valid interface address at the argi'th argv index + */ +enum { + VTY_SET = 0, + VTY_UNSET, +}; +#define OSPF_VTY_GET_IF_PARAMS(ifp,params,argi,addr,set) \ + (params) = IF_DEF_PARAMS ((ifp)); \ + \ + if (argc == (argi) + 1) \ + { \ + int ret = inet_aton(argv[(argi)], &(addr)); \ + if (!ret) \ + { \ + vty_out (vty, "Please specify interface address by A.B.C.D%s", \ + VTY_NEWLINE); \ + return CMD_WARNING; \ + } \ + (params) = ospf_get_if_params ((ifp), (addr)); \ + \ + if (set) \ + ospf_if_update_params ((ifp), (addr)); \ + else if ((params) == NULL) \ + return CMD_SUCCESS; \ + } + +#define OSPF_VTY_PARAM_UNSET(params,var,ifp,addr) \ + UNSET_IF_PARAM ((params), var); \ + if ((params) != IF_DEF_PARAMS ((ifp))) \ + { \ + ospf_free_if_params ((ifp), (addr)); \ + ospf_if_update_params ((ifp), (addr)); \ + } + DEFUN (ospf_passive_interface, ospf_passive_interface_addr_cmd, "passive-interface IFNAME A.B.C.D", @@ -320,8 +362,11 @@ DEFUN (ospf_passive_interface, params = ospf_get_if_params (ifp, addr); ospf_if_update_params (ifp, addr); + ospf_passive_interface_update_addr (ospf, ifp, params, + OSPF_IF_PASSIVE, addr); } - ospf_passive_interface_update (ospf, ifp, addr, params, OSPF_IF_PASSIVE); + + ospf_passive_interface_update (ospf, ifp, params, OSPF_IF_PASSIVE); /* XXX We should call ospf_if_set_multicast on exactly those * interfaces for which the passive property changed. It is too much @@ -397,9 +442,11 @@ DEFUN (no_ospf_passive_interface, params = ospf_lookup_if_params (ifp, addr); if (params == NULL) return CMD_SUCCESS; + ospf_passive_interface_update_addr (ospf, ifp, params, OSPF_IF_ACTIVE, + addr); } - ospf_passive_interface_update (ospf, ifp, addr, params, OSPF_IF_ACTIVE); - + ospf_passive_interface_update (ospf, ifp, params, OSPF_IF_ACTIVE); + /* XXX We should call ospf_if_set_multicast on exactly those * interfaces for which the passive property changed. It is too much * work to determine this set, so we do this for every interface. @@ -441,7 +488,7 @@ DEFUN (ospf_network_area, "OSPF area ID in IP address format\n" "OSPF area ID as a decimal value\n") { - struct ospf *ospf= vty->index; + struct ospf *ospf = vty->index; struct prefix_ipv4 p; struct in_addr area_id; int ret, format; @@ -490,7 +537,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 +681,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 +733,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 +818,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; @@ -839,7 +886,7 @@ static int ospf_vl_set_timers (struct ospf_vl_data *vl_data, struct ospf_vl_config_data *vl_config) { - struct interface *ifp = ifp = vl_data->vl_oi->ifp; + struct interface *ifp = vl_data->vl_oi->ifp; /* Virtual Link data initialised to defaults, so only set if a value given */ if (vl_config->hello_interval) @@ -1382,7 +1429,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 +1497,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 +1789,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 +1979,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 +1986,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 +2018,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 +2089,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 +2219,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 +2236,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 +2253,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, @@ -2222,6 +2268,83 @@ ospf_timers_spf_set (struct vty *vty, unsigned int delay, return CMD_SUCCESS; } +DEFUN (ospf_timers_min_ls_interval, + ospf_timers_min_ls_interval_cmd, + "timers throttle lsa all <0-5000>", + "Adjust routing timers\n" + "Throttling adaptive timer\n" + "LSA delay between transmissions\n" + NO_STR + "Delay (msec) between sending LSAs\n") +{ + struct ospf *ospf = vty->index; + unsigned int interval; + + if (argc != 1) + { + vty_out (vty, "Insufficient arguments%s", VTY_NEWLINE); + return CMD_WARNING; + } + + VTY_GET_INTEGER ("LSA interval", interval, argv[0]); + + ospf->min_ls_interval = interval; + + return CMD_SUCCESS; +} + +DEFUN (no_ospf_timers_min_ls_interval, + no_ospf_timers_min_ls_interval_cmd, + "no timers throttle lsa all", + NO_STR + "Adjust routing timers\n" + "Throttling adaptive timer\n" + "LSA delay between transmissions\n") +{ + struct ospf *ospf = vty->index; + ospf->min_ls_interval = OSPF_MIN_LS_INTERVAL; + + return CMD_SUCCESS; +} + +DEFUN (ospf_timers_min_ls_arrival, + ospf_timers_min_ls_arrival_cmd, + "timers lsa arrival <0-1000>", + "Adjust routing timers\n" + "Throttling link state advertisement delays\n" + "OSPF minimum arrival interval delay\n" + "Delay (msec) between accepted LSAs\n") +{ + struct ospf *ospf = vty->index; + unsigned int arrival; + + if (argc != 1) + { + vty_out (vty, "Insufficient arguments%s", VTY_NEWLINE); + return CMD_WARNING; + } + + VTY_GET_INTEGER_RANGE ("minimum LSA inter-arrival time", arrival, argv[0], 0, 1000); + + ospf->min_ls_arrival = arrival; + + return CMD_SUCCESS; +} + +DEFUN (no_ospf_timers_min_ls_arrival, + no_ospf_timers_min_ls_arrival_cmd, + "no timers lsa arrival", + NO_STR + "Adjust routing timers\n" + "Throttling link state advertisement delays\n" + "OSPF minimum arrival interval delay\n") +{ + struct ospf *ospf = vty->index; + ospf->min_ls_arrival = OSPF_MIN_LS_ARRIVAL; + + return CMD_SUCCESS; +} + DEFUN (ospf_timers_throttle_spf, ospf_timers_throttle_spf_cmd, "timers throttle spf <0-600000> <0-600000> <0-600000>", @@ -2298,7 +2421,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 +2445,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 +2517,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 +2554,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 +2672,7 @@ const char *ospf_shortcut_mode_descr_str[] = }; - + static void show_ip_ospf_area (struct vty *vty, struct ospf_area *area) { @@ -2664,14 +2786,12 @@ show_ip_ospf_area (struct vty *vty, struct ospf_area *area) vty_out (vty, " Number of NSSA LSA %ld. Checksum Sum 0x%08x%s", ospf_lsdb_count (area->lsdb, OSPF_AS_NSSA_LSA), ospf_lsdb_checksum (area->lsdb, OSPF_AS_NSSA_LSA), VTY_NEWLINE); -#ifdef HAVE_OPAQUE_LSA vty_out (vty, " Number of opaque link LSA %ld. Checksum Sum 0x%08x%s", ospf_lsdb_count (area->lsdb, OSPF_OPAQUE_LINK_LSA), ospf_lsdb_checksum (area->lsdb, OSPF_OPAQUE_LINK_LSA), VTY_NEWLINE); vty_out (vty, " Number of opaque area LSA %ld. Checksum Sum 0x%08x%s", ospf_lsdb_count (area->lsdb, OSPF_OPAQUE_AREA_LSA), ospf_lsdb_checksum (area->lsdb, OSPF_OPAQUE_AREA_LSA), VTY_NEWLINE); -#endif /* HAVE_OPAQUE_LSA */ vty_out (vty, "%s", VTY_NEWLINE); } @@ -2712,14 +2832,10 @@ DEFUN (show_ip_ospf, vty_out (vty, " RFC1583Compatibility flag is %s%s", CHECK_FLAG (ospf->config, OSPF_RFC1583_COMPATIBLE) ? "enabled" : "disabled", VTY_NEWLINE); -#ifdef HAVE_OPAQUE_LSA - vty_out (vty, " OpaqueCapability flag is %s%s%s", + vty_out (vty, " OpaqueCapability flag is %s%s", CHECK_FLAG (ospf->config, OSPF_OPAQUE_CAPABLE) ? "enabled" : "disabled", - IS_OPAQUE_LSA_ORIGINATION_BLOCKED (ospf->opaque) ? - " (origination blocked)" : "", VTY_NEWLINE); -#endif /* HAVE_OPAQUE_LSA */ /* Show stub-router configuration */ if (ospf->stub_router_startup_time != OSPF_STUB_ROUTER_UNCONFIGURED @@ -2751,6 +2867,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); @@ -2776,11 +2895,9 @@ DEFUN (show_ip_ospf, vty_out (vty, " Number of external LSA %ld. Checksum Sum 0x%08x%s", ospf_lsdb_count (ospf->lsdb, OSPF_AS_EXTERNAL_LSA), ospf_lsdb_checksum (ospf->lsdb, OSPF_AS_EXTERNAL_LSA), VTY_NEWLINE); -#ifdef HAVE_OPAQUE_LSA vty_out (vty, " Number of opaque AS LSA %ld. Checksum Sum 0x%08x%s", ospf_lsdb_count (ospf->lsdb, OSPF_OPAQUE_AS_LSA), ospf_lsdb_checksum (ospf->lsdb, OSPF_OPAQUE_AS_LSA), VTY_NEWLINE); -#endif /* HAVE_OPAQUE_LSA */ /* Show number of areas attached. */ vty_out (vty, " Number of areas attached to this router: %d%s", listcount (ospf->areas), VTY_NEWLINE); @@ -2802,7 +2919,7 @@ DEFUN (show_ip_ospf, return CMD_SUCCESS; } - + static void show_ip_ospf_interface_sub (struct vty *vty, struct ospf *ospf, struct interface *ifp) @@ -3000,7 +3117,7 @@ DEFUN (show_ip_ospf_interface, static void show_ip_ospf_neighbour_header (struct vty *vty) { - vty_out (vty, "%s%15s %3s %-15s %9s %-15s %-20s %5s %5s %5s%s", + vty_out (vty, "%s%-15s %3s %-15s %9s %-15s %-20s %5s %5s %5s%s", VTY_NEWLINE, "Neighbor ID", "Pri", "State", "Dead Time", "Address", "Interface", "RXmtL", "RqstL", "DBsmL", @@ -3431,7 +3548,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) @@ -3483,11 +3600,9 @@ show_lsa_summary (struct vty *vty, struct ospf_lsa *lsa, int self) break; case OSPF_NETWORK_LSA: case OSPF_ASBR_SUMMARY_LSA: -#ifdef HAVE_OPAQUE_LSA case OSPF_OPAQUE_LINK_LSA: case OSPF_OPAQUE_AREA_LSA: case OSPF_OPAQUE_AS_LSA: -#endif /* HAVE_OPAQUE_LSA */ default: break; } @@ -3507,12 +3622,10 @@ static const char *show_database_desc[] = "AS External Link States", "Group Membership LSA", "NSSA-external Link States", -#ifdef HAVE_OPAQUE_LSA "Type-8 LSA", "Link-Local Opaque-LSA", "Area-Local Opaque-LSA", "AS-external Opaque-LSA", -#endif /* HAVE_OPAQUE_LSA */ }; static const char *show_database_header[] = @@ -3525,12 +3638,10 @@ static const char *show_database_header[] = "Link ID ADV Router Age Seq# CkSum Route", " --- header for Group Member ----", "Link ID ADV Router Age Seq# CkSum Route", -#ifdef HAVE_OPAQUE_LSA " --- type-8 ---", "Opaque-Type/Id ADV Router Age Seq# CkSum", "Opaque-Type/Id ADV Router Age Seq# CkSum", "Opaque-Type/Id ADV Router Age Seq# CkSum", -#endif /* HAVE_OPAQUE_LSA */ }; static void @@ -3606,7 +3717,8 @@ static void show_ip_ospf_database_router_links (struct vty *vty, struct router_lsa *rl) { - int len, i, type; + int len, type; + unsigned int i; len = ntohs (rl->header.length) - 4; for (i = 0; i < ntohs (rl->links) && len > 0; len -= 12, i++) @@ -3760,8 +3872,8 @@ show_as_external_lsa_stdvty (struct ospf_lsa *lsa) zlog_debug( " Forward Address: %s%s", inet_ntoa (al->e[0].fwd_addr), "\n"); - zlog_debug( " External Route Tag: %u%s%s", - ntohl (al->e[0].route_tag), "\n", "\n"); + zlog_debug( " External Route Tag: %lu%s%s", + (u_long)ntohl (al->e[0].route_tag), "\n", "\n"); return 0; } @@ -3788,8 +3900,8 @@ show_as_nssa_lsa_detail (struct vty *vty, struct ospf_lsa *lsa) vty_out (vty, " NSSA: Forward Address: %s%s", inet_ntoa (al->e[0].fwd_addr), VTY_NEWLINE); - vty_out (vty, " External Route Tag: %u%s%s", - ntohl (al->e[0].route_tag), VTY_NEWLINE, VTY_NEWLINE); + vty_out (vty, " External Route Tag: %lu%s%s", + (u_long)ntohl (al->e[0].route_tag), VTY_NEWLINE, VTY_NEWLINE); } return 0; @@ -3801,7 +3913,6 @@ show_func_dummy (struct vty *vty, struct ospf_lsa *lsa) return 0; } -#ifdef HAVE_OPAQUE_LSA static int show_opaque_lsa_detail (struct vty *vty, struct ospf_lsa *lsa) { @@ -3814,7 +3925,6 @@ show_opaque_lsa_detail (struct vty *vty, struct ospf_lsa *lsa) } return 0; } -#endif /* HAVE_OPAQUE_LSA */ int (*show_function[])(struct vty *, struct ospf_lsa *) = { @@ -3826,12 +3936,10 @@ int (*show_function[])(struct vty *, struct ospf_lsa *) = show_as_external_lsa_detail, show_func_dummy, show_as_nssa_lsa_detail, /* almost same as external */ -#ifdef HAVE_OPAQUE_LSA NULL, /* type-8 */ show_opaque_lsa_detail, show_opaque_lsa_detail, show_opaque_lsa_detail, -#endif /* HAVE_OPAQUE_LSA */ }; static void @@ -3890,9 +3998,7 @@ show_lsa_detail (struct vty *vty, struct ospf *ospf, int type, switch (type) { case OSPF_AS_EXTERNAL_LSA: -#ifdef HAVE_OPAQUE_LSA case OSPF_OPAQUE_AS_LSA: -#endif /* HAVE_OPAQUE_LSA */ vty_out (vty, " %s %s%s", show_database_desc[type], VTY_NEWLINE, VTY_NEWLINE); @@ -3939,9 +4045,7 @@ show_lsa_detail_adv_router (struct vty *vty, struct ospf *ospf, int type, switch (type) { case OSPF_AS_EXTERNAL_LSA: -#ifdef HAVE_OPAQUE_LSA case OSPF_OPAQUE_AS_LSA: -#endif /* HAVE_OPAQUE_LSA */ vty_out (vty, " %s %s%s", show_database_desc[type], VTY_NEWLINE, VTY_NEWLINE); @@ -3977,9 +4081,7 @@ show_ip_ospf_database_summary (struct vty *vty, struct ospf *ospf, int self) switch (type) { case OSPF_AS_EXTERNAL_LSA: -#ifdef HAVE_OPAQUE_LSA case OSPF_OPAQUE_AS_LSA: -#endif /* HAVE_OPAQUE_LSA */ continue; default: break; @@ -4006,9 +4108,7 @@ show_ip_ospf_database_summary (struct vty *vty, struct ospf *ospf, int self) switch (type) { case OSPF_AS_EXTERNAL_LSA: -#ifdef HAVE_OPAQUE_LSA case OSPF_OPAQUE_AS_LSA: -#endif /* HAVE_OPAQUE_LSA */ break; default: continue; @@ -4035,38 +4135,35 @@ 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); + } } } #define OSPF_LSA_TYPE_NSSA_DESC "NSSA external link state\n" #define OSPF_LSA_TYPE_NSSA_CMD_STR "|nssa-external" -#ifdef HAVE_OPAQUE_LSA #define OSPF_LSA_TYPE_OPAQUE_LINK_DESC "Link local Opaque-LSA\n" #define OSPF_LSA_TYPE_OPAQUE_AREA_DESC "Link area Opaque-LSA\n" #define OSPF_LSA_TYPE_OPAQUE_AS_DESC "Link AS Opaque-LSA\n" #define OSPF_LSA_TYPE_OPAQUE_CMD_STR "|opaque-link|opaque-area|opaque-as" -#else /* HAVE_OPAQUE_LSA */ -#define OSPF_LSA_TYPE_OPAQUE_LINK_DESC "" -#define OSPF_LSA_TYPE_OPAQUE_AREA_DESC "" -#define OSPF_LSA_TYPE_OPAQUE_AS_DESC "" -#define OSPF_LSA_TYPE_OPAQUE_CMD_STR "" -#endif /* HAVE_OPAQUE_LSA */ #define OSPF_LSA_TYPES_CMD_STR \ "asbr-summary|external|network|router|summary" \ @@ -4136,14 +4233,12 @@ DEFUN (show_ip_ospf_database, show_ip_ospf_database_maxage (vty, ospf); return CMD_SUCCESS; } -#ifdef HAVE_OPAQUE_LSA else if (strncmp (argv[0], "opaque-l", 8) == 0) type = OSPF_OPAQUE_LINK_LSA; else if (strncmp (argv[0], "opaque-ar", 9) == 0) type = OSPF_OPAQUE_AREA_LSA; else if (strncmp (argv[0], "opaque-as", 9) == 0) type = OSPF_OPAQUE_AS_LSA; -#endif /* HAVE_OPAQUE_LSA */ else return CMD_WARNING; @@ -4263,14 +4358,12 @@ DEFUN (show_ip_ospf_database_type_adv_router, type = OSPF_ASBR_SUMMARY_LSA; else if (strncmp (argv[0], "e", 1) == 0) type = OSPF_AS_EXTERNAL_LSA; -#ifdef HAVE_OPAQUE_LSA else if (strncmp (argv[0], "opaque-l", 8) == 0) type = OSPF_OPAQUE_LINK_LSA; else if (strncmp (argv[0], "opaque-ar", 9) == 0) type = OSPF_OPAQUE_AREA_LSA; else if (strncmp (argv[0], "opaque-as", 9) == 0) type = OSPF_OPAQUE_AS_LSA; -#endif /* HAVE_OPAQUE_LSA */ else return CMD_WARNING; @@ -4299,7 +4392,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", @@ -5091,11 +5184,12 @@ ALIAS (ip_ospf_dead_interval_minimal, DEFUN (no_ip_ospf_dead_interval, no_ip_ospf_dead_interval_addr_cmd, - "no ip ospf dead-interval A.B.C.D", + "no ip ospf dead-interval <1-65535> A.B.C.D", NO_STR "IP Information\n" "OSPF interface commands\n" "Interval after which a neighbor is declared dead\n" + "Seconds\n" "Address of interface") { struct interface *ifp = vty->index; @@ -5108,9 +5202,9 @@ DEFUN (no_ip_ospf_dead_interval, ifp = vty->index; params = IF_DEF_PARAMS (ifp); - if (argc == 1) + if (argc == 2) { - ret = inet_aton(argv[0], &addr); + ret = inet_aton(argv[1], &addr); if (!ret) { vty_out (vty, "Please specify interface address by A.B.C.D%s", @@ -5157,6 +5251,15 @@ DEFUN (no_ip_ospf_dead_interval, return CMD_SUCCESS; } +ALIAS (no_ip_ospf_dead_interval, + no_ip_ospf_dead_interval_seconds_cmd, + "no ip ospf dead-interval <1-65535>", + NO_STR + "IP Information\n" + "OSPF interface commands\n" + "Interval after which a neighbor is declared dead\n" + "Seconds\n") + ALIAS (no_ip_ospf_dead_interval, no_ip_ospf_dead_interval_cmd, "no ip ospf dead-interval", @@ -5235,11 +5338,12 @@ ALIAS (ip_ospf_hello_interval, DEFUN (no_ip_ospf_hello_interval, no_ip_ospf_hello_interval_addr_cmd, - "no ip ospf hello-interval A.B.C.D", + "no ip ospf hello-interval <1-65535> A.B.C.D", NO_STR "IP Information\n" "OSPF interface commands\n" "Time between HELLO packets\n" + "Seconds\n" "Address of interface") { struct interface *ifp = vty->index; @@ -5250,9 +5354,9 @@ DEFUN (no_ip_ospf_hello_interval, ifp = vty->index; params = IF_DEF_PARAMS (ifp); - if (argc == 1) + if (argc == 2) { - ret = inet_aton(argv[0], &addr); + ret = inet_aton(argv[1], &addr); if (!ret) { vty_out (vty, "Please specify interface address by A.B.C.D%s", @@ -5277,6 +5381,15 @@ DEFUN (no_ip_ospf_hello_interval, return CMD_SUCCESS; } +ALIAS (no_ip_ospf_hello_interval, + no_ip_ospf_hello_interval_seconds_cmd, + "no ip ospf hello-interval <1-65535>", + NO_STR + "IP Information\n" + "OSPF interface commands\n" + "Time between HELLO packets\n" + "Seconds\n") + ALIAS (no_ip_ospf_hello_interval, no_ip_ospf_hello_interval_cmd, "no ip ospf hello-interval", @@ -5305,8 +5418,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) @@ -5320,23 +5438,7 @@ DEFUN (ip_ospf_network, return CMD_SUCCESS; SET_IF_PARAM (IF_DEF_PARAMS (ifp), type); - - for (rn = route_top (IF_OIFS (ifp)); rn; rn = route_next (rn)) - { - struct ospf_interface *oi = rn->info; - - if (!oi) - continue; - - oi->type = IF_DEF_PARAMS (ifp)->type; - - if (oi->state > ISM_Down) - { - OSPF_ISM_EVENT_EXECUTE (oi, ISM_InterfaceDown); - OSPF_ISM_EVENT_EXECUTE (oi, ISM_InterfaceUp); - } - } - + ospf_if_reset_type (ifp, IF_DEF_PARAMS (ifp)->type); return CMD_SUCCESS; } @@ -5360,29 +5462,14 @@ DEFUN (no_ip_ospf_network, { struct interface *ifp = vty->index; int old_type = IF_DEF_PARAMS (ifp)->type; - struct route_node *rn; IF_DEF_PARAMS (ifp)->type = ospf_default_iftype(ifp); if (IF_DEF_PARAMS (ifp)->type == old_type) return CMD_SUCCESS; - - for (rn = route_top (IF_OIFS (ifp)); rn; rn = route_next (rn)) - { - struct ospf_interface *oi = rn->info; - - if (!oi) - continue; - - oi->type = IF_DEF_PARAMS (ifp)->type; - - if (oi->state > ISM_Down) - { - OSPF_ISM_EVENT_EXECUTE (oi, ISM_InterfaceDown); - OSPF_ISM_EVENT_EXECUTE (oi, ISM_InterfaceUp); - } - } - + + ospf_if_reset_type (ifp, IF_DEF_PARAMS (ifp)->type); + return CMD_SUCCESS; } @@ -5403,7 +5490,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 +5871,74 @@ 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 (ip_ospf_area, + ip_ospf_area_cmd, + "ip ospf area (A.B.C.D|<0-4294967295>) [A.B.C.D]", + "IP Information\n" + "OSPF interface commands\n" + "Enable OSPF on this interface\n" + "OSPF area ID in IP address format\n" + "OSPF area ID as a decimal value\n" + "Address of interface\n") +{ + struct interface *ifp = vty->index; + struct in_addr area_id; + struct in_addr addr; + int format; + struct ospf_if_params *params; + + VTY_GET_OSPF_AREA_ID (area_id, format, argv[0]); + + OSPF_VTY_GET_IF_PARAMS(ifp, params, 1, addr, VTY_SET); + + if (OSPF_IF_PARAM_CONFIGURED(params, if_area)) + { + vty_out (vty, "There is already an interface area statement.%s", + VTY_NEWLINE); + return CMD_WARNING; + } + if (memcmp (ifp->name, "VLINK", 5) == 0) + { + vty_out (vty, "Cannot enable OSPF on a virtual link.%s", VTY_NEWLINE); + return CMD_WARNING; + } + + SET_IF_PARAM (params, if_area); + params->if_area = area_id; + ospf_interface_area_set (ifp); + + return CMD_SUCCESS; +} + +DEFUN (no_ip_ospf_area, + no_ip_ospf_area_cmd, + "no ip ospf area [A.B.C.D]", + NO_STR + "IP Information\n" + "OSPF interface commands\n" + "Disable OSPF on this interface\n" + "Address of interface\n") +{ + struct interface *ifp = vty->index; + struct ospf_if_params *params; + struct in_addr addr; + + OSPF_VTY_GET_IF_PARAMS(ifp, params, 0, addr, VTY_UNSET); + + if (!OSPF_IF_PARAM_CONFIGURED(params, if_area)) + return CMD_SUCCESS; + + OSPF_VTY_PARAM_UNSET(params, if_area, ifp, addr); + + ospf_interface_area_unset (ifp); + + return CMD_SUCCESS; +} + +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,225 +5954,48 @@ 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) + if (argv[3] != NULL) 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", +DEFUN (no_ospf_redistribute_source, + no_ospf_redistribute_source_cmd, + "no redistribute " QUAGGA_REDIST_STR_OSPFD, + NO_STR 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") + QUAGGA_REDIST_HELP_STR_OSPFD) { 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) - 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_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, - NO_STR - REDIST_STR - QUAGGA_REDIST_HELP_STR_OSPFD) -{ - struct ospf *ospf = vty->index; - int source; - - source = proto_redistnum(AFI_IP, argv[0]); - if (source < 0 || source == ZEBRA_ROUTE_OSPF) - return CMD_WARNING; - - ospf_routemap_unset (ospf, source); - return ospf_redistribute_unset (ospf, source); + ospf_routemap_unset (ospf, source); + return ospf_redistribute_unset (ospf, source); } DEFUN (ospf_distribute_list_out, @@ -6037,7 +6010,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; @@ -6052,388 +6025,27 @@ DEFUN (no_ospf_distribute_list_out, "Access-list name\n" OUT_STR QUAGGA_REDIST_HELP_STR_OSPFD) -{ - struct ospf *ospf = vty->index; - int source; - - source = proto_redistnum(AFI_IP, argv[0]); - if (source < 0 || source == ZEBRA_ROUTE_OSPF) - return CMD_WARNING; - - return ospf_distribute_list_out_unset (ospf, source, argv[0]); -} - -/* 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", - "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" - "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_ZEBRA, - 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)", - "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") - -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") - -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") - -/* 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") -{ - 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_ZEBRA, - -1, metric); -} - -/* 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") -{ - 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_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; +{ + struct ospf *ospf = vty->index; + int source; - if (argc == 3) - ospf_routemap_set (ospf, DEFAULT_ROUTE, argv[2]); - else - ospf_routemap_unset (ospf, DEFAULT_ROUTE); + source = proto_redistnum(AFI_IP, argv[1]); + if (source < 0 || source == ZEBRA_ROUTE_OSPF) + return CMD_WARNING; - return ospf_redistribute_default_set (ospf, DEFAULT_ORIGINATE_ALWAYS, - type, metric); + return ospf_distribute_list_out_unset (ospf, source, argv[0]); } -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>", +/* Default information originate. */ +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 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\n" "OSPF metric type for default routes\n" "Set OSPF External Type 1 metrics\n" "Set OSPF External Type 2 metrics\n" @@ -6441,20 +6053,34 @@ DEFUN (ospf_default_information_originate_always_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 (argv[1] != NULL) + if (!str2metric (argv[1], &metric)) + return CMD_WARNING; /* Get metric type. */ - if (argc >= 1) - if (!str2metric_type (argv[0], &type)) + if (argv[2] != NULL) + if (!str2metric_type (argv[2], &type)) return CMD_WARNING; - if (argc == 2) - ospf_routemap_set (ospf, DEFAULT_ROUTE, argv[1]); + 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_ALWAYS, - type, -1); + return ospf_redistribute_default_set (ospf, default_originate, + type, metric); } DEFUN (no_ospf_default_information_originate, @@ -6549,296 +6175,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" - "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" + "OSPF 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 +6404,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 +6423,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 +6454,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 +6573,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 +6792,7 @@ DEFUN (show_ip_ospf_route, return CMD_SUCCESS; } - + const char *ospf_abr_type_str[] = { "unknown", @@ -7413,7 +6821,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. */ @@ -7591,6 +6999,15 @@ config_write_interface (struct vty *vty) vty_out (vty, "%s", VTY_NEWLINE); } + /* Area print. */ + if (OSPF_IF_PARAM_CONFIGURED (params, if_area)) + { + vty_out (vty, " ip ospf area %s", inet_ntoa (params->if_area)); + if (params != IF_DEF_PARAMS (ifp)) + vty_out (vty, " %s", inet_ntoa (rn->p.u.prefix4)); + vty_out (vty, "%s", VTY_NEWLINE); + } + /* MTU ignore print. */ if (OSPF_IF_PARAM_CONFIGURED (params, mtu_ignore) && params->mtu_ignore != OSPF_MTU_IGNORE_DEFAULT) @@ -7620,9 +7037,7 @@ config_write_interface (struct vty *vty) } } while (rn); -#ifdef HAVE_OPAQUE_LSA ospf_opaque_config_write_if (vty, ifp); -#endif /* HAVE_OPAQUE_LSA */ } return write; @@ -7847,7 +7262,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) { @@ -7855,7 +7270,8 @@ config_write_ospf_redistribute (struct vty *vty, struct ospf *ospf) /* redistribute print. */ for (type = 0; type < ZEBRA_ROUTE_MAX; type++) - if (type != zclient->redist_default && zclient->redist[type]) + if (type != zclient->redist_default && + vrf_bitmap_check (zclient->redist[type], VRF_DEFAULT)) { vty_out (vty, " redistribute %s", zebra_route_string(type)); if (ospf->dmetric[type].value >= 0) @@ -8010,6 +7426,14 @@ ospf_config_write (struct vty *vty) ospf->ref_bandwidth / 1000, VTY_NEWLINE); } + /* LSA timers */ + if (ospf->min_ls_interval != OSPF_MIN_LS_INTERVAL) + vty_out (vty, " timers throttle lsa all %d%s", + ospf->min_ls_interval, VTY_NEWLINE); + if (ospf->min_ls_arrival != OSPF_MIN_LS_ARRIVAL) + vty_out (vty, " timers lsa arrival %d%s", + ospf->min_ls_arrival, VTY_NEWLINE); + /* SPF timers print. */ if (ospf->spf_delay != OSPF_SPF_DELAY_DEFAULT || ospf->spf_holdtime != OSPF_SPF_HOLDTIME_DEFAULT || @@ -8082,9 +7506,7 @@ ospf_config_write (struct vty *vty) /* Distance configuration. */ config_write_ospf_distance (vty, ospf); -#ifdef HAVE_OPAQUE_LSA ospf_opaque_config_write_router (vty, ospf); -#endif /* HAVE_OPAQUE_LSA */ } return write; @@ -8095,7 +7517,6 @@ ospf_vty_show_init (void) { /* "show ip ospf" commands. */ install_element (VIEW_NODE, &show_ip_ospf_cmd); - install_element (ENABLE_NODE, &show_ip_ospf_cmd); /* "show ip ospf database" commands. */ install_element (VIEW_NODE, &show_ip_ospf_database_type_cmd); @@ -8105,17 +7526,9 @@ ospf_vty_show_init (void) install_element (VIEW_NODE, &show_ip_ospf_database_type_id_self_cmd); install_element (VIEW_NODE, &show_ip_ospf_database_type_self_cmd); install_element (VIEW_NODE, &show_ip_ospf_database_cmd); - install_element (ENABLE_NODE, &show_ip_ospf_database_type_cmd); - install_element (ENABLE_NODE, &show_ip_ospf_database_type_id_cmd); - install_element (ENABLE_NODE, &show_ip_ospf_database_type_id_adv_router_cmd); - install_element (ENABLE_NODE, &show_ip_ospf_database_type_adv_router_cmd); - install_element (ENABLE_NODE, &show_ip_ospf_database_type_id_self_cmd); - install_element (ENABLE_NODE, &show_ip_ospf_database_type_self_cmd); - install_element (ENABLE_NODE, &show_ip_ospf_database_cmd); /* "show ip ospf interface" commands. */ install_element (VIEW_NODE, &show_ip_ospf_interface_cmd); - install_element (ENABLE_NODE, &show_ip_ospf_interface_cmd); /* "show ip ospf neighbor" commands. */ install_element (VIEW_NODE, &show_ip_ospf_neighbor_int_detail_cmd); @@ -8125,22 +7538,13 @@ ospf_vty_show_init (void) install_element (VIEW_NODE, &show_ip_ospf_neighbor_detail_cmd); install_element (VIEW_NODE, &show_ip_ospf_neighbor_cmd); install_element (VIEW_NODE, &show_ip_ospf_neighbor_all_cmd); - install_element (ENABLE_NODE, &show_ip_ospf_neighbor_int_detail_cmd); - install_element (ENABLE_NODE, &show_ip_ospf_neighbor_int_cmd); - install_element (ENABLE_NODE, &show_ip_ospf_neighbor_id_cmd); - install_element (ENABLE_NODE, &show_ip_ospf_neighbor_detail_all_cmd); - install_element (ENABLE_NODE, &show_ip_ospf_neighbor_detail_cmd); - install_element (ENABLE_NODE, &show_ip_ospf_neighbor_cmd); - install_element (ENABLE_NODE, &show_ip_ospf_neighbor_all_cmd); /* "show ip ospf route" commands. */ install_element (VIEW_NODE, &show_ip_ospf_route_cmd); - install_element (ENABLE_NODE, &show_ip_ospf_route_cmd); install_element (VIEW_NODE, &show_ip_ospf_border_routers_cmd); - install_element (ENABLE_NODE, &show_ip_ospf_border_routers_cmd); } - + /* ospfd's interface node. */ static struct cmd_node interface_node = { @@ -8203,12 +7607,14 @@ ospf_vty_if_init (void) install_element (INTERFACE_NODE, &ip_ospf_dead_interval_minimal_cmd); install_element (INTERFACE_NODE, &no_ip_ospf_dead_interval_addr_cmd); install_element (INTERFACE_NODE, &no_ip_ospf_dead_interval_cmd); + install_element (INTERFACE_NODE, &no_ip_ospf_dead_interval_seconds_cmd); /* "ip ospf hello-interval" commands. */ install_element (INTERFACE_NODE, &ip_ospf_hello_interval_addr_cmd); install_element (INTERFACE_NODE, &ip_ospf_hello_interval_cmd); install_element (INTERFACE_NODE, &no_ip_ospf_hello_interval_addr_cmd); install_element (INTERFACE_NODE, &no_ip_ospf_hello_interval_cmd); + install_element (INTERFACE_NODE, &no_ip_ospf_hello_interval_seconds_cmd); /* "ip ospf network" commands. */ install_element (INTERFACE_NODE, &ip_ospf_network_cmd); @@ -8232,6 +7638,10 @@ ospf_vty_if_init (void) install_element (INTERFACE_NODE, &no_ip_ospf_transmit_delay_addr_cmd); install_element (INTERFACE_NODE, &no_ip_ospf_transmit_delay_cmd); + /* "ip ospf area" commands. */ + install_element (INTERFACE_NODE, &ip_ospf_area_cmd); + install_element (INTERFACE_NODE, &no_ip_ospf_area_cmd); + /* These commands are compatibitliy for previous version. */ install_element (INTERFACE_NODE, &ospf_authentication_key_cmd); install_element (INTERFACE_NODE, &no_ospf_authentication_key_cmd); @@ -8260,63 +7670,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 +7686,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 +7702,52 @@ static struct cmd_node ospf_node = 1 }; - +static void +ospf_interface_clear (struct interface *ifp) +{ + if (!if_is_operative (ifp)) return; + + if (IS_DEBUG_OSPF (ism, ISM_EVENTS)) + zlog (NULL, LOG_DEBUG, "ISM[%s]: clear by reset", ifp->name); + + ospf_if_reset(ifp); +} + +DEFUN (clear_ip_ospf_interface, + clear_ip_ospf_interface_cmd, + "clear ip ospf interface [IFNAME]", + CLEAR_STR + IP_STR + "OSPF information\n" + "Interface information\n" + "Interface name\n") +{ + struct interface *ifp; + struct listnode *node; + + if (argc == 0) /* Clear all the ospfv2 interfaces. */ + { + for (ALL_LIST_ELEMENTS_RO (iflist, node, ifp)) + ospf_interface_clear(ifp); + } + else /* Interface name is specified. */ + { + if ((ifp = if_lookup_by_name (argv[0])) == NULL) + vty_out (vty, "No such interface name%s", VTY_NEWLINE); + else + ospf_interface_clear(ifp); + } + + return CMD_SUCCESS; +} + +void +ospf_vty_clear_init (void) +{ + install_element (ENABLE_NODE, &clear_ip_ospf_interface_cmd); +} + + /* Install OSPF related vty commands. */ void ospf_vty_init (void) @@ -8485,6 +7876,12 @@ ospf_vty_init (void) install_element (OSPF_NODE, &ospf_area_import_list_cmd); install_element (OSPF_NODE, &no_ospf_area_import_list_cmd); + /* LSA timer commands */ + install_element (OSPF_NODE, &ospf_timers_min_ls_interval_cmd); + install_element (OSPF_NODE, &no_ospf_timers_min_ls_interval_cmd); + install_element (OSPF_NODE, &ospf_timers_min_ls_arrival_cmd); + install_element (OSPF_NODE, &no_ospf_timers_min_ls_arrival_cmd); + /* SPF timer commands */ install_element (OSPF_NODE, &ospf_timers_spf_cmd); install_element (OSPF_NODE, &no_ospf_timers_spf_cmd); diff --git a/ospfd/ospf_vty.h b/ospfd/ospf_vty.h index da0ed1cc1..4610638d5 100644 --- a/ospfd/ospf_vty.h +++ b/ospfd/ospf_vty.h @@ -53,5 +53,7 @@ /* Prototypes. */ extern void ospf_vty_init (void); extern void ospf_vty_show_init (void); +extern int ospf_str2area_id (const char *, struct in_addr *, int *); +extern void ospf_vty_clear_init (void); #endif /* _QUAGGA_OSPF_VTY_H */ diff --git a/ospfd/ospf_zebra.c b/ospfd/ospf_zebra.c index f8d1cb7c6..60d98529d 100644 --- a/ospfd/ospf_zebra.c +++ b/ospfd/ospf_zebra.c @@ -48,6 +48,7 @@ #ifdef HAVE_SNMP #include "ospfd/ospf_snmp.h" #endif /* HAVE_SNMP */ +#include "ospfd/ospf_te.h" /* Zebra structure to hold current status. */ struct zclient *zclient = NULL; @@ -59,7 +60,7 @@ struct in_addr router_id_zebra; /* Router-id update message from zebra. */ static int ospf_router_id_update_zebra (int command, struct zclient *zclient, - zebra_size_t length) + zebra_size_t length, vrf_id_t vrf_id) { struct ospf *ospf; struct prefix router_id; @@ -84,11 +85,12 @@ ospf_router_id_update_zebra (int command, struct zclient *zclient, /* Inteface addition message from zebra. */ static int -ospf_interface_add (int command, struct zclient *zclient, zebra_size_t length) +ospf_interface_add (int command, struct zclient *zclient, zebra_size_t length, + vrf_id_t vrf_id) { struct interface *ifp; - ifp = zebra_interface_add_read (zclient->ibuf); + ifp = zebra_interface_add_read (zclient->ibuf, vrf_id); if (IS_DEBUG_OSPF (zebra, ZEBRA_INTERFACE)) zlog_debug ("Zebra: interface add %s index %d flags %llx metric %d mtu %d", @@ -114,7 +116,7 @@ ospf_interface_add (int command, struct zclient *zclient, zebra_size_t length) static int ospf_interface_delete (int command, struct zclient *zclient, - zebra_size_t length) + zebra_size_t length, vrf_id_t vrf_id) { struct interface *ifp; struct stream *s; @@ -122,7 +124,7 @@ ospf_interface_delete (int command, struct zclient *zclient, s = zclient->ibuf; /* zebra_interface_state_read() updates interface structure in iflist */ - ifp = zebra_interface_state_read (s); + ifp = zebra_interface_state_read (s, vrf_id); if (ifp == NULL) return 0; @@ -133,8 +135,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); @@ -149,7 +151,7 @@ ospf_interface_delete (int command, struct zclient *zclient, } static struct interface * -zebra_interface_if_lookup (struct stream *s) +zebra_interface_if_lookup (struct stream *s, vrf_id_t vrf_id) { char ifname_tmp[INTERFACE_NAMSIZ]; @@ -163,13 +165,13 @@ zebra_interface_if_lookup (struct stream *s) static int ospf_interface_state_up (int command, struct zclient *zclient, - zebra_size_t length) + zebra_size_t length, vrf_id_t vrf_id) { struct interface *ifp; struct ospf_interface *oi; struct route_node *rn; - ifp = zebra_interface_if_lookup (zclient->ibuf); + ifp = zebra_interface_if_lookup (zclient->ibuf, vrf_id); if (ifp == NULL) return 0; @@ -225,13 +227,13 @@ ospf_interface_state_up (int command, struct zclient *zclient, static int ospf_interface_state_down (int command, struct zclient *zclient, - zebra_size_t length) + zebra_size_t length, vrf_id_t vrf_id) { struct interface *ifp; struct ospf_interface *oi; struct route_node *node; - ifp = zebra_interface_state_read (zclient->ibuf); + ifp = zebra_interface_state_read (zclient->ibuf, vrf_id); if (ifp == NULL) return 0; @@ -251,11 +253,11 @@ ospf_interface_state_down (int command, struct zclient *zclient, static int ospf_interface_address_add (int command, struct zclient *zclient, - zebra_size_t length) + zebra_size_t length, vrf_id_t vrf_id) { struct connected *c; - c = zebra_interface_address_read (command, zclient->ibuf); + c = zebra_interface_address_read (command, zclient->ibuf, vrf_id); if (c == NULL) return 0; @@ -278,7 +280,7 @@ ospf_interface_address_add (int command, struct zclient *zclient, static int ospf_interface_address_delete (int command, struct zclient *zclient, - zebra_size_t length) + zebra_size_t length, vrf_id_t vrf_id) { struct connected *c; struct interface *ifp; @@ -286,7 +288,7 @@ ospf_interface_address_delete (int command, struct zclient *zclient, struct route_node *rn; struct prefix p; - c = zebra_interface_address_read (command, zclient->ibuf); + c = zebra_interface_address_read (command, zclient->ibuf, vrf_id); if (c == NULL) return 0; @@ -311,6 +313,7 @@ ospf_interface_address_delete (int command, struct zclient *zclient, assert (rn->info); oi = rn->info; + route_unlock_node (rn); /* Call interface hook functions to clean up */ ospf_if_free (oi); @@ -324,6 +327,24 @@ ospf_interface_address_delete (int command, struct zclient *zclient, return 0; } +static int +ospf_interface_link_params (int command, struct zclient *zclient, + zebra_size_t length) +{ + struct interface *ifp; + + ifp = zebra_interface_link_params_read (zclient->ibuf); + + if (ifp == NULL) + return 0; + + /* Update TE TLV */ + ospf_mpls_te_update_if (ifp); + + return 0; +} + + void ospf_zebra_add (struct prefix_ipv4 *p, struct ospf_route *or) { @@ -335,7 +356,7 @@ ospf_zebra_add (struct prefix_ipv4 *p, struct ospf_route *or) struct ospf_path *path; struct listnode *node; - if (zclient->redist[ZEBRA_ROUTE_OSPF]) + if (vrf_bitmap_check (zclient->redist[ZEBRA_ROUTE_OSPF], VRF_DEFAULT)) { message = 0; flags = 0; @@ -349,12 +370,18 @@ ospf_zebra_add (struct prefix_ipv4 *p, struct ospf_route *or) if (distance) SET_FLAG (message, ZAPI_MESSAGE_DISTANCE); + /* Check if path type is ASE */ + if (((or->path_type == OSPF_PATH_TYPE1_EXTERNAL) || + (or->path_type == OSPF_PATH_TYPE2_EXTERNAL)) && + (or->u.ext.tag > 0) && (or->u.ext.tag <= ROUTE_TAG_MAX)) + SET_FLAG (message, ZAPI_MESSAGE_TAG); + /* Make packet. */ s = zclient->obuf; stream_reset (s); /* Put command, type, flags, message. */ - zclient_create_header (s, ZEBRA_IPV4_ROUTE_ADD); + zclient_create_header (s, ZEBRA_IPV4_ROUTE_ADD, VRF_DEFAULT); stream_putc (s, ZEBRA_ROUTE_OSPF); stream_putc (s, flags); stream_putc (s, message); @@ -371,7 +398,14 @@ ospf_zebra_add (struct prefix_ipv4 *p, struct ospf_route *or) /* Nexthop, ifindex, distance and metric information. */ for (ALL_LIST_ELEMENTS_RO (or->paths, node, path)) { - if (path->nexthop.s_addr != INADDR_ANY) + if (path->nexthop.s_addr != INADDR_ANY && + path->ifindex != 0) + { + stream_putc (s, ZEBRA_NEXTHOP_IPV4_IFINDEX); + stream_put_in_addr (s, &path->nexthop); + stream_putl (s, path->ifindex); + } + else if (path->nexthop.s_addr != INADDR_ANY) { stream_putc (s, ZEBRA_NEXTHOP_IPV4); stream_put_in_addr (s, &path->nexthop); @@ -409,6 +443,9 @@ ospf_zebra_add (struct prefix_ipv4 *p, struct ospf_route *or) stream_putl (s, or->cost); } + if (CHECK_FLAG (message, ZAPI_MESSAGE_TAG)) + stream_putl (s, or->u.ext.tag); + stream_putw_at (s, 0, stream_get_endp (s)); zclient_send_message(zclient); @@ -418,60 +455,87 @@ ospf_zebra_add (struct prefix_ipv4 *p, struct ospf_route *or) void ospf_zebra_delete (struct prefix_ipv4 *p, struct ospf_route *or) { - struct zapi_ipv4 api; + u_char message; + u_char distance; + u_char flags; + int psize; + struct stream *s; struct ospf_path *path; - struct in_addr *nexthop; - struct listnode *node, *nnode; + struct listnode *node; - if (zclient->redist[ZEBRA_ROUTE_OSPF]) + if (vrf_bitmap_check (zclient->redist[ZEBRA_ROUTE_OSPF], VRF_DEFAULT)) { - api.type = ZEBRA_ROUTE_OSPF; - api.flags = 0; - api.message = 0; - api.safi = SAFI_UNICAST; - api.ifindex_num = 0; - api.nexthop_num = 0; + message = 0; + flags = 0; + /* Distance value. */ + distance = ospf_distance_apply (p, or); + /* Make packet. */ + s = zclient->obuf; + stream_reset (s); - for (ALL_LIST_ELEMENTS (or->paths, node, nnode, path)) - { - if (path->nexthop.s_addr != INADDR_ANY) - { - SET_FLAG (api.message, ZAPI_MESSAGE_NEXTHOP); - api.nexthop_num = 1; - nexthop = &path->nexthop; - api.nexthop = &nexthop; - } - else if (if_lookup_by_index(path->ifindex)) - { - SET_FLAG (api.message, ZAPI_MESSAGE_NEXTHOP); - api.ifindex_num = 1; - api.ifindex = &path->ifindex; - } - else if ( IS_DEBUG_OSPF(zebra,ZEBRA_REDISTRIBUTE) ) - { - zlog_debug("Zebra: no ifp %s %d", - inet_ntoa(p->prefix), - p->prefixlen); - } + /* Put command, type, flags, message. */ + zclient_create_header (s, ZEBRA_IPV4_ROUTE_DELETE, VRF_DEFAULT); + stream_putc (s, ZEBRA_ROUTE_OSPF); + stream_putc (s, flags); + stream_putc (s, message); + stream_putw (s, SAFI_UNICAST); - zapi_ipv4_route (ZEBRA_IPV4_ROUTE_DELETE, zclient, p, &api); + /* Put prefix information. */ + psize = PSIZE (p->prefixlen); + stream_putc (s, p->prefixlen); + stream_write (s, (u_char *) & p->prefix, psize); - if (IS_DEBUG_OSPF (zebra, ZEBRA_REDISTRIBUTE) && api.nexthop_num) - { + /* Nexthop count. */ + stream_putc (s, or->paths->count); + + /* Nexthop, ifindex, distance and metric information. */ + for (ALL_LIST_ELEMENTS_RO (or->paths, node, path)) + { + if (path->nexthop.s_addr != INADDR_ANY && + path->ifindex != 0) + { + stream_putc (s, ZEBRA_NEXTHOP_IPV4_IFINDEX); + stream_put_in_addr (s, &path->nexthop); + stream_putl (s, path->ifindex); + } + else if (path->nexthop.s_addr != INADDR_ANY) + { + stream_putc (s, ZEBRA_NEXTHOP_IPV4); + stream_put_in_addr (s, &path->nexthop); + } + else + { + stream_putc (s, ZEBRA_NEXTHOP_IFINDEX); + stream_putl (s, path->ifindex); + } + + if (IS_DEBUG_OSPF (zebra, ZEBRA_REDISTRIBUTE)) + { char buf[2][INET_ADDRSTRLEN]; zlog_debug("Zebra: Route delete %s/%d nexthop %s", - inet_ntop(AF_INET, &p->prefix, buf[0], sizeof(buf[0])), + inet_ntop(AF_INET, &p->prefix, + buf[0], sizeof(buf[0])), p->prefixlen, - inet_ntop(AF_INET, *api.nexthop, + inet_ntop(AF_INET, &path->nexthop, buf[1], sizeof(buf[1]))); - } - if (IS_DEBUG_OSPF (zebra, ZEBRA_REDISTRIBUTE) && api.ifindex_num) - { - zlog_debug ("Zebra: Route delete %s/%d ifindex %d", - inet_ntoa (p->prefix), - p->prefixlen, *api.ifindex); - } - } + } + } + + if (CHECK_FLAG (message, ZAPI_MESSAGE_DISTANCE)) + stream_putc (s, distance); + if (CHECK_FLAG (message, ZAPI_MESSAGE_METRIC)) + { + if (or->path_type == OSPF_PATH_TYPE1_EXTERNAL) + stream_putl (s, or->cost + or->u.ext.type2_cost); + else if (or->path_type == OSPF_PATH_TYPE2_EXTERNAL) + stream_putl (s, or->u.ext.type2_cost); + else + stream_putl (s, or->cost); + } + + stream_putw_at (s, 0, stream_get_endp (s)); + + zclient_send_message(zclient); } } @@ -480,8 +544,9 @@ ospf_zebra_add_discard (struct prefix_ipv4 *p) { struct zapi_ipv4 api; - if (zclient->redist[ZEBRA_ROUTE_OSPF]) + if (vrf_bitmap_check (zclient->redist[ZEBRA_ROUTE_OSPF], VRF_DEFAULT)) { + api.vrf_id = VRF_DEFAULT; api.type = ZEBRA_ROUTE_OSPF; api.flags = ZEBRA_FLAG_BLACKHOLE; api.message = 0; @@ -489,6 +554,7 @@ ospf_zebra_add_discard (struct prefix_ipv4 *p) SET_FLAG (api.message, ZAPI_MESSAGE_NEXTHOP); api.nexthop_num = 0; api.ifindex_num = 0; + api.tag = 0; zapi_ipv4_route (ZEBRA_IPV4_ROUTE_ADD, zclient, p, &api); @@ -503,8 +569,9 @@ ospf_zebra_delete_discard (struct prefix_ipv4 *p) { struct zapi_ipv4 api; - if (zclient->redist[ZEBRA_ROUTE_OSPF]) + if (vrf_bitmap_check (zclient->redist[ZEBRA_ROUTE_OSPF], VRF_DEFAULT)) { + api.vrf_id = VRF_DEFAULT; api.type = ZEBRA_ROUTE_OSPF; api.flags = ZEBRA_FLAG_BLACKHOLE; api.message = 0; @@ -512,6 +579,7 @@ ospf_zebra_delete_discard (struct prefix_ipv4 *p) SET_FLAG (api.message, ZAPI_MESSAGE_NEXTHOP); api.nexthop_num = 0; api.ifindex_num = 0; + api.tag = 0; zapi_ipv4_route (ZEBRA_IPV4_ROUTE_DELETE, zclient, p, &api); @@ -526,7 +594,8 @@ int ospf_is_type_redistributed (int type) { return (DEFAULT_ROUTE_TYPE (type)) ? - zclient->default_information : zclient->redist[type]; + vrf_bitmap_check (zclient->default_information, VRF_DEFAULT) : \ + vrf_bitmap_check (zclient->redist[type], VRF_DEFAULT); } int @@ -560,7 +629,7 @@ ospf_redistribute_set (struct ospf *ospf, int type, int mtype, int mvalue) ospf->dmetric[type].type = mtype; ospf->dmetric[type].value = mvalue; - zclient_redistribute (ZEBRA_REDISTRIBUTE_ADD, zclient, type); + zclient_redistribute (ZEBRA_REDISTRIBUTE_ADD, zclient, type, VRF_DEFAULT); if (IS_DEBUG_OSPF (zebra, ZEBRA_REDISTRIBUTE)) zlog_debug ("Redistribute[%s]: Start Type[%d], Metric[%d]", @@ -581,7 +650,7 @@ ospf_redistribute_unset (struct ospf *ospf, int type) if (!ospf_is_type_redistributed (type)) return CMD_SUCCESS; - zclient_redistribute (ZEBRA_REDISTRIBUTE_DELETE, zclient, type); + zclient_redistribute (ZEBRA_REDISTRIBUTE_DELETE, zclient, type, VRF_DEFAULT); if (IS_DEBUG_OSPF (zebra, ZEBRA_REDISTRIBUTE)) zlog_debug ("Redistribute[%s]: Stop", @@ -621,7 +690,8 @@ ospf_redistribute_default_set (struct ospf *ospf, int originate, return CMD_SUCCESS; } - zclient_redistribute_default (ZEBRA_REDISTRIBUTE_DEFAULT_ADD, zclient); + zclient_redistribute_default (ZEBRA_REDISTRIBUTE_DEFAULT_ADD, zclient, + VRF_DEFAULT); if (IS_DEBUG_OSPF (zebra, ZEBRA_REDISTRIBUTE)) zlog_debug ("Redistribute[DEFAULT]: Start Type[%d], Metric[%d]", @@ -648,7 +718,8 @@ ospf_redistribute_default_unset (struct ospf *ospf) ospf->dmetric[DEFAULT_ROUTE].type = -1; ospf->dmetric[DEFAULT_ROUTE].value = -1; - zclient_redistribute_default (ZEBRA_REDISTRIBUTE_DEFAULT_DELETE, zclient); + zclient_redistribute_default (ZEBRA_REDISTRIBUTE_DEFAULT_DELETE, zclient, + VRF_DEFAULT); if (IS_DEBUG_OSPF (zebra, ZEBRA_REDISTRIBUTE)) zlog_debug ("Redistribute[DEFAULT]: Stop"); @@ -782,7 +853,7 @@ ospf_routemap_unset (struct ospf *ospf, int type) /* Zebra route add and delete treatment. */ static int ospf_zebra_read_ipv4 (int command, struct zclient *zclient, - zebra_size_t length) + zebra_size_t length, vrf_id_t vrf_id) { struct stream *s; struct zapi_ipv4 api; @@ -791,6 +862,7 @@ ospf_zebra_read_ipv4 (int command, struct zclient *zclient, struct prefix_ipv4 p; struct external_info *ei; struct ospf *ospf; + unsigned char plength = 0; s = zclient->ibuf; ifindex = 0; @@ -804,7 +876,8 @@ ospf_zebra_read_ipv4 (int command, struct zclient *zclient, /* IPv4 prefix. */ memset (&p, 0, sizeof (struct prefix_ipv4)); p.family = AF_INET; - p.prefixlen = stream_getc (s); + plength = stream_getc (s); + p.prefixlen = MIN(IPV4_MAX_PREFIXLEN, plength); stream_get (&p.prefix, s, PSIZE (p.prefixlen)); if (IPV4_NET127(ntohl(p.prefix.s_addr))) @@ -826,6 +899,10 @@ ospf_zebra_read_ipv4 (int command, struct zclient *zclient, api.distance = stream_getc (s); if (CHECK_FLAG (api.message, ZAPI_MESSAGE_METRIC)) api.metric = stream_getl (s); + if (CHECK_FLAG (api.message, ZAPI_MESSAGE_TAG)) + api.tag = stream_getl (s); + else + api.tag = 0; ospf = ospf_lookup (); if (ospf == NULL) @@ -843,8 +920,12 @@ ospf_zebra_read_ipv4 (int command, struct zclient *zclient, * || CHECK_FLAG (api.flags, ZEBRA_FLAG_REJECT)) * return 0; */ - - ei = ospf_external_info_add (api.type, p, ifindex, nexthop); + + /* Protocol tag overwrites all other tag value send by zebra */ + if (ospf->dtag[api.type] > 0) + api.tag = ospf->dtag[api.type]; + + ei = ospf_external_info_add (api.type, p, ifindex, nexthop, api.tag); if (ospf->router_id.s_addr == 0) /* Set flags to generate AS-external-LSA originate event @@ -884,7 +965,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) @@ -966,11 +1047,9 @@ ospf_distribute_list_update_timer (struct thread *thread) return 0; } -#define OSPF_DISTRIBUTE_UPDATE_DELAY 5 - /* 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; @@ -984,8 +1063,8 @@ ospf_distribute_list_update (struct ospf *ospf, int type) /* Set timer. */ ospf->t_distribute_update = - thread_add_timer (master, ospf_distribute_list_update_timer, - (void *) type, OSPF_DISTRIBUTE_UPDATE_DELAY); + thread_add_timer_msec (master, ospf_distribute_list_update_timer, + (void *) type, ospf->min_ls_interval); } /* If access-list is updated, apply some check. */ @@ -1092,7 +1171,7 @@ ospf_prefix_list_update (struct prefix_list *plist) { /* Update filter-list in. */ if (PREFIX_NAME_IN (area)) - if (strcmp (PREFIX_NAME_IN (area), plist->name) == 0) + if (strcmp (PREFIX_NAME_IN (area), prefix_list_name (plist)) == 0) { PREFIX_LIST_IN (area) = prefix_list_lookup (AFI_IP, PREFIX_NAME_IN (area)); @@ -1101,7 +1180,7 @@ ospf_prefix_list_update (struct prefix_list *plist) /* Update filter-list out. */ if (PREFIX_NAME_OUT (area)) - if (strcmp (PREFIX_NAME_OUT (area), plist->name) == 0) + if (strcmp (PREFIX_NAME_OUT (area), prefix_list_name (plist)) == 0) { PREFIX_LIST_IN (area) = prefix_list_lookup (AFI_IP, PREFIX_NAME_OUT (area)); @@ -1183,7 +1262,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; @@ -1194,8 +1272,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) { @@ -1261,12 +1337,19 @@ ospf_distance_apply (struct prefix_ipv4 *p, struct ospf_route *or) return 0; } +static void +ospf_zebra_connected (struct zclient *zclient) +{ + zclient_send_requests (zclient, VRF_DEFAULT); +} + void -ospf_zebra_init () +ospf_zebra_init (struct thread_master *master) { /* Allocate zebra structure. */ - zclient = zclient_new (); + zclient = zclient_new (master); zclient_init (zclient, ZEBRA_ROUTE_OSPF); + zclient->zebra_connected = ospf_zebra_connected; zclient->router_id_update = ospf_router_id_update_zebra; zclient->interface_add = ospf_interface_add; zclient->interface_delete = ospf_interface_delete; @@ -1274,6 +1357,8 @@ ospf_zebra_init () zclient->interface_down = ospf_interface_state_down; zclient->interface_address_add = ospf_interface_address_add; zclient->interface_address_delete = ospf_interface_address_delete; + zclient->interface_link_params = ospf_interface_link_params; + zclient->ipv4_route_add = ospf_zebra_read_ipv4; zclient->ipv4_route_delete = ospf_zebra_read_ipv4; diff --git a/ospfd/ospf_zebra.h b/ospfd/ospf_zebra.h index fbb344424..32a027161 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 *); @@ -72,7 +72,7 @@ extern int ospf_distance_set (struct vty *, struct ospf *, const char *, const char *, const char *); extern int ospf_distance_unset (struct vty *, struct ospf *, const char *, const char *, const char *); -extern void ospf_zebra_init (void); +extern void ospf_zebra_init (struct thread_master *); #endif /* _ZEBRA_OSPF_ZEBRA_H */ diff --git a/ospfd/ospfd.c b/ospfd/ospfd.c index e84051367..98d6d77a5 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,19 +64,21 @@ 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 *); static void ospf_network_run (struct prefix *, struct ospf_area *); -static void ospf_network_run_interface (struct prefix *, struct ospf_area *, - struct interface *); +static void ospf_network_run_interface (struct ospf *, struct interface *, + struct prefix *, struct ospf_area *); +static void ospf_network_run_subnet (struct ospf *, struct connected *, + struct prefix *, struct ospf_area *); static int ospf_network_match_iface (const struct connected *, const struct prefix *); static void ospf_finish_final (struct ospf *); #define OSPF_EXTERNAL_LSA_ORIGINATE_DELAY 1 - + void ospf_router_id_update (struct ospf *ospf) { @@ -112,8 +114,14 @@ ospf_router_id_update (struct ospf *ospf) if (!IPV4_ADDR_SAME (&router_id_old, &router_id)) { for (ALL_LIST_ELEMENTS_RO (ospf->oiflist, node, oi)) - /* Update self-neighbor's router_id. */ - oi->nbr_self->router_id = router_id; + { + /* Some nbrs are identified by router_id, these needs + * to be rebuilt. Possible optimization would be to do + * oi->nbr_self->router_id = router_id for + * !(virtual | ptop) links + */ + ospf_nbr_self_reset (oi); + } /* If AS-external-LSA is queued, then flush those LSAs. */ if (router_id_old.s_addr == 0 && ospf->external_origin) @@ -139,7 +147,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,16 +190,22 @@ 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++) { new->dmetric[i].type = -1; new->dmetric[i].value = -1; + new->dtag[i] = 0; } new->default_metric = -1; new->ref_bandwidth = OSPF_DEFAULT_REF_BANDWIDTH; + /* LSA timers */ + new->min_ls_interval = OSPF_MIN_LS_INTERVAL; + new->min_ls_arrival = OSPF_MIN_LS_ARRIVAL; + /* SPF timer value init. */ new->spf_delay = OSPF_SPF_DELAY_DEFAULT; new->spf_holdtime = OSPF_SPF_HOLDTIME_DEFAULT; @@ -199,8 +213,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 +236,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) { @@ -242,7 +256,17 @@ ospf_lookup () if (listcount (om->ospf) == 0) return NULL; - return listgetdata (listhead (om->ospf)); + return listgetdata ((struct listnode *)listhead (om->ospf)); +} + +static int +ospf_is_ready (struct ospf *ospf) +{ + /* OSPF must be on and Router-ID must be configured. */ + if (!ospf || ospf->router_id.s_addr == 0) + return 0; + + return 1; } static void @@ -271,14 +295,12 @@ ospf_get () if (ospf->router_id_static.s_addr == 0) ospf_router_id_update (ospf); -#ifdef HAVE_OPAQUE_LSA ospf_opaque_type11_lsa_init (ospf); -#endif /* HAVE_OPAQUE_LSA */ } 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 +375,7 @@ ospf_deferred_shutdown_check (struct ospf *ospf) timeout); return; } - + /* Shut down the entire process */ void ospf_terminate (void) @@ -406,9 +428,7 @@ ospf_finish_final (struct ospf *ospf) struct listnode *node, *nnode; int i; -#ifdef HAVE_OPAQUE_LSA ospf_opaque_type11_lsa_term (ospf); -#endif /* HAVE_OPAQUE_LSA */ /* be nice if this worked, but it doesn't */ /*ospf_flush_self_originated_lsas_now (ospf);*/ @@ -484,27 +504,31 @@ ospf_finish_final (struct ospf *ospf) OSPF_TIMER_OFF (ospf->t_lsa_refresher); OSPF_TIMER_OFF (ospf->t_read); OSPF_TIMER_OFF (ospf->t_write); -#ifdef HAVE_OPAQUE_LSA OSPF_TIMER_OFF (ospf->t_opaque_lsa_self); -#endif close (ospf->fd); stream_free(ospf->ibuf); -#ifdef HAVE_OPAQUE_LSA LSDB_LOOP (OPAQUE_AS_LSDB (ospf), rn, lsa) ospf_discard_from_db (ospf, ospf->lsdb, lsa); -#endif /* HAVE_OPAQUE_LSA */ LSDB_LOOP (EXTERNAL_LSDB (ospf), rn, lsa) ospf_discard_from_db (ospf, ospf->lsdb, lsa); 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 +578,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) @@ -578,9 +602,7 @@ ospf_area_new (struct ospf *ospf, struct in_addr area_id) /* Self-originated LSAs initialize. */ new->router_lsa_self = NULL; -#ifdef HAVE_OPAQUE_LSA ospf_opaque_type10_lsa_init (new); -#endif /* HAVE_OPAQUE_LSA */ new->oiflist = list_new (); new->ranges = route_table_init (); @@ -609,12 +631,10 @@ ospf_area_free (struct ospf_area *area) LSDB_LOOP (NSSA_LSDB (area), rn, lsa) ospf_discard_from_db (area->ospf, area->lsdb, lsa); -#ifdef HAVE_OPAQUE_LSA LSDB_LOOP (OPAQUE_AREA_LSDB (area), rn, lsa) ospf_discard_from_db (area->ospf, area->lsdb, lsa); LSDB_LOOP (OPAQUE_LINK_LSDB (area), rn, lsa) ospf_discard_from_db (area->ospf, area->lsdb, lsa); -#endif /* HAVE_OPAQUE_LSA */ ospf_lsdb_delete_all (area->lsdb); ospf_lsdb_free (area->lsdb); @@ -632,9 +652,7 @@ ospf_area_free (struct ospf_area *area) /* Cancel timer. */ OSPF_TIMER_OFF (area->t_stub_router); -#ifdef HAVE_OPAQUE_LSA OSPF_TIMER_OFF (area->t_opaque_lsa_self); -#endif /* HAVE_OPAQUE_LSA */ if (OSPF_IS_AREA_BACKBONE (area)) area->ospf->backbone = NULL; @@ -676,6 +694,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 +728,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) @@ -720,6 +742,70 @@ ospf_network_new (struct in_addr area_id, int format) return new; } +static void +add_ospf_interface (struct connected *co, struct ospf_area *area) +{ + struct ospf_interface *oi; + + oi = ospf_if_new (area->ospf, co->ifp, co->address); + oi->connected = co; + + oi->area = area; + + oi->params = ospf_lookup_if_params (co->ifp, oi->address->u.prefix4); + oi->output_cost = ospf_if_get_output_cost (oi); + + /* Relate ospf interface to ospf instance. */ + oi->ospf = area->ospf; + + /* update network type as interface flag */ + /* If network type is specified previously, + skip network type setting. */ + oi->type = IF_DEF_PARAMS (co->ifp)->type; + + /* Add pseudo neighbor. */ + ospf_nbr_self_reset (oi); + + ospf_area_add_if (oi->area, oi); + + /* if router_id is not configured, dont bring up + * interfaces. + * ospf_router_id_update() will call ospf_if_update + * whenever r-id is configured instead. + */ + if ((area->ospf->router_id.s_addr != 0) + && if_is_operative (co->ifp)) + ospf_if_up (oi); +} + +static void +update_redistributed (struct ospf *ospf, int add_to_ospf) +{ + struct route_node *rn; + struct external_info *ei; + + if (ospf_is_type_redistributed (ZEBRA_ROUTE_CONNECT)) + if (EXTERNAL_INFO (ZEBRA_ROUTE_CONNECT)) + for (rn = route_top (EXTERNAL_INFO (ZEBRA_ROUTE_CONNECT)); + rn; rn = route_next (rn)) + if ((ei = rn->info) != NULL) + { + if (add_to_ospf) + { + if (ospf_external_info_find_lsa (ospf, &ei->p)) + if (!ospf_distribute_check_connected (ospf, ei)) + ospf_external_lsa_flush (ospf, ei->type, &ei->p, + ei->ifindex /*, ei->nexthop */); + } + else + { + if (!ospf_external_info_find_lsa (ospf, &ei->p)) + if (ospf_distribute_check_connected (ospf, ei)) + ospf_external_lsa_originate (ospf, ei); + } + } +} + static void ospf_network_free (struct ospf *ospf, struct ospf_network *network) { @@ -735,7 +821,6 @@ ospf_network_set (struct ospf *ospf, struct prefix_ipv4 *p, struct ospf_network *network; struct ospf_area *area; struct route_node *rn; - struct external_info *ei; int ret = OSPF_AREA_ID_FORMAT_ADDRESS; rn = route_node_get (ospf->networks, (struct prefix *)p); @@ -753,16 +838,8 @@ ospf_network_set (struct ospf *ospf, struct prefix_ipv4 *p, ospf_network_run ((struct prefix *)p, area); /* Update connected redistribute. */ - if (ospf_is_type_redistributed (ZEBRA_ROUTE_CONNECT)) - if (EXTERNAL_INFO (ZEBRA_ROUTE_CONNECT)) - for (rn = route_top (EXTERNAL_INFO (ZEBRA_ROUTE_CONNECT)); - rn; rn = route_next (rn)) - if ((ei = rn->info) != NULL) - if (ospf_external_info_find_lsa (ospf, &ei->p)) - if (!ospf_distribute_check_connected (ospf, ei)) - ospf_external_lsa_flush (ospf, ei->type, &ei->p, - ei->ifindex /*, ei->nexthop */); - + update_redistributed(ospf, 1); + ospf_area_check_free (ospf, area_id); return 1; @@ -774,7 +851,6 @@ ospf_network_unset (struct ospf *ospf, struct prefix_ipv4 *p, { struct route_node *rn; struct ospf_network *network; - struct external_info *ei; struct listnode *node, *nnode; struct ospf_interface *oi; @@ -794,42 +870,66 @@ ospf_network_unset (struct ospf *ospf, struct prefix_ipv4 *p, /* Find interfaces that not configured already. */ for (ALL_LIST_ELEMENTS (ospf->oiflist, node, nnode, oi)) { - int found = 0; - struct connected *co = oi->connected; - if (oi->type == OSPF_IFTYPE_VIRTUALLINK) continue; - for (rn = route_top (ospf->networks); rn; rn = route_next (rn)) - { - if (rn->info == NULL) - continue; - - if (ospf_network_match_iface(co,&rn->p)) - { - found = 1; - route_unlock_node (rn); - break; - } - } - - if (found == 0) - ospf_if_free (oi); + ospf_network_run_subnet (ospf, oi->connected, NULL, NULL); } /* Update connected redistribute. */ - if (ospf_is_type_redistributed (ZEBRA_ROUTE_CONNECT)) - if (EXTERNAL_INFO (ZEBRA_ROUTE_CONNECT)) - for (rn = route_top (EXTERNAL_INFO (ZEBRA_ROUTE_CONNECT)); - rn; rn = route_next (rn)) - if ((ei = rn->info) != NULL) - if (!ospf_external_info_find_lsa (ospf, &ei->p)) - if (ospf_distribute_check_connected (ospf, ei)) - ospf_external_lsa_originate (ospf, ei); - + update_redistributed(ospf, 0); + + ospf_area_check_free (ospf, area_id); + return 1; } +/* Ensure there's an OSPF instance, as "ip ospf area" enabled OSPF means + * there might not be any 'router ospf' config. + * + * Otherwise, doesn't do anything different to ospf_if_update for now + */ +void +ospf_interface_area_set (struct interface *ifp) +{ + struct ospf *ospf = ospf_get(); + + ospf_if_update (ospf, ifp); + /* if_update does a update_redistributed */ + + return; +} + +void +ospf_interface_area_unset (struct interface *ifp) +{ + struct route_node *rn_oi; + struct ospf *ospf; + + if ((ospf = ospf_lookup ()) == NULL) + return; /* Ospf not ready yet */ + + /* Find interfaces that may need to be removed. */ + for (rn_oi = route_top (IF_OIFS (ifp)); rn_oi; rn_oi = route_next (rn_oi)) + { + struct ospf_interface *oi; + + if ( (oi = rn_oi->info) == NULL) + continue; + + if (oi->type == OSPF_IFTYPE_VIRTUALLINK) + continue; + + ospf_network_run_subnet (ospf, oi->connected, NULL, NULL); + } + + /* Update connected redistribute. */ + update_redistributed (ospf, 0); /* interfaces possibly removed */ + + return; +} + + /* Check whether interface matches given network * returns: 1, true. 0, false */ @@ -841,8 +941,101 @@ ospf_network_match_iface(const struct connected *co, const struct prefix *net) } static void -ospf_network_run_interface (struct prefix *p, struct ospf_area *area, - struct interface *ifp) +ospf_update_interface_area (struct connected *co, struct ospf_area *area) +{ + struct ospf_interface *oi = ospf_if_table_lookup (co->ifp, co->address); + + /* nothing to be done case */ + if (oi && oi->area == area) + return; + + if (oi) + ospf_if_free (oi); + + add_ospf_interface (co, area); +} + +/* Run OSPF for the given subnet, taking into account the following + * possible sources of area configuration, in the given order of preference: + * + * - Whether there is interface+address specific area configuration + * - Whether there is a default area for the interface + * - Whether there is an area given as a parameter. + * - If no specific network prefix/area is supplied, whether there's + * a matching network configured. + */ +static void +ospf_network_run_subnet (struct ospf *ospf, struct connected *co, + struct prefix *p, struct ospf_area *given_area) +{ + struct ospf_interface *oi; + struct ospf_if_params *params; + struct ospf_area *area = NULL; + struct route_node *rn; + int configed = 0; + + if (CHECK_FLAG(co->flags, ZEBRA_IFA_SECONDARY)) + return; + + if (co->address->family != AF_INET) + return; + + /* Try determine the appropriate area for this interface + address + * Start by checking interface config + */ + if (!(params = ospf_lookup_if_params (co->ifp, co->address->u.prefix4))) + params = IF_DEF_PARAMS (co->ifp); + + if (OSPF_IF_PARAM_CONFIGURED(params, if_area)) + area = (ospf_area_get (ospf, params->if_area, + OSPF_AREA_ID_FORMAT_ADDRESS)); + + /* If we've found an interface and/or addr specific area, then we're + * done + */ + if (area) + { + ospf_update_interface_area (co, area); + return; + } + + /* Otherwise, only remaining possibility is a matching network statement */ + if (p) + { + assert (given_area != NULL); + + /* Which either was supplied as a parameter.. (e.g. cause a new + * network/area was just added).. + */ + if (p->family == co->address->family + && ospf_network_match_iface (co, p)) + ospf_update_interface_area (co, given_area); + + return; + } + + /* Else we have to search the existing network/area config to see + * if any match.. + */ + for (rn = route_top (ospf->networks); rn; rn = route_next (rn)) + if (rn->info != NULL + && ospf_network_match_iface (co, &rn->p)) + { + struct ospf_network *network = (struct ospf_network *) rn->info; + area = ospf_area_get (ospf, network->area_id, network->format); + ospf_update_interface_area (co, area); + configed = 1; + } + + /* If the subnet isn't in any area, deconfigure */ + if (!configed && (oi = ospf_if_table_lookup (co->ifp, co->address))) + ospf_if_free (oi); +} + +static void +ospf_network_run_interface (struct ospf *ospf, struct interface *ifp, + struct prefix *p, + struct ospf_area *given_area) { struct listnode *cnode; struct connected *co; @@ -850,51 +1043,14 @@ ospf_network_run_interface (struct prefix *p, struct ospf_area *area, if (memcmp (ifp->name, "VLINK", 5) == 0) return; + /* Network prefix without area is nonsensical */ + if (p) + assert (given_area != NULL); + /* if interface prefix is match specified prefix, then create socket and join multicast group. */ for (ALL_LIST_ELEMENTS_RO (ifp->connected, cnode, co)) - { - - if (CHECK_FLAG(co->flags,ZEBRA_IFA_SECONDARY)) - continue; - - if (p->family == co->address->family - && ! ospf_if_table_lookup(ifp, co->address) - && ospf_network_match_iface(co,p)) - { - struct ospf_interface *oi; - - oi = ospf_if_new (area->ospf, ifp, co->address); - oi->connected = co; - - oi->area = area; - - oi->params = ospf_lookup_if_params (ifp, oi->address->u.prefix4); - oi->output_cost = ospf_if_get_output_cost (oi); - - /* Add pseudo neighbor. */ - ospf_nbr_add_self (oi); - - /* Relate ospf interface to ospf instance. */ - oi->ospf = area->ospf; - - /* update network type as interface flag */ - /* If network type is specified previously, - skip network type setting. */ - oi->type = IF_DEF_PARAMS (ifp)->type; - - ospf_area_add_if (oi->area, oi); - - /* if router_id is not configured, dont bring up - * interfaces. - * ospf_router_id_update() will call ospf_if_update - * whenever r-id is configured instead. - */ - if ((area->ospf->router_id.s_addr != 0) - && if_is_operative (ifp)) - ospf_if_up (oi); - } - } + ospf_network_run_subnet (ospf, co, p, given_area); } static void @@ -909,7 +1065,7 @@ ospf_network_run (struct prefix *p, struct ospf_area *area) /* Get target interface. */ for (ALL_LIST_ELEMENTS_RO (om->iflist, node, ifp)) - ospf_network_run_interface (p, area, ifp); + ospf_network_run_interface (area->ospf, ifp, p, area); } void @@ -942,25 +1098,17 @@ ospf_ls_upd_queue_empty (struct ospf_interface *oi) void ospf_if_update (struct ospf *ospf, struct interface *ifp) { - struct route_node *rn; - struct ospf_network *network; - struct ospf_area *area; - if (!ospf) ospf = ospf_lookup (); - /* OSPF must be on and Router-ID must be configured. */ - if (!ospf || ospf->router_id.s_addr == 0) + /* OSPF must be ready. */ + if (!ospf_is_ready (ospf)) return; - /* Run each netowrk for this interface. */ - for (rn = route_top (ospf->networks); rn; rn = route_next (rn)) - if (rn->info != NULL) - { - network = (struct ospf_network *) rn->info; - area = ospf_area_get (ospf, network->area_id, network->format); - ospf_network_run_interface (&rn->p, area, ifp); - } + ospf_network_run_interface (ospf, ifp, NULL, NULL); + + /* Update connected redistribute. */ + update_redistributed(ospf, 1); } void @@ -974,7 +1122,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 +1485,7 @@ ospf_timers_refresh_unset (struct ospf *ospf) return 1; } - + static struct ospf_nbr_nbma * ospf_nbr_nbma_new (void) { @@ -1534,6 +1682,8 @@ ospf_nbr_nbma_set (struct ospf *ospf, struct in_addr nbr_addr) p.prefixlen = IPV4_MAX_BITLEN; rn = route_node_get (ospf->nbr_nbma, (struct prefix *)&p); + if (rn->info) + route_unlock_node (rn); rn->info = nbr_nbma; for (ALL_LIST_ELEMENTS_RO (ospf->oiflist, node, oi)) diff --git a/ospfd/ospfd.h b/ospfd/ospfd.h index 7a56d163f..0315164b6 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,38 +58,15 @@ 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_MT 0x01 /* M/T */ #define OSPF_OPTION_E 0x02 #define OSPF_OPTION_MC 0x04 #define OSPF_OPTION_NP 0x08 #define OSPF_OPTION_EA 0x10 #define OSPF_OPTION_DC 0x20 #define OSPF_OPTION_O 0x40 +#define OSPF_OPTION_DN 0x80 /* OSPF Database Description flags. */ #define OSPF_DD_FLAG_MS 0x01 @@ -182,19 +132,23 @@ struct ospf #define OSPF_LOG_ADJACENCY_CHANGES (1 << 3) #define OSPF_LOG_ADJACENCY_DETAIL (1 << 4) -#ifdef HAVE_OPAQUE_LSA /* Opaque-LSA administrative flags. */ u_char opaque; #define OPAQUE_OPERATION_READY_BIT (1 << 0) -#define OPAQUE_BLOCK_TYPE_09_LSA_BIT (1 << 1) -#define OPAQUE_BLOCK_TYPE_10_LSA_BIT (1 << 2) -#define OPAQUE_BLOCK_TYPE_11_LSA_BIT (1 << 3) -#endif /* HAVE_OPAQUE_LSA */ /* RFC3137 stub router. Configured time to stay stub / max-metric */ 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 + + /* LSA timers */ + unsigned int min_ls_interval; /* minimum delay between LSAs (in msec) */ + unsigned int min_ls_arrival; /* minimum interarrival time between LSAs (in msec) */ /* SPF parameters */ unsigned int spf_delay; /* SPF delay time. */ @@ -223,9 +177,7 @@ struct ospf int external_origin; /* AS-external-LSA origin flag. */ int ase_calc; /* ASE calculation flag. */ -#ifdef HAVE_OPAQUE_LSA struct list *opaque_lsa_self; /* Type-11 Opaque-LSAs */ -#endif /* HAVE_OPAQUE_LSA */ /* Routing tables. */ struct route_table *old_table; /* Old routing table. */ @@ -240,10 +192,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. */ @@ -253,14 +206,10 @@ struct ospf struct thread *t_spf_calc; /* SPF calculation timer. */ struct thread *t_ase_calc; /* ASE calculation timer. */ struct thread *t_external_lsa; /* AS-external-LSA origin timer. */ -#ifdef HAVE_OPAQUE_LSA 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*/ @@ -268,7 +217,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; @@ -289,6 +238,9 @@ struct ospf -1 means metric value is not set. */ } dmetric [ZEBRA_ROUTE_MAX + 1]; + /* Redistribute tag info. */ + route_tag_t dtag [ZEBRA_ROUTE_MAX + 1]; + /* For redistribute route map. */ struct { @@ -390,9 +342,7 @@ struct ospf_area /* Self-originated LSAs. */ struct ospf_lsa *router_lsa_self; -#ifdef HAVE_OPAQUE_LSA struct list *opaque_lsa_self; /* Type-10 Opaque-LSAs */ -#endif /* HAVE_OPAQUE_LSA */ /* Area announce list. */ struct @@ -434,13 +384,14 @@ struct ospf_area /* Threads. */ struct thread *t_stub_router; /* Stub-router timer */ -#ifdef HAVE_OPAQUE_LSA struct thread *t_opaque_lsa_self; /* Type-10 Opaque-LSAs origin. */ -#endif /* HAVE_OPAQUE_LSA */ /* 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. */ @@ -606,6 +557,9 @@ extern struct ospf_area *ospf_area_lookup_by_area_id (struct ospf *, extern void ospf_area_add_if (struct ospf_area *, struct ospf_interface *); extern void ospf_area_del_if (struct ospf_area *, struct ospf_interface *); +extern void ospf_interface_area_set (struct interface *); +extern void ospf_interface_area_unset (struct interface *); + extern void ospf_route_map_init (void); extern void ospf_snmp_init (void); diff --git a/pimd/.gitignore b/pimd/.gitignore new file mode 100644 index 000000000..51a2ac87e --- /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 000000000..f6135a410 --- /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 000000000..9f07bda66 --- /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 000000000..425ac8229 --- /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 000000000..3912109b5 --- /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 000000000..72fb8264b --- /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 000000000..e87e567f9 --- /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 000000000..d04543685 --- /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 -I$(top_builddir)/lib +DEFS = @DEFS@ -DSYSCONFDIR=\"$(sysconfdir)/\" $(PIM_DEFS) +INSTALL_SDATA=@INSTALL@ -m 600 +LIBS = @LIBS@ + +AM_CFLAGS = $(WERROR) + +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_macro.c \ + pim_igmp_join.c pim_ssmpingd.c pim_int.c pim_static.c \ + pim_routemap.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_macro.h \ + pim_igmp_join.h pim_ssmpingd.h pim_int.h pim_static.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 000000000..1e3f72c83 --- /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 000000000..2308573b8 --- /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 000000000..7d1f52de8 --- /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 000000000..2e8c966f1 --- /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/pim_assert.c b/pimd/pim_assert.c new file mode 100644 index 000000000..ad21e08ef --- /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 000000000..bd3fb3e25 --- /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 000000000..749eeab63 --- /dev/null +++ b/pimd/pim_cmd.c @@ -0,0 +1,4951 @@ +/* + 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" +#include "pim_static.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" + "DrPri: Designated Router Priority sent%s%s", + VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE); + + vty_out(vty, "Interface Address DR Uptime Elections Changes NonPri DrPri%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 %10d%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, + pim_ifp->pim_dr_priority, + 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); + } + } +} + +static void static_mroute_add_all() +{ + struct listnode *node; + struct static_route *s_route; + + for (ALL_LIST_ELEMENTS_RO(qpim_static_route_list, node, s_route)) { + if (pim_mroute_add(&s_route->mc)) { + /* just log warning */ + char source_str[100]; + char group_str[100]; + pim_inet4_dump("", s_route->mc.mfcc_origin, source_str, sizeof(source_str)); + pim_inet4_dump("", s_route->mc.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 static_mroute_del_all() +{ + struct listnode *node; + struct static_route *s_route; + + for (ALL_LIST_ELEMENTS_RO(qpim_static_route_list, node, s_route)) { + if (pim_mroute_del(&s_route->mc)) { + /* just log warning */ + char source_str[100]; + char group_str[100]; + pim_inet4_dump("", s_route->mc.mfcc_origin, source_str, sizeof(source_str)); + pim_inet4_dump("", s_route->mc.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)) { + zlog_warn("ioctl(SIOCGETVIFCNT=%lu) failure for interface %s vif_index=%d: errno=%d: %s%s", + (unsigned long)SIOCGETVIFCNT, + ifp->name, + pim_ifp->mroute_vif_index, + errno, + safe_strerror(errno), + VTY_NEWLINE); + } + + 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; + struct static_route *s_route; + time_t now; + + vty_out(vty, "Proto: I=IGMP P=PIM S=STATIC%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(); + + /* print list of PIM and IGMP routes */ + 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); + } + } + + /* Print list of static routes */ + for (ALL_LIST_ELEMENTS_RO(qpim_static_route_list, node, s_route)) { + char group_str[100]; + char source_str[100]; + int oif_vif_index; + + pim_inet4_dump("", s_route->group, group_str, sizeof(group_str)); + pim_inet4_dump("", s_route->source, 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 = s_route->oif_ttls[oif_vif_index]; + if (ttl < 1) + continue; + + ifp_in = pim_if_find_by_vif_index(s_route->iif); + ifp_out = pim_if_find_by_vif_index(oif_vif_index); + + pim_time_uptime(oif_uptime, sizeof(oif_uptime), now - s_route->creation[oif_vif_index]); + + proto[0] = '\0'; + strcat(proto, "S"); + + vty_out(vty, "%-15s %-15s %-5s %-5s %5d %-6s %5d %3d %8s %s", + source_str, + group_str, + proto, + ifp_in ? ifp_in->name : "", + s_route->iif, + 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; + struct static_route *s_route; + + vty_out(vty, "%s", VTY_NEWLINE); + + vty_out(vty, "Source Group Packets Bytes WrongIf %s", + VTY_NEWLINE); + + /* Print PIM and IGMP route counts */ + 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=%lu) failure for (S,G)=(%s,%s): errno=%d: %s%s", + (unsigned long)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); + } + + /* Print static route counts */ + for (ALL_LIST_ELEMENTS_RO(qpim_static_route_list, node, s_route)) { + char group_str[100]; + char source_str[100]; + struct sioc_sg_req sgreq; + + memset(&sgreq, 0, sizeof(sgreq)); + sgreq.src = s_route->mc.mfcc_origin; + sgreq.grp = s_route->mc.mfcc_mcastgrp; + + pim_inet4_dump("", s_route->mc.mfcc_mcastgrp, group_str, sizeof(group_str)); + pim_inet4_dump("", s_route->mc.mfcc_origin, source_str, sizeof(source_str)); + + if (ioctl(qpim_mroute_socket_fd, SIOCGETSGCNT, &sgreq)) { + int e = errno; + vty_out(vty, + "ioctl(SIOCGETSGCNT=%lu) failure for (S,G)=(%s,%s): errno=%d: %s%s", + /* note that typeof ioctl defs can vary across platforms, from + * int, to unsigned int, to long unsigned int + */ + (unsigned long)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(); + static_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(); + static_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_drprio, + interface_ip_pim_drprio_cmd, + "ip pim drpriority <1-4294967295>", + IP_STR + PIM_STR + "Set the Designated Router Election Priority\n" + "Value of the new DR Priority\n") +{ + struct interface *ifp; + struct pim_interface *pim_ifp; + uint32_t old_dr_prio; + + ifp = vty->index; + pim_ifp = ifp->info; + + if (!pim_ifp) { + vty_out(vty, "Please enable PIM on interface, first%s", VTY_NEWLINE); + return CMD_WARNING; + } + + old_dr_prio = pim_ifp->pim_dr_priority; + + pim_ifp->pim_dr_priority = strtol(argv[0], NULL, 10); + + if (old_dr_prio != pim_ifp->pim_dr_priority) { + if (pim_if_dr_election(ifp)) + pim_hello_restart_now(ifp); + } + + return CMD_SUCCESS; +} + +DEFUN (interface_no_ip_pim_drprio, + interface_no_ip_pim_drprio_cmd, + "no ip pim drpriority {<1-4294967295>}", + IP_STR + PIM_STR + "Revert the Designated Router Priority to default\n" + "Old Value of the Priority\n") +{ + struct interface *ifp; + struct pim_interface *pim_ifp; + + ifp = vty->index; + pim_ifp = ifp->info; + + if (!pim_ifp) { + vty_out(vty, "Pim not enabled on this interface%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (pim_ifp->pim_dr_priority != PIM_DEFAULT_DR_PRIORITY) { + pim_ifp->pim_dr_priority = PIM_DEFAULT_DR_PRIORITY; + if (pim_if_dr_election(ifp)) + pim_hello_restart_now(ifp); + } + + 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 (interface_ip_mroute, + interface_ip_mroute_cmd, + "ip mroute INTERFACE A.B.C.D", + IP_STR + "Add multicast route\n" + "Outgoing interface name\n" + "Group address\n") +{ + struct interface *iif; + struct interface *oif; + const char *oifname; + const char *grp_str; + struct in_addr grp_addr; + struct in_addr src_addr; + int result; + + iif = vty->index; + + oifname = argv[0]; + oif = if_lookup_by_name(oifname); + if (!oif) { + vty_out(vty, "No such interface name %s%s", + oifname, 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; + } + + src_addr.s_addr = INADDR_ANY; + + if (pim_static_add(iif, oif, grp_addr, src_addr)) { + vty_out(vty, "Failed to add route%s", VTY_NEWLINE); + return CMD_WARNING; + } + + return CMD_SUCCESS; +} + +DEFUN (interface_ip_mroute_source, + interface_ip_mroute_source_cmd, + "ip mroute INTERFACE A.B.C.D A.B.C.D", + IP_STR + "Add multicast route\n" + "Outgoing interface name\n" + "Group address\n" + "Source address\n") +{ + struct interface *iif; + struct interface *oif; + const char *oifname; + const char *grp_str; + struct in_addr grp_addr; + const char *src_str; + struct in_addr src_addr; + int result; + + iif = vty->index; + + oifname = argv[0]; + oif = if_lookup_by_name(oifname); + if (!oif) { + vty_out(vty, "No such interface name %s%s", + oifname, 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; + } + + src_str = argv[2]; + 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; + } + + if (pim_static_add(iif, oif, grp_addr, src_addr)) { + vty_out(vty, "Failed to add route%s", VTY_NEWLINE); + return CMD_WARNING; + } + + return CMD_SUCCESS; +} + +DEFUN (interface_no_ip_mroute, + interface_no_ip_mroute_cmd, + "no ip mroute INTERFACE A.B.C.D", + NO_STR + IP_STR + "Add multicast route\n" + "Outgoing interface name\n" + "Group Address\n") +{ + struct interface *iif; + struct interface *oif; + const char *oifname; + const char *grp_str; + struct in_addr grp_addr; + struct in_addr src_addr; + int result; + + iif = vty->index; + + oifname = argv[0]; + oif = if_lookup_by_name(oifname); + if (!oif) { + vty_out(vty, "No such interface name %s%s", + oifname, 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; + } + + src_addr.s_addr = INADDR_ANY; + + if (pim_static_del(iif, oif, grp_addr, src_addr)) { + vty_out(vty, "Failed to remove route%s", VTY_NEWLINE); + return CMD_WARNING; + } + + return CMD_SUCCESS; +} + +DEFUN (interface_no_ip_mroute_source, + interface_no_ip_mroute_source_cmd, + "no ip mroute INTERFACE A.B.C.D A.B.C.D", + NO_STR + IP_STR + "Add multicast route\n" + "Outgoing interface name\n" + "Group Address\n" + "Source Address\n") +{ + struct interface *iif; + struct interface *oif; + const char *oifname; + const char *grp_str; + struct in_addr grp_addr; + const char *src_str; + struct in_addr src_addr; + int result; + + iif = vty->index; + + oifname = argv[0]; + oif = if_lookup_by_name(oifname); + if (!oif) { + vty_out(vty, "No such interface name %s%s", + oifname, 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; + } + + src_str = argv[2]; + 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; + } + + if (pim_static_del(iif, oif, grp_addr, src_addr)) { + vty_out(vty, "Failed to remove route%s", VTY_NEWLINE); + return CMD_WARNING; + } + + return CMD_SUCCESS; +} + +DEFUN (interface_ip_pim_hello, + interface_ip_pim_hello_cmd, + "ip pim hello <1-180>", + IP_STR + PIM_STR + IFACE_PIM_HELLO_STR + IFACE_PIM_HELLO_TIME_STR) +{ + struct interface *ifp; + struct pim_interface *pim_ifp; + + ifp = vty->index; + pim_ifp = ifp->info; + + if (!pim_ifp) { + vty_out(vty, "Pim not enabled on this interface%s", VTY_NEWLINE); + return CMD_WARNING; + } + + pim_ifp->pim_hello_period = strtol(argv[0], NULL, 10); + + if (argc == 2) + pim_ifp->pim_default_holdtime = strtol(argv[1], NULL, 10); + + return CMD_SUCCESS; +} + +ALIAS (interface_ip_pim_hello, + interface_ip_pim_hello_hold_cmd, + "ip pim hello <1-180> <1-180>", + IP_STR + PIM_STR + IFACE_PIM_HELLO_STR + IFACE_PIM_HELLO_TIME_STR + IFACE_PIM_HELLO_HOLD_STR) + + +DEFUN (interface_no_ip_pim_hello, + interface_no_ip_pim_hello_cmd, + "no ip pim hello {<1-180> <1-180>}", + NO_STR + IP_STR + PIM_STR + IFACE_PIM_HELLO_STR + IFACE_PIM_HELLO_TIME_STR + IFACE_PIM_HELLO_HOLD_STR) +{ + struct interface *ifp; + struct pim_interface *pim_ifp; + + ifp = vty->index; + pim_ifp = ifp->info; + + if (!pim_ifp) { + vty_out(vty, "Pim not enabled on this interface%s", VTY_NEWLINE); + return CMD_WARNING; + } + + pim_ifp->pim_hello_period = PIM_DEFAULT_HELLO_PERIOD; + pim_ifp->pim_default_holdtime = -1; + + 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_static, + debug_static_cmd, + "debug static", + DEBUG_STR + DEBUG_STATIC_STR) +{ + PIM_DO_DEBUG_STATIC; + return CMD_SUCCESS; +} + +DEFUN (no_debug_static, + no_debug_static_cmd, + "no debug static", + NO_STR + DEBUG_STR + DEBUG_STATIC_STR) +{ + PIM_DONT_DEBUG_STATIC; + return CMD_SUCCESS; +} + +ALIAS (no_debug_static, + undebug_static_cmd, + "undebug static", + UNDEBUG_STR + DEBUG_STATIC_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_pim, + show_debugging_pim_cmd, + "show debugging pim", + SHOW_STR + DEBUG_STR + PIM_STR) +{ + 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 (INTERFACE_NODE, &interface_ip_pim_drprio_cmd); + install_element (INTERFACE_NODE, &interface_no_ip_pim_drprio_cmd); + install_element (INTERFACE_NODE, &interface_ip_pim_hello_cmd); + install_element (INTERFACE_NODE, &interface_ip_pim_hello_hold_cmd); + install_element (INTERFACE_NODE, &interface_no_ip_pim_hello_cmd); + + // Static mroutes NEB + install_element (INTERFACE_NODE, &interface_ip_mroute_cmd); + install_element (INTERFACE_NODE, &interface_ip_mroute_source_cmd); + install_element (INTERFACE_NODE, &interface_no_ip_mroute_cmd); + install_element (INTERFACE_NODE, &interface_no_ip_mroute_source_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_pim_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, &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_static_cmd); + install_element (ENABLE_NODE, &no_debug_static_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_static_cmd); + install_element (CONFIG_NODE, &no_debug_static_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 000000000..a1cb58161 --- /dev/null +++ b/pimd/pim_cmd.h @@ -0,0 +1,70 @@ +/* + 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_PIM_HELLO_STR "Hello Interval\n" +#define IFACE_PIM_HELLO_TIME_STR "Time in seconds for Hello Interval\n" +#define IFACE_PIM_HELLO_HOLD_STR "Time in seconds for Hold Interval\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_STATIC_STR "PIM Static Multicast Route activity\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 000000000..bfc128b97 --- /dev/null +++ b/pimd/pim_hello.c @@ -0,0 +1,550 @@ +/* + 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; + + if (PIM_DEBUG_PIM_HELLO) + 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) { + if (PIM_DEBUG_PIM_HELLO) { + char src_str[100]; + pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); + zlog_debug("%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) { + if (PIM_DEBUG_PIM_HELLO) { + char src_str[100]; + pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); + zlog_debug("%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_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_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: + if (PIM_DEBUG_PIM_HELLO) { + char src_str[100]; + pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); + zlog_debug("%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_HELLO) { + 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)) { + if (PIM_DEBUG_PIM_HELLO) { + char src_str[100]; + pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); + zlog_debug("%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) { + if (PIM_DEBUG_PIM_HELLO) { + 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_HELLO) { + 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) { + if (PIM_DEBUG_PIM_HELLO) { + char src_str[100]; + pim_inet4_dump("", src_addr, src_str, sizeof(src_str)); + zlog_debug("%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) { + if (PIM_DEBUG_PIM_HELLO) { + zlog_debug("%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) { + if (PIM_DEBUG_PIM_HELLO) { + zlog_debug("%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) { + if (PIM_DEBUG_PIM_HELLO) { + zlog_debug("%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) { + if (PIM_DEBUG_PIM_HELLO) { + zlog_debug("%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) { + if (PIM_DEBUG_PIM_HELLO) { + zlog_debug("%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 000000000..b5e272d5d --- /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 000000000..ddad6cb75 --- /dev/null +++ b/pimd/pim_iface.c @@ -0,0 +1,1194 @@ +/* + 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_sock.h" +#include "pim_time.h" +#include "pim_ssmpingd.h" + +static void pim_if_igmp_join_del_all(struct interface *ifp); + +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; + pim_ifp->pim_generation_id = 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 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) { + pim_ifp->primary_address = new_prim_addr; + + if (!PIM_IF_TEST_PIM(pim_ifp->options)) { + return changed; + } + + pim_addr_change(ifp); + } + + 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 */ + if (PIM_DEBUG_ZEBRA) + zlog_debug("FIXME T31 C15 %s: on interface %s: acting on any addr change", + __PRETTY_FUNCTION__, ifp->name); + + 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(ifindex_t 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 = random() % (effective_override_interval_msec + 1); + + 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; + uint32_t ramount = 0; + + 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) */ + ramount = 1100 + (random() % (1400 - 1100 + 1)); + t_suppressed_msec = qpim_t_periodic * ramount; + + 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, + ifindex_t 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; + } + + if (PIM_DEBUG_IGMP_EVENTS) { + 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 000000000..8cad3d135 --- /dev/null +++ b/pimd/pim_iface.h @@ -0,0 +1,159 @@ +/* + 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)) + +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(ifindex_t vif_index); +int pim_if_find_vifindex_by_ifindex(ifindex_t 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 000000000..ad97879d6 --- /dev/null +++ b/pimd/pim_ifchannel.c @@ -0,0 +1,898 @@ +/* + 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) { + if (PIM_DEBUG_PIM_EVENTS) { + 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; + + ch->ifassert_my_metric = pim_macro_ch_my_assert_metric_eval(ch); + ch->ifassert_winner_metric = pim_macro_ch_my_assert_metric_eval (ch); + + ch->ifassert_winner.s_addr = 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); + + /* 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 000000000..e6f1c2947 --- /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 000000000..ad3c7b555 --- /dev/null +++ b/pimd/pim_igmp.c @@ -0,0 +1,1435 @@ +/* + 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, ifindex_t 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); + int i; + + for (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); + 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); + 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; + ifindex_t 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) +{ + 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 = NULL; + group->t_group_query_retransmit_timer = NULL; + 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, igmp->fd, igmp->interface->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); /* 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 000000000..ab396159e --- /dev/null +++ b/pimd/pim_igmp.h @@ -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$ $ +*/ + +#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); + +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); + +struct igmp_source * +source_new (struct igmp_group *group, + struct in_addr src_addr); +#endif /* PIM_IGMP_H */ diff --git a/pimd/pim_igmp_join.c b/pimd/pim_igmp_join.c new file mode 100644 index 000000000..042818a67 --- /dev/null +++ b/pimd/pim_igmp_join.c @@ -0,0 +1,72 @@ +/* + 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 "zebra.h" +#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, ifindex_t ifindex, + struct in_addr group_addr, + struct in_addr source_addr) +{ + struct group_source_req req; + struct sockaddr_in group; + struct sockaddr_in source; + + memset(&group, 0, sizeof(group)); + group.sin_family = AF_INET; + group.sin_addr = group_addr; + group.sin_port = htons(0); + memcpy(&req.gsr_group, &group, sizeof(struct sockaddr_in)); + + memset(&source, 0, sizeof(source)); + source.sin_family = AF_INET; + source.sin_addr = source_addr; + source.sin_port = htons(0); + memcpy(&req.gsr_source, &source, sizeof(struct sockaddr_in)); + + 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 000000000..67779fffd --- /dev/null +++ b/pimd/pim_igmp_join.h @@ -0,0 +1,33 @@ +/* + 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 +#include "if.h" + +int pim_igmp_join_source(int fd, ifindex_t 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 000000000..180de9d71 --- /dev/null +++ b/pimd/pim_igmpv3.c @@ -0,0 +1,1716 @@ +/* + 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; +} + +struct igmp_source * +source_new (struct igmp_group *group, + struct in_addr src_addr) +{ + 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, + group->group_igmp_sock->interface->name); + } + + 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 = NULL; + 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 = NULL; + + 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) +{ + struct igmp_source *src; + + src = igmp_find_source_by_addr(group, src_addr); + if (src) { + return src; + } + + src = source_new(group, src_addr); + 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 igmp_group *group; + int i; + + /* non-existant group is created as INCLUDE {empty} */ + group = igmp_add_group_by_addr(igmp, group_addr); + 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); + 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); + 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); + 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); + 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); + 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); + 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); + 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); + 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); + 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); + 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); + 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); + 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); + } + + memset(&to, 0, sizeof(to)); + to.sin_family = AF_INET; + to.sin_addr = dst_addr; + tolen = sizeof(to); + + sent = sendto(fd, query_buf, msg_size, MSG_DONTWAIT, + (struct sockaddr *)&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 000000000..bb7e92672 --- /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 000000000..208075190 --- /dev/null +++ b/pimd/pim_int.c @@ -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$ $ +*/ + +#include + +#include +#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 000000000..d64b10327 --- /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 000000000..9d8e0012e --- /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 000000000..37ec0f452 --- /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 000000000..3f5653252 --- /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 000000000..472fa9b4e --- /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 000000000..ea70a8fcf --- /dev/null +++ b/pimd/pim_main.c @@ -0,0 +1,284 @@ +/* + 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 "vrf.h" +#include "filter.h" +#include "vty.h" +#include "sigevent.h" +#include "version.h" +#include "prefix.h" +#include "plist.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(); + + 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(); + vrf_init(); + access_list_init(); + prefix_list_init (); + pim_route_map_init (); + pim_init(); + + /* + * Initialize zclient "update" and "lookup" sockets + */ + pim_zebra_init (master, 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 000000000..fa460e28f --- /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 000000000..125d1901b --- /dev/null +++ b/pimd/pim_mroute.h @@ -0,0 +1,177 @@ +/* + 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) + +#if defined(HAVE_LINUX_MROUTE_H) +#include +#else +/* + 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 +#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 000000000..8ead7ce64 --- /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 000000000..a884fc84d --- /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 000000000..8932dc324 --- /dev/null +++ b/pimd/pim_neighbor.c @@ -0,0 +1,722 @@ +/* + 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. + */ +int 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); + return 1; + } + + return 0; +} + +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; + } + + 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); + + /* + 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 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 000000000..5b2172dfc --- /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); +int 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 000000000..1aaece3f4 --- /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 000000000..1753545ab --- /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 000000000..c52b0d393 --- /dev/null +++ b/pimd/pim_pim.c @@ -0,0 +1,765 @@ +/* + 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" + +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_REGISTER || + pim_type == PIM_MSG_TYPE_REG_STOP || + pim_type == PIM_MSG_TYPE_BOOTSTRAP || + pim_type == PIM_MSG_TYPE_GRAFT || + pim_type == PIM_MSG_TYPE_GRAFT_ACK || + pim_type == PIM_MSG_TYPE_CANDIDATE) + { + if (PIM_DEBUG_PIM_PACKETS) { + zlog_debug("Recv PIM packet type %d which is not currently understood", + pim_type); + } + return -1; + } + + 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); + 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; + ifindex_t 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, ifindex_t 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)) { + close(fd); + 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)); + } + + memset(&to, 0, sizeof(to)); + to.sin_family = AF_INET; + to.sin_addr = dst; + 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, + (struct sockaddr *)&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_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)) { + if (PIM_DEBUG_PIM_HELLO) { + zlog_debug("%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; + + if (PIM_DEBUG_PIM_HELLO) { + 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_HELLO) { + 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 = random() % (triggered_hello_delay_msec + 1); + + if (PIM_DEBUG_PIM_HELLO) { + 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; + uint32_t old_genid; + + 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(); + + /* + * Just ensure that the new generation id + * actually chooses something different. + * Actually ran across a case where this + * happened, pre-switch to random(). + * While this is unlikely to happen now + * let's make sure it doesn't. + */ + old_genid = pim_ifp->pim_generation_id; + + while (old_genid == pim_ifp->pim_generation_id) + pim_ifp->pim_generation_id = random(); + + 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 000000000..4b378fb2c --- /dev/null +++ b/pimd/pim_pim.h @@ -0,0 +1,77 @@ +/* + 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_REGISTER (1) +#define PIM_MSG_TYPE_REG_STOP (2) +#define PIM_MSG_TYPE_JOIN_PRUNE (3) +#define PIM_MSG_TYPE_BOOTSTRAP (4) +#define PIM_MSG_TYPE_ASSERT (5) +#define PIM_MSG_TYPE_GRAFT (6) +#define PIM_MSG_TYPE_GRAFT_ACK (7) +#define PIM_MSG_TYPE_CANDIDATE (8) + +#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_routemap.c b/pimd/pim_routemap.c new file mode 100644 index 000000000..9cc13be68 --- /dev/null +++ b/pimd/pim_routemap.c @@ -0,0 +1,32 @@ +/* PIM Route-map Code + * Copyright (C) 2016 Cumulus Networks + * + * 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 "routemap.h" + +#include "pimd.h" + +void +pim_route_map_init (void) +{ + route_map_init (); + route_map_init_vty (); +} diff --git a/pimd/pim_rpf.c b/pimd/pim_rpf.c new file mode 100644 index 000000000..e7619a5cc --- /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_info("%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 pim_rpf *old_rpf) +{ + struct pim_nexthop save_nexthop; + struct pim_rpf save_rpf; + struct pim_rpf *rpf = &up->rpf; + + save_nexthop = rpf->source_nexthop; /* detect change in pim_nexthop */ + save_rpf = up->rpf; + + 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) && PIM_DEBUG_PIM_EVENTS) { + /* 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_debug("%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.rpf_addr.s_addr != rpf->rpf_addr.s_addr) { + + /* return old rpf to caller ? */ + if (old_rpf) + *old_rpf = save_rpf; + + 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 000000000..9a48ea017 --- /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 pim_rpf *old_rpf); + +#endif /* PIM_RPF_H */ diff --git a/pimd/pim_signals.c b/pimd/pim_signals.c new file mode 100644 index 000000000..35493311f --- /dev/null +++ b/pimd/pim_signals.c @@ -0,0 +1,87 @@ +/* + 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_info ("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_info ("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 000000000..62523c038 --- /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 000000000..ceae4f207 --- /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 +#include "pim_mroute.h" + +#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 and Solaris IP_PKTINFO */ + int opt = 1; + if (setsockopt(fd, IPPROTO_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, ifindex_t 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, ifindex_t 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, + ifindex_t *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 == IPPROTO_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 000000000..622fb47cb --- /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, ifindex_t ifindex); +int pim_socket_join_source(int fd, ifindex_t 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, + ifindex_t *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 000000000..fe88eba27 --- /dev/null +++ b/pimd/pim_ssmpingd.c @@ -0,0 +1,449 @@ +/* + 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, (struct sockaddr *)&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 and Solaris IP_PKTINFO */ + int opt = 1; + if (setsockopt(fd, IPPROTO_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, + (struct sockaddr *)&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); + ifindex_t 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 000000000..4bef20b20 --- /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_static.c b/pimd/pim_static.c new file mode 100644 index 000000000..078beab24 --- /dev/null +++ b/pimd/pim_static.c @@ -0,0 +1,342 @@ +/* + PIM for Quagga: add the ability to configure multicast static routes + Copyright (C) 2014 Nathan Bahr, ATCorp + + 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 "vty.h" + +#include "pim_static.h" +#include "pim_time.h" +#include "pim_str.h" +#include "pimd.h" +#include "pim_iface.h" +#include "log.h" +#include "memory.h" +#include "linklist.h" + +void pim_static_route_free(struct static_route *s_route) +{ + XFREE(MTYPE_PIM_STATIC_ROUTE, s_route); +} + +static struct static_route * static_route_alloc() +{ + struct static_route *s_route; + + s_route = XCALLOC(MTYPE_PIM_STATIC_ROUTE, sizeof(*s_route)); + if (!s_route) { + zlog_err("PIM XCALLOC(%zu) failure", sizeof(*s_route)); + return 0; + } + return s_route; +} + +static struct static_route *static_route_new(unsigned int iif, + unsigned int oif, + struct in_addr group, + struct in_addr source) +{ + struct static_route * s_route; + s_route = static_route_alloc(); + if (!s_route) { + return 0; + } + + s_route->group = group; + s_route->source = source; + s_route->iif = iif; + s_route->oif_ttls[oif] = 1; + s_route->oif_count = 1; + s_route->mc.mfcc_origin = source; + s_route->mc.mfcc_mcastgrp = group; + s_route->mc.mfcc_parent = iif; + s_route->mc.mfcc_ttls[oif] = 1; + s_route->creation[oif] = pim_time_monotonic_sec(); + + return s_route; +} + + +int pim_static_add(struct interface *iif, struct interface *oif, struct in_addr group, struct in_addr source) +{ + struct listnode *node = 0; + struct static_route *s_route = 0; + struct static_route *original_s_route = 0; + struct pim_interface *pim_iif = iif ? iif->info : 0; + struct pim_interface *pim_oif = oif ? oif->info : 0; + unsigned int iif_index = pim_iif ? pim_iif->mroute_vif_index : 0; + unsigned int oif_index = pim_oif ? pim_oif->mroute_vif_index : 0; + + if (!iif_index || !oif_index) { + zlog_warn("%s %s: Unable to add static route: Invalid interface index(iif=%d,oif=%d)", + __FILE__, __PRETTY_FUNCTION__, + iif_index, + oif_index); + return -2; + } + +#ifdef PIM_ENFORCE_LOOPFREE_MFC + if (iif_index == oif_index) { + /* looped MFC entry */ + zlog_warn("%s %s: Unable to add static route: Looped MFC entry(iif=%d,oif=%d)", + __FILE__, __PRETTY_FUNCTION__, + iif_index, + oif_index); + return -4; + } +#endif + + for (ALL_LIST_ELEMENTS_RO(qpim_static_route_list, node, s_route)) { + if (s_route->group.s_addr == group.s_addr && + s_route->source.s_addr == source.s_addr) { + if (s_route->iif == iif_index && + s_route->oif_ttls[oif_index]) { + char gifaddr_str[100]; + char sifaddr_str[100]; + pim_inet4_dump("", group, gifaddr_str, sizeof(gifaddr_str)); + pim_inet4_dump("", source, sifaddr_str, sizeof(sifaddr_str)); + zlog_warn("%s %s: Unable to add static route: Route already exists (iif=%d,oif=%d,group=%s,source=%s)", + __FILE__, __PRETTY_FUNCTION__, + iif_index, + oif_index, + gifaddr_str, + sifaddr_str); + return -3; + } + + /* Ok, from here on out we will be making changes to the s_route structure, but if + * for some reason we fail to commit these changes to the kernel, we want to be able + * restore the state of the list. So copy the node data and if need be, we can copy + * back if it fails. + */ + original_s_route = static_route_alloc(); + if (!original_s_route) { + return -5; + } + memcpy(original_s_route, s_route, sizeof(struct static_route)); + + /* Route exists and has the same input interface, but adding a new output interface */ + if (s_route->iif == iif_index) { + s_route->oif_ttls[oif_index] = 1; + s_route->mc.mfcc_ttls[oif_index] = 1; + s_route->creation[oif_index] = pim_time_monotonic_sec(); + ++s_route->oif_count; + } else { + /* input interface changed */ + s_route->iif = iif_index; + s_route->mc.mfcc_parent = iif_index; + +#ifdef PIM_ENFORCE_LOOPFREE_MFC + /* check to make sure the new input was not an old output */ + if (s_route->oif_ttls[iif_index]) { + s_route->oif_ttls[iif_index] = 0; + s_route->creation[iif_index] = 0; + s_route->mc.mfcc_ttls[iif_index] = 0; + --s_route->oif_count; + } +#endif + + /* now add the new output, if it is new */ + if (!s_route->oif_ttls[oif_index]) { + s_route->oif_ttls[oif_index] = 1; + s_route->creation[oif_index] = pim_time_monotonic_sec(); + s_route->mc.mfcc_ttls[oif_index] = 1; + ++s_route->oif_count; + } + } + + break; + } + } + + /* If node is null then we reached the end of the list without finding a match */ + if (!node) { + s_route = static_route_new(iif_index, oif_index, group, source); + listnode_add(qpim_static_route_list, s_route); + } + + if (pim_mroute_add(&(s_route->mc))) + { + char gifaddr_str[100]; + char sifaddr_str[100]; + pim_inet4_dump("", group, gifaddr_str, sizeof(gifaddr_str)); + pim_inet4_dump("", source, sifaddr_str, sizeof(sifaddr_str)); + zlog_warn("%s %s: Unable to add static route(iif=%d,oif=%d,group=%s,source=%s)", + __FILE__, __PRETTY_FUNCTION__, + iif_index, + oif_index, + gifaddr_str, + sifaddr_str); + + /* Need to put s_route back to the way it was */ + if (original_s_route) { + memcpy(s_route, original_s_route, sizeof(struct static_route)); + } else { + /* we never stored off a copy, so it must have been a fresh new route */ + listnode_delete(qpim_static_route_list, s_route); + pim_static_route_free(s_route); + } + + if (original_s_route) { + pim_static_route_free(original_s_route); + } + + return -1; + } + + /* Make sure we free the memory for the route copy if used */ + if (original_s_route) { + pim_static_route_free(original_s_route); + } + + if (PIM_DEBUG_STATIC) { + char gifaddr_str[100]; + char sifaddr_str[100]; + pim_inet4_dump("", group, gifaddr_str, sizeof(gifaddr_str)); + pim_inet4_dump("", source, sifaddr_str, sizeof(sifaddr_str)); + zlog_debug("%s: Static route added(iif=%d,oif=%d,group=%s,source=%s)", + __PRETTY_FUNCTION__, + iif_index, + oif_index, + gifaddr_str, + sifaddr_str); + } + + return 0; +} + +int pim_static_del(struct interface *iif, struct interface *oif, struct in_addr group, struct in_addr source) +{ + struct listnode *node = 0; + struct listnode *nextnode = 0; + struct static_route *s_route = 0; + struct pim_interface *pim_iif = iif ? iif->info : 0; + struct pim_interface *pim_oif = oif ? oif->info : 0; + unsigned int iif_index = pim_iif ? pim_iif->mroute_vif_index : 0; + unsigned int oif_index = pim_oif ? pim_oif->mroute_vif_index : 0; + + if (!iif_index || !oif_index) { + zlog_warn("%s %s: Unable to remove static route: Invalid interface index(iif=%d,oif=%d)", + __FILE__, __PRETTY_FUNCTION__, + iif_index, + oif_index); + return -2; + } + + for (ALL_LIST_ELEMENTS(qpim_static_route_list, node, nextnode, s_route)) { + if (s_route->iif == iif_index && + s_route->group.s_addr == group.s_addr && + s_route->source.s_addr == source.s_addr && + s_route->oif_ttls[oif_index]) { + s_route->oif_ttls[oif_index] = 0; + s_route->mc.mfcc_ttls[oif_index] = 0; + --s_route->oif_count; + + /* If there are no more outputs then delete the whole route, otherwise set the route with the new outputs */ + if (s_route->oif_count <= 0 ? pim_mroute_del(&s_route->mc) : pim_mroute_add(&s_route->mc)) { + char gifaddr_str[100]; + char sifaddr_str[100]; + pim_inet4_dump("", group, gifaddr_str, sizeof(gifaddr_str)); + pim_inet4_dump("", source, sifaddr_str, sizeof(sifaddr_str)); + zlog_warn("%s %s: Unable to remove static route(iif=%d,oif=%d,group=%s,source=%s)", + __FILE__, __PRETTY_FUNCTION__, + iif_index, + oif_index, + gifaddr_str, + sifaddr_str); + + s_route->oif_ttls[oif_index] = 1; + s_route->mc.mfcc_ttls[oif_index] = 1; + ++s_route->oif_count; + + return -1; + } + + s_route->creation[oif_index] = 0; + + if (s_route->oif_count <= 0) { + listnode_delete(qpim_static_route_list, s_route); + pim_static_route_free(s_route); + } + + if (PIM_DEBUG_STATIC) { + char gifaddr_str[100]; + char sifaddr_str[100]; + pim_inet4_dump("", group, gifaddr_str, sizeof(gifaddr_str)); + pim_inet4_dump("", source, sifaddr_str, sizeof(sifaddr_str)); + zlog_debug("%s: Static route removed(iif=%d,oif=%d,group=%s,source=%s)", + __PRETTY_FUNCTION__, + iif_index, + oif_index, + gifaddr_str, + sifaddr_str); + } + + break; + } + } + + if (!node) { + char gifaddr_str[100]; + char sifaddr_str[100]; + pim_inet4_dump("", group, gifaddr_str, sizeof(gifaddr_str)); + pim_inet4_dump("", source, sifaddr_str, sizeof(sifaddr_str)); + zlog_warn("%s %s: Unable to remove static route: Route does not exist(iif=%d,oif=%d,group=%s,source=%s)", + __FILE__, __PRETTY_FUNCTION__, + iif_index, + oif_index, + gifaddr_str, + sifaddr_str); + return -3; + } + + return 0; +} + +int +pim_static_write_mroute (struct vty *vty, struct interface *ifp) +{ + struct listnode *node; + struct static_route *sroute; + int count = 0; + char sbuf[100]; + char gbuf[100]; + + for (ALL_LIST_ELEMENTS_RO (qpim_static_route_list, node, sroute)) + { + pim_inet4_dump ("", sroute->group, gbuf, sizeof (gbuf)); + pim_inet4_dump ("", sroute->source, sbuf, sizeof (sbuf)); + if (sroute->iif == ifp->ifindex) + { + int i; + for (i = 0; i < MAXVIFS; i++) + if (sroute->oif_ttls[i]) + { + struct interface *oifp = if_lookup_by_index (i); + vty_out (vty, " ip mroute %s %s %s%s", oifp->name, gbuf, sbuf, VTY_NEWLINE); + count ++; + } + } + } + + return count; +} diff --git a/pimd/pim_static.h b/pimd/pim_static.h new file mode 100644 index 000000000..b3be09e91 --- /dev/null +++ b/pimd/pim_static.h @@ -0,0 +1,48 @@ +/* + PIM for Quagga: add the ability to configure multicast static routes + Copyright (C) 2014 Nathan Bahr, ATCorp + + 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_STATIC_H_ +#define PIM_STATIC_H_ + +#include +#include "pim_mroute.h" +#include "if.h" + +struct static_route { + /* Each static route is unique by these pair of addresses */ + struct in_addr group; + struct in_addr source; + + unsigned int iif; + unsigned char oif_ttls[MAXVIFS]; + int oif_count; + struct mfcctl mc; + time_t creation[MAXVIFS]; +}; + +void pim_static_route_free(struct static_route *s_route); + +int pim_static_add(struct interface *iif, struct interface *oif, struct in_addr group, struct in_addr source); +int pim_static_del(struct interface *iif, struct interface *oif, struct in_addr group, struct in_addr source); +int pim_static_write_mroute (struct vty *vty, struct interface *ifp); + +#endif /* PIM_STATIC_H_ */ diff --git a/pimd/pim_str.c b/pimd/pim_str.c new file mode 100644 index 000000000..3a8353cd8 --- /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 000000000..925f17f73 --- /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 000000000..4e5832cc6 --- /dev/null +++ b/pimd/pim_time.c @@ -0,0 +1,166 @@ +/* + 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 000000000..2984d9a8d --- /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 000000000..ed4dbba2e --- /dev/null +++ b/pimd/pim_tlv.c @@ -0,0 +1,705 @@ +/* + 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) + return NULL; + + *(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) + return NULL; + + *(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) + return NULL; + + *(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) + 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_debug("%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 000000000..b802cf897 --- /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 000000000..a4d274ab5 --- /dev/null +++ b/pimd/pim_upstream.c @@ -0,0 +1,688 @@ +/* + 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; + enum pim_rpf_result rpf_result; + + 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; + + rpf_result = pim_rpf_update(up, 0); + if (rpf_result == PIM_RPF_FAILURE) { + XFREE(MTYPE_PIM_UPSTREAM, up); + return NULL; + } + + 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 000000000..5b5182dd4 --- /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 000000000..fdfed2bf2 --- /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 000000000..a8613e2b9 --- /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 000000000..f3a5ee335 --- /dev/null +++ b/pimd/pim_version.c @@ -0,0 +1,27 @@ +/* + 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 "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 000000000..ef9f370c7 --- /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 000000000..56de83c8b --- /dev/null +++ b/pimd/pim_vty.c @@ -0,0 +1,203 @@ +/* + 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" +#include "pim_pim.h" +#include "pim_static.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 pim drpriority */ + if (pim_ifp->pim_dr_priority != PIM_DEFAULT_DR_PRIORITY) { + vty_out(vty, " ip pim drpriority %d%s", pim_ifp->pim_dr_priority, + VTY_NEWLINE); + ++writes; + } + + /* IF ip pim hello */ + if (pim_ifp->pim_hello_period != PIM_DEFAULT_HELLO_PERIOD) { + vty_out(vty, " ip pim hello %d", pim_ifp->pim_hello_period); + if (pim_ifp->pim_default_holdtime != -1) + vty_out(vty, " %d", pim_ifp->pim_default_holdtime); + vty_out(vty, "%s", VTY_NEWLINE); + } + + /* 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 */ + if (pim_ifp->igmp_default_query_interval != IGMP_GENERAL_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 */ + if (pim_ifp->igmp_query_max_response_time_dsec != IGMP_QUERY_MAX_RESPONSE_TIME_DSEC) + { + 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; + } + } + + writes += pim_static_write_mroute (vty, ifp); + } + 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 000000000..904ee5530 --- /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 000000000..efff10009 --- /dev/null +++ b/pimd/pim_zebra.c @@ -0,0 +1,1300 @@ +/* + 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); + +/* Router-id update message from zebra. */ +static int pim_router_id_update_zebra(int command, struct zclient *zclient, + zebra_size_t length, vrf_id_t vrf_id) +{ + 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, vrf_id_t vrf_id) +{ + 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, vrf_id); + 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, vrf_id_t vrf_id) +{ + 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, vrf_id); + 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, vrf_id_t vrf_id) +{ + 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, vrf_id); + 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() 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, vrf_id_t vrf_id) +{ + 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, vrf_id); + 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() 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, vrf_id_t vrf_id) +{ + struct connected *c; + struct prefix *p; + + /* + 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, vrf_id); + 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) { + if (PIM_DEBUG_ZEBRA) { + /* 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, vrf_id_t vrf_id) +{ + struct connected *c; + struct prefix *p; + + /* + 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, vrf_id); + 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 pim_rpf old_rpf; + enum pim_rpf_result rpf_result; + + rpf_result = pim_rpf_update(up, &old_rpf); + 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(old_rpf.source_nexthop.interface, + old_rpf.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, vrf_id_t vrf_id) +{ + struct stream *s; + struct zapi_ipv4 api; + ifindex_t 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; + + if (CHECK_FLAG (api.message, ZAPI_MESSAGE_TAG)) + api.tag = stream_getl (s); + else + api.tag = 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 %d 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 %d 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; +} + +static void pim_zebra_connected(struct zclient *zclient) +{ + zclient_send_requests(zclient, VRF_DEFAULT); +} + +void pim_zebra_init (struct thread_master *master, 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 (master); + + qpim_zclient_update->zebra_connected = pim_zebra_connected; + 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; + vrf_bitmap_set(qpim_zclient_update->redist[i], VRF_DEFAULT); + if (PIM_DEBUG_PIM_TRACE) { + zlog_debug("%s: requesting redistribution for %s (%i)", + __PRETTY_FUNCTION__, zebra_route_string(i), i); + } + } + + /* Request default information */ + vrf_bitmap_set(qpim_zclient_update->default_information, VRF_DEFAULT); + 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; + ifindex_t 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_info("%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 000000000..af5baef28 --- /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 (struct thread_master *master, 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 000000000..770fbf742 --- /dev/null +++ b/pimd/pim_zlookup.c @@ -0,0 +1,440 @@ +/* + 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 (master); + 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 = 10; /* getipv4=4 getc=1 getl=4 getc=1 */ + uint16_t length; + u_char marker; + u_char version; + uint16_t vrf_id; + uint16_t command; + struct in_addr raddr; + uint8_t distance; + uint32_t metric; + int nexthop_num; + int i, err; + + 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); + + err = zclient_read_header (s, zlookup->sock, &length, &marker, &version, + &vrf_id, &command); + if (err < 0) { + zlog_err("%s %s: zclient_read_header() failed", + __FILE__, __PRETTY_FUNCTION__); + zclient_lookup_failed(zlookup); + return -1; + } + + if (length < MIN_LEN) { + zlog_err("%s %s: failure reading zclient lookup socket: len=%d < MIN_LEN=%d", + __FILE__, __PRETTY_FUNCTION__, length, MIN_LEN); + zclient_lookup_failed(zlookup); + return -2; + } + + 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; + } + + length -= MIN_LEN; + + for (i = 0; i < nexthop_num; ++i) { + enum nexthop_types_t nexthop_type; + + if (length < 1) { + zlog_err("%s: socket %d empty input expecting nexthop_type: len=%d", + __func__, zlookup->sock, length); + return -7; + } + + nexthop_type = stream_getc(s); + --length; + + 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 (length < 4) { + zlog_err("%s: socket %d short input expecting nexthop IPv4-addr: len=%d", + __func__, zlookup->sock, length); + return -8; + } + nexthop_tab[num_ifindex].nexthop_addr.s_addr = stream_get_ipv4(s); + length -= 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); + length -= 4; + nexthop_tab[num_ifindex].ifindex = 0; + nexthop_tab[num_ifindex].protocol_distance = distance; + nexthop_tab[num_ifindex].route_metric = metric; + if (PIM_DEBUG_ZEBRA) { + 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_debug("%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, VRF_DEFAULT); + 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) && PIM_DEBUG_ZEBRA) { + 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) && PIM_DEBUG_ZEBRA) { + /* Report non-recursive success after first lookup */ + char addr_str[100]; + pim_inet4_dump("", addr, addr_str, sizeof(addr_str)); + zlog_debug("%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; + } + + if (PIM_DEBUG_ZEBRA) { + 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_debug("%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) */ + + if (PIM_DEBUG_ZEBRA) { + 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 000000000..f2be6d4fa --- /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; + ifindex_t 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 000000000..97fb22331 --- /dev/null +++ b/pimd/pimd.c @@ -0,0 +1,154 @@ +/* + 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 "vrf.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_rpf.h" +#include "pim_ssmpingd.h" +#include "pim_static.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; +struct list *qpim_static_route_list = 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); + + if (qpim_static_route_list) + list_free(qpim_static_route_list); +} + +void pim_init() +{ + srandom(time(NULL)); + + 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_static_route_list = list_new(); + if (!qpim_static_route_list) { + zlog_err("%s %s: failure: static_route_list=list_new()", + __FILE__, __PRETTY_FUNCTION__); + return; + } + qpim_static_route_list->del = (void (*)(void *)) pim_static_route_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_cmd_init(); + pim_ssmpingd_init(); +} + +void pim_terminate() +{ + vrf_terminate(); + pim_free(); +} diff --git a/pimd/pimd.conf.sample b/pimd/pimd.conf.sample new file mode 100644 index 000000000..67530856b --- /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 000000000..9a7e60583 --- /dev/null +++ b/pimd/pimd.h @@ -0,0 +1,164 @@ +/* + 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) +#define PIM_MASK_STATIC (1 << 13) + +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; +struct list *qpim_static_route_list; /* list of routes added statically */ + +#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_STATIC (qpim_debugs & PIM_MASK_STATIC) + +#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_DO_DEBUG_STATIC (qpim_debugs |= PIM_MASK_STATIC) + +#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) +#define PIM_DONT_DEBUG_STATIC (qpim_debugs &= ~PIM_MASK_STATIC) + +void pim_init(void); +void pim_terminate(void); + +extern void pim_route_map_init (void); + +#endif /* PIMD_H */ diff --git a/pimd/test_igmpv3_join.c b/pimd/test_igmpv3_join.c new file mode 100644 index 000000000..81026aecc --- /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; + ifindex_t 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; + ifindex_t 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 d085d06ec..86f77bd00 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 0c8d01b76..aeb1950e2 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/qpb/.gitignore b/qpb/.gitignore new file mode 100644 index 000000000..b133c52a4 --- /dev/null +++ b/qpb/.gitignore @@ -0,0 +1,15 @@ +Makefile +Makefile.in +*.o +tags +TAGS +.deps +.nfs* +*.lo +*.la +*.a +*.libs +.arch-inventory +.arch-ids +*~ +*.loT diff --git a/qpb/Makefile.am b/qpb/Makefile.am new file mode 100644 index 000000000..0fbda61f3 --- /dev/null +++ b/qpb/Makefile.am @@ -0,0 +1,30 @@ +include ../common.am + +AM_CPPFLAGS = -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib $(Q_PROTOBUF_C_CLIENT_INCLUDES) + +PROTOBUF_INCLUDES=-I$(top_srcdir) +PROTOBUF_PACKAGE = qpb + +lib_LTLIBRARIES = libquagga_pb.la +libquagga_pb_la_LDFLAGS = -version-info 0:0:0 + +if HAVE_PROTOBUF +protobuf_srcs = \ + qpb_allocator.c + +protobuf_srcs_nodist = \ + qpb.pb-c.c +endif + +libquagga_pb_la_SOURCES = \ + linear_allocator.h \ + qpb.h \ + qpb.c \ + qpb_allocator.h \ + $(protobuf_srcs) + +nodist_libquagga_pb_la_SOURCES = $(protobuf_srcs_nodist) + +CLEANFILES = $(Q_CLEANFILES) +BUILT_SOURCES = $(Q_PROTOBUF_SRCS) +EXTRA_DIST = qpb.proto diff --git a/qpb/README.txt b/qpb/README.txt new file mode 100644 index 000000000..99ccd0551 --- /dev/null +++ b/qpb/README.txt @@ -0,0 +1 @@ +Protobuf definitions and code that is applicable to all of quagga. diff --git a/qpb/linear_allocator.h b/qpb/linear_allocator.h new file mode 100644 index 000000000..e3ebbc64f --- /dev/null +++ b/qpb/linear_allocator.h @@ -0,0 +1,207 @@ +/* + * linear_allocator.h + * + * @copyright Copyright (C) 2016 Sproute Networks, Inc. + * + * @author Avneesh Sachdev + * + * 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. + */ + +/* + * Header file for the linear allocator. + * + * An allocator that allocates memory by walking down towards the end + * of a buffer. No attempt is made to reuse blocks that are freed + * subsequently. The assumption is that the buffer is big enough to + * cover allocations for a given purpose. + */ +#include +#include +#include +#include + +/* + * Alignment for block allocated by the allocator. Must be a power of 2. + */ +#define LINEAR_ALLOCATOR_ALIGNMENT 8 + +#define LINEAR_ALLOCATOR_ALIGN(value) \ + (((value) + LINEAR_ALLOCATOR_ALIGNMENT - 1) & ~(LINEAR_ALLOCATOR_ALIGNMENT - 1)); + +/* + * linear_allocator_align_ptr + */ +static inline char * +linear_allocator_align_ptr (char *ptr) +{ + return (char *) LINEAR_ALLOCATOR_ALIGN ((intptr_t) ptr); +} + +typedef struct linear_allocator_t_ +{ + char *buf; + + /* + * Current location in the buffer. + */ + char *cur; + + /* + * End of buffer. + */ + char *end; + + /* + * Version number of the allocator, this is bumped up when the allocator + * is reset and helps identifies bad frees. + */ + uint32_t version; + + /* + * The number of blocks that are currently allocated. + */ + int num_allocated; +} linear_allocator_t; + +/* + * linear_allocator_block_t + * + * Header structure at the begining of each block. + */ +typedef struct linear_allocator_block_t_ +{ + uint32_t flags; + + /* + * The version of the allocator when this block was allocated. + */ + uint32_t version; + char data[0]; +} linear_allocator_block_t; + +#define LINEAR_ALLOCATOR_BLOCK_IN_USE 0x01 + +#define LINEAR_ALLOCATOR_HDR_SIZE (sizeof(linear_allocator_block_t)) + +/* + * linear_allocator_block_size + * + * The total amount of space a block will take in the buffer, + * including the size of the header. + */ +static inline size_t +linear_allocator_block_size (size_t user_size) +{ + return LINEAR_ALLOCATOR_ALIGN (LINEAR_ALLOCATOR_HDR_SIZE + user_size); +} + +/* + * linear_allocator_ptr_to_block + */ +static inline linear_allocator_block_t * +linear_allocator_ptr_to_block (void *ptr) +{ + void *block_ptr; + block_ptr = ((char *) ptr) - offsetof (linear_allocator_block_t, data); + return block_ptr; +} + +/* + * linear_allocator_init + */ +static inline void +linear_allocator_init (linear_allocator_t * allocator, char *buf, + size_t buf_len) +{ + memset (allocator, 0, sizeof (*allocator)); + + assert (linear_allocator_align_ptr (buf) == buf); + allocator->buf = buf; + allocator->cur = buf; + allocator->end = buf + buf_len; +} + +/* + * linear_allocator_reset + * + * Prepare an allocator for reuse. + * + * *** NOTE ** This implicitly frees all the blocks in the allocator. + */ +static inline void +linear_allocator_reset (linear_allocator_t *allocator) +{ + allocator->num_allocated = 0; + allocator->version++; + allocator->cur = allocator->buf; +} + +/* + * linear_allocator_alloc + */ +static inline void * +linear_allocator_alloc (linear_allocator_t *allocator, size_t user_size) +{ + size_t block_size; + linear_allocator_block_t *block; + + block_size = linear_allocator_block_size (user_size); + + if (allocator->cur + block_size > allocator->end) + { + return NULL; + } + + block = (linear_allocator_block_t *) allocator->cur; + allocator->cur += block_size; + + block->flags = LINEAR_ALLOCATOR_BLOCK_IN_USE; + block->version = allocator->version; + allocator->num_allocated++; + return block->data; +} + +/* + * linear_allocator_free + */ +static inline void +linear_allocator_free (linear_allocator_t *allocator, void *ptr) +{ + linear_allocator_block_t *block; + + if (((char *) ptr) < allocator->buf || ((char *) ptr) >= allocator->end) + { + assert (0); + return; + } + + block = linear_allocator_ptr_to_block (ptr); + if (block->version != allocator->version) + { + assert (0); + return; + } + + block->flags = block->flags & ~LINEAR_ALLOCATOR_BLOCK_IN_USE; + + if (--allocator->num_allocated < 0) + { + assert (0); + } +} diff --git a/qpb/qpb.c b/qpb/qpb.c new file mode 100644 index 000000000..1b2b47fce --- /dev/null +++ b/qpb/qpb.c @@ -0,0 +1,29 @@ +/* + * qpb.c + * + * @copyright Copyright (C) 2016 Sproute Networks, Inc. + * + * @author Avneesh Sachdev + * + * 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. + */ + +/* + * Main file for the qpb library. + */ + diff --git a/qpb/qpb.h b/qpb/qpb.h new file mode 100644 index 000000000..55c1deb19 --- /dev/null +++ b/qpb/qpb.h @@ -0,0 +1,372 @@ +/* + * qpb.h + * + * @copyright Copyright (C) 2016 Sproute Networks, Inc. + * + * @author Avneesh Sachdev + * + * 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. + */ + +/* + * Main public header file for the quagga protobuf library. + */ + +#ifndef _QPB_H +#define _QPB_H + +#include "prefix.h" + +#include "qpb/qpb.pb-c.h" + +#include "qpb/qpb_allocator.h" + +/* + * qpb__address_family__set + */ +#define qpb_address_family_set qpb__address_family__set +static inline int +qpb__address_family__set (Qpb__AddressFamily *pb_family, u_char family) +{ + switch (family) { + case AF_INET: + *pb_family = QPB__ADDRESS_FAMILY__IPV4; + return 1; + + case AF_INET6: + *pb_family = QPB__ADDRESS_FAMILY__IPV6; + return 1; + + default: + *pb_family = QPB__ADDRESS_FAMILY__UNKNOWN_AF; + } + + return 0; +} + +/* + * qpb__address_family__get + */ +#define qpb_address_family_get qpb__address_family__get +static inline int +qpb__address_family__get (Qpb__AddressFamily pb_family, u_char *family) +{ + + switch (pb_family) { + case QPB__ADDRESS_FAMILY__IPV4: + *family = AF_INET; + return 1; + + case QPB__ADDRESS_FAMILY__IPV6: + *family = AF_INET6; + return 1; + + case QPB__ADDRESS_FAMILY__UNKNOWN_AF: + return 0; + } + + return 0; +} + +/* + * qpb__l3_prefix__create + */ +#define qpb_l3_prefix_create qpb__l3_prefix__create +static inline Qpb__L3Prefix * +qpb__l3_prefix__create (qpb_allocator_t *allocator, struct prefix *p) +{ + Qpb__L3Prefix *prefix; + + prefix = QPB_ALLOC(allocator, typeof(*prefix)); + if (!prefix) { + return NULL; + } + qpb__l3_prefix__init(prefix); + prefix->length = p->prefixlen; + prefix->bytes.len = (p->prefixlen + 7)/8; + prefix->bytes.data = qpb_alloc(allocator, prefix->bytes.len); + if (!prefix->bytes.data) { + return NULL; + } + + memcpy(prefix->bytes.data, &p->u.prefix, prefix->bytes.len); + + return prefix; +} + +/* + * qpb__l3_prefix__get + */ +#define qpb_l3_prefix_get qpb__l3_prefix__get +static inline int +qpb__l3_prefix__get (const Qpb__L3Prefix *pb_prefix, u_char family, + struct prefix *prefix) +{ + + switch (family) + { + + case AF_INET: + memset(prefix, 0, sizeof(struct prefix_ipv4)); + break; + + case AF_INET6: + memset(prefix, 0, sizeof(struct prefix_ipv6)); + break; + + default: + memset(prefix, 0, sizeof(*prefix)); + } + + prefix->prefixlen = pb_prefix->length; + prefix->family = family; + memcpy(&prefix->u.prefix, pb_prefix->bytes.data, pb_prefix->bytes.len); + return 1; +} + +/* + * qpb__protocol__set + * + * Translate a quagga route type to a protobuf protocol. + */ +#define qpb_protocol_set qpb__protocol__set +static inline int +qpb__protocol__set (Qpb__Protocol *pb_proto, int route_type) +{ + switch (route_type) { + case ZEBRA_ROUTE_KERNEL: + *pb_proto = QPB__PROTOCOL__KERNEL; + break; + + case ZEBRA_ROUTE_CONNECT: + *pb_proto = QPB__PROTOCOL__CONNECTED; + break; + + case ZEBRA_ROUTE_STATIC: + *pb_proto = QPB__PROTOCOL__STATIC; + break; + + case ZEBRA_ROUTE_RIP: + *pb_proto = QPB__PROTOCOL__RIP; + break; + + case ZEBRA_ROUTE_RIPNG: + *pb_proto = QPB__PROTOCOL__RIPNG; + break; + + case ZEBRA_ROUTE_OSPF: + case ZEBRA_ROUTE_OSPF6: + *pb_proto = QPB__PROTOCOL__OSPF; + break; + + case ZEBRA_ROUTE_ISIS: + *pb_proto = QPB__PROTOCOL__ISIS; + break; + + case ZEBRA_ROUTE_BGP: + *pb_proto = QPB__PROTOCOL__BGP; + break; + + case ZEBRA_ROUTE_HSLS: + case ZEBRA_ROUTE_OLSR: + case ZEBRA_ROUTE_BABEL: + case ZEBRA_ROUTE_MAX: + case ZEBRA_ROUTE_SYSTEM: + default: + *pb_proto = QPB__PROTOCOL__OTHER; + } + + return 1; +} + +/* + * qpb__ipv4_address__create + */ +static inline Qpb__Ipv4Address * +qpb__ipv4_address__create (qpb_allocator_t *allocator, + struct in_addr *addr) +{ + Qpb__Ipv4Address *v4; + + v4 = QPB_ALLOC(allocator, typeof(*v4)); + if (!v4) { + return NULL; + } + qpb__ipv4_address__init(v4); + + v4->value = ntohl(addr->s_addr); + return v4; +} + +/* + * qpb__ipv4_address__get + */ +static inline int +qpb__ipv4_address__get (const Qpb__Ipv4Address *v4, struct in_addr *addr) +{ + addr->s_addr = htonl(v4->value); + return 1; +} + +/* + * qpb__ipv6_address__create + */ +static inline Qpb__Ipv6Address * +qpb__ipv6_address__create (qpb_allocator_t *allocator, struct in6_addr *addr) +{ + Qpb__Ipv6Address *v6; + + v6 = QPB_ALLOC(allocator, typeof(*v6)); + if (!v6) + return NULL; + + qpb__ipv6_address__init(v6); + v6->bytes.len = 16; + v6->bytes.data = qpb_alloc(allocator, 16); + if (!v6->bytes.data) + return NULL; + + memcpy(v6->bytes.data, addr->s6_addr, v6->bytes.len); + return v6; +} + +/* + * qpb__ipv6_address__get + * + * Read out information from a protobuf ipv6 address structure. + */ +static inline int +qpb__ipv6_address__get (const Qpb__Ipv6Address *v6, struct in6_addr *addr) +{ + if (v6->bytes.len != 16) + return 0; + + memcpy(addr->s6_addr, v6->bytes.data, v6->bytes.len); + return 1; +} + +/* + * qpb__l3_address__create + */ +#define qpb_l3_address_create qpb__l3_address__create +static inline Qpb__L3Address * +qpb__l3_address__create (qpb_allocator_t *allocator, union g_addr *addr, + u_char family) +{ + Qpb__L3Address *l3_addr; + + l3_addr = QPB_ALLOC(allocator, typeof(*l3_addr)); + if (!l3_addr) + return NULL; + + qpb__l3_address__init(l3_addr); + + switch (family) { + + case AF_INET: + l3_addr->v4 = qpb__ipv4_address__create (allocator, &addr->ipv4); + if (!l3_addr->v4) + return NULL; + + break; + + case AF_INET6: + l3_addr->v6 = qpb__ipv6_address__create (allocator, &addr->ipv6); + if (!l3_addr->v6) + return NULL; + + break; + } + return l3_addr; +} + +/* + * qpb__l3_address__get + * + * Read out a gateway address from a protobuf l3 address. + */ +#define qpb_l3_address_get qpb__l3_address__get +static inline int +qpb__l3_address__get (const Qpb__L3Address *l3_addr, + u_char *family, union g_addr *addr) +{ + if (l3_addr->v4) + { + qpb__ipv4_address__get (l3_addr->v4, &addr->ipv4); + *family = AF_INET; + return 1; + } + + if (l3_addr->v6) + { + qpb__ipv6_address__get(l3_addr->v6, &addr->ipv6); + *family = AF_INET6; + return 1; + } + + return 0; +} + +/* + * qpb__if_identifier__create + */ +#define qpb_if_identifier_create qpb__if_identifier__create +static inline Qpb__IfIdentifier * +qpb__if_identifier__create (qpb_allocator_t *allocator, uint if_index) +{ + Qpb__IfIdentifier *if_id; + + if_id = QPB_ALLOC(allocator, typeof(*if_id)); + if (!if_id) { + return NULL; + } + qpb__if_identifier__init(if_id); + if_id->has_index = 1; + if_id->index = if_index; + return if_id; +} + +/* + * qpb__if_identifier__get + * + * Get interface name and/or if_index from an if identifier. + */ +#define qpb_if_identifier_get qpb__if_identifier__get +static inline int +qpb__if_identifier__get (Qpb__IfIdentifier *if_id, uint *if_index, + char **name) +{ + char *str; + uint ix; + + if (!if_index) + if_index = &ix; + + if (!name) + name = &str; + + if (if_id->has_index) + *if_index = if_id->index; + else + *if_index = 0; + + *name = if_id->name; + return 1; +} + +#endif diff --git a/qpb/qpb.proto b/qpb/qpb.proto new file mode 100644 index 000000000..8323d3ed7 --- /dev/null +++ b/qpb/qpb.proto @@ -0,0 +1,90 @@ +/* + * qpb.proto + * + * @copyright Copyright (C) 2016 Sproute Networks, Inc. + * + * @author Avneesh Sachdev + * + * 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. + */ + +/* + * Protobuf definitions pertaining to the Quagga Protobuf component. + */ +package qpb; + +enum AddressFamily { + UNKNOWN_AF = 0; + IPV4 = 1; // IP version 4 + IPV6 = 2; // IP version 6 +}; + +enum SubAddressFamily { + UNKNOWN_SAF = 0; + UNICAST = 1; + MULTICAST = 2; +}; + +// +// An IP version 4 address, such as 10.1.1.1. +// +message Ipv4Address { + required fixed32 value = 1 ; +}; + +message Ipv6Address { + + // 16 bytes. + required bytes bytes = 1; +}; + +// +// An IP version 4 or IP version 6 address. +// +message L3Address { + optional Ipv4Address v4 = 1; + optional Ipv6Address v6 = 2; +}; + +// +// An IP prefix, such as 10.1/16. +// We use the message below to represent both IPv4 and IPv6 prefixes. +message L3Prefix { + required uint32 length = 1; + required bytes bytes = 2; +}; + +// +// Something that identifies an interface on a machine. It can either +// be a name (for instance, 'eth0') or a number currently. +// +message IfIdentifier { + optional uint32 index = 1; + optional string name = 2; +}; + +enum Protocol { + UNKNOWN_PROTO = 0; + LOCAL = 1; + CONNECTED = 2; + KERNEL = 3; + STATIC = 4; + RIP = 5; + RIPNG = 6; + OSPF = 7; + ISIS = 8; + BGP = 9; + OTHER = 10; +} \ No newline at end of file diff --git a/qpb/qpb_allocator.c b/qpb/qpb_allocator.c new file mode 100644 index 000000000..4b4830a47 --- /dev/null +++ b/qpb/qpb_allocator.c @@ -0,0 +1,67 @@ +/* + * qpb_allocator.c + * + * @copyright Copyright (C) 2016 Sproute Networks, Inc. + * + * @author Avneesh Sachdev + * + * 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 "linear_allocator.h" + +#include "qpb_allocator.h" + +/* + * _qpb_alloc + */ +static void * +_qpb_alloc (void *allocator_data, size_t size) +{ + return linear_allocator_alloc (allocator_data, size); +} + +/* + * _qpb_free + */ +static void +_qpb_free (void *allocator_data, void *ptr) +{ + linear_allocator_free (allocator_data, ptr); +} + +static ProtobufCAllocator allocator_template = { + _qpb_alloc, + _qpb_free, + NULL, + 8192, + NULL +}; + +/* + * qpb_allocator_init_linear + * + * Initialize qpb_allocator_t with the given linear allocator. + */ +void +qpb_allocator_init_linear (qpb_allocator_t *allocator, + linear_allocator_t *linear_allocator) +{ + *allocator = allocator_template; + allocator->allocator_data = linear_allocator; +} diff --git a/qpb/qpb_allocator.h b/qpb/qpb_allocator.h new file mode 100644 index 000000000..83ddf56cb --- /dev/null +++ b/qpb/qpb_allocator.h @@ -0,0 +1,113 @@ +/* + * qpb_allocator.h + * + * @copyright Copyright (C) 2016 Sproute Networks, Inc. + * + * @author Avneesh Sachdev + * + * 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. + */ + +/* + * Header file for quagga protobuf memory management code. + */ + +#ifndef _QPB_ALLOCATOR_H_ +#define _QPB_ALLOCATOR_H_ + +#include + +struct linear_allocator_t_; + +/* + * Alias for ProtobufCAllocator that is easier on the fingers. + */ +typedef ProtobufCAllocator qpb_allocator_t; + +/* + * qpb_alloc + */ +static inline void * +qpb_alloc (qpb_allocator_t *allocator, size_t size) +{ + return allocator->alloc (allocator->allocator_data, size); +} + +/* + * qpb_alloc_ptr_array + * + * Allocate space for the specified number of pointers. + */ +static inline void * +qpb_alloc_ptr_array (qpb_allocator_t *allocator, size_t num_ptrs) +{ + return qpb_alloc (allocator, num_ptrs * sizeof (void *)); +} + +/* + * qpb_free + */ +static inline void +qpb_free (qpb_allocator_t *allocator, void *ptr) +{ + allocator->free (allocator->allocator_data, ptr); +} + +/* + * QPB_ALLOC + * + * Convenience macro to reduce the probability of allocating memory of + * incorrect size. It returns enough memory to store the given type, + * and evaluates to an appropriately typed pointer. + */ +#define QPB_ALLOC(allocator, type) \ + (type *) qpb_alloc(allocator, sizeof(type)) + + +/* + * Externs. + */ +extern void qpb_allocator_init_linear (qpb_allocator_t *, + struct linear_allocator_t_ *); + +/* + * The following macros are for the common case where a qpb allocator + * is being used alongside a linear allocator that allocates memory + * off of the stack. + */ +#define QPB_DECLARE_STACK_ALLOCATOR(allocator, size) \ + qpb_allocator_t allocator; \ + linear_allocator_t lin_ ## allocator; \ + char lin_ ## allocator ## _buf[size] + +#define QPB_INIT_STACK_ALLOCATOR(allocator) \ + do \ + { \ + linear_allocator_init(&(lin_ ## allocator), \ + lin_ ## allocator ## _buf, \ + sizeof(lin_ ## allocator ## _buf)); \ + qpb_allocator_init_linear(&allocator, &(lin_ ## allocator)); \ + } while (0) + +#define QPB_RESET_STACK_ALLOCATOR(allocator) \ + do \ + { \ + linear_allocator_reset (&(lin_ ## allocator)); \ + } while (0) + +#endif /* _QPB_ALLOCATOR_H_ */ diff --git a/redhat/Makefile.am b/redhat/Makefile.am index 6a3d38f70..b1d49ac3b 100644 --- a/redhat/Makefile.am +++ b/redhat/Makefile.am @@ -1,4 +1,9 @@ -EXTRA_DIST = quagga.pam quagga.sysconfig quagga.spec quagga.logrotate \ - zebra.init ripd.init ospfd.init ripngd.init ospf6d.init bgpd.init \ - isisd.init watchquagga.init quagga.pam.stack +EXTRA_DIST = bgpd.init bgpd.service isisd.init \ + isisd.service ospf6d.init ospf6d.service ospfd.init ospfd.service \ + quagga.logrotate quagga.pam quagga.spec \ + quagga.sysconfig ripd.init ripd.service ripngd.init ripngd.service \ + watchquagga.init pimd.init pimd.service zebra.init zebra.service \ + nhrpd.service \ + quagga-filter-perl-requires.sh quagga-tmpfs.conf \ + README.rpm_build.md diff --git a/redhat/README.rpm_build.md b/redhat/README.rpm_build.md new file mode 100644 index 000000000..3e8fa0530 --- /dev/null +++ b/redhat/README.rpm_build.md @@ -0,0 +1,128 @@ +Building your own Quagga RPM +============================ +(Tested on CentOS 6, CentOS 7 and Fedora 22.) + +1. Install the following packages to build the RPMs: + + yum install git autoconf automake libtool make gawk readline-devel \ + texinfo dejagnu net-snmp-devel groff rpm-build net-snmp-devel \ + libcap-devel texi2html + + (use `dnf install` on new Fedora instead of `yum install `) + +2. Checkout Quagga under a **unpriviledged** user account + + git clone git://git.savannah.nongnu.org/quagga.git quagga + +3. Run Bootstrap and make distribution tar.gz + + cd quagga + ./bootstrap.sh + ./configure --with-pkg-extra-version=-MyRPMVersion + make dist + + Note: configure parameters are not important for the RPM building - except the + `with-pkg-extra-version` if you want to give the RPM a specific name to + mark your own unoffical build + +4. Create RPM directory structure and populate with sources + + mkdir rpmbuild + mkdir rpmbuild/SOURCES + mkdir rpmbuild/SPECS + cp redhat/*.spec rpmbuild/SPECS/ + cp quagga*.tar.gz rpmbuild/SOURCES/ + +5. Edit rpm/SPECS/quagga.spec with configuration as needed + Look at the beginning of the file and adjust the following parameters to enable + or disable features as required: + + ################# Quagga configure options #################### + # with-feature options + %{!?with_snmp: %global with_snmp 1 } + %{!?with_vtysh: %global with_vtysh 1 } + %{!?with_ospf_te: %global with_ospf_te 1 } + %{!?with_opaque_lsa: %global with_opaque_lsa 1 } + %{!?with_tcp_zebra: %global with_tcp_zebra 0 } + %{!?with_vtysh: %global with_vtysh 1 } + %{!?with_pam: %global with_pam 1 } + %{!?with_ospfclient: %global with_ospfclient 1 } + %{!?with_ospfapi: %global with_ospfapi 1 } + %{!?with_irdp: %global with_irdp 1 } + %{!?with_rtadv: %global with_rtadv 1 } + %{!?with_isisd: %global with_isisd 1 } + %{!?with_pimd: %global with_pimd 1 } + %{!?with_shared: %global with_shared 1 } + %{!?with_multipath: %global with_multipath 64 } + %{!?quagga_user: %global quagga_user quagga } + %{!?vty_group: %global vty_group quaggavt } + %{!?with_fpm: %global with_fpm 0 } + %{!?with_watchquagga: %global with_watchquagga 1 } + +6. Build the RPM + + rpmbuild --define "_topdir `pwd`/rpmbuild" -ba rpmbuild/SPECS/quagga.spec + +DONE. + +If all works correctly, then you should end up with the RPMs under `rpmbuild/RPMS` +and the Source RPM under `rpmbuild/SRPMS` + + +Enabling daemons after installation of the package: +--------------------------------------------------- + +### init.d based systems (ie CentOS 6): + +1. Enable the daemons as needed to run after boot (Zebra is mandatory) + + chkconfig zebra on + chkconfig ospfd on + chkconfig ospf6d on + chkconfig bgpd on + ... etc + +2. If you want to run `watchquagga`, then configure `/etc/sysconfig/quagga` + and uncomment the line with the daemons for `watchquagga` to monitor, + then enable watchquagga + + chkconfig watchquagga on + +3. Check your firewall / IPtables to make sure the routing protocols are +allowed. + +4. Start the daemons (or reboot) + + service zebra start + service bgpd start + service ospfd start + ... etc + +Configuration is stored in `/etc/quagga/*.conf` files. + + +### systemd based systems (ie CentOS 7, Fedora 22) + +1. Enable the daemons as needed to run after boot (Zebra is mandatory) + + systemctl enable zebra + systemctl enable ospfd + systemctl enable ospf6d + systemctl enable bgpd + ... etc + + Note: There is no watchquagga on systemd based systems. Systemd contains + the functionality of monitoring and restarting daemons. + +2. Check your firewall / IPtables to make sure the routing protocols are +allowed. + +3. Start the daemons (or reboot) + + systemctl start zebra + systemctl start bgpd + systemctl start ospfd + ... etc + +Configuration is stored in `/etc/quagga/*.conf` files. + diff --git a/redhat/bgpd.init b/redhat/bgpd.init index ef59c2a61..e18511a03 100644 --- a/redhat/bgpd.init +++ b/redhat/bgpd.init @@ -1,10 +1,12 @@ #!/bin/bash -# -# chkconfig: 2345 16 84 -# description: A BGPv4, BGPv4+, BGPv4- routing engine for use with Zebra -# -# processname: bgpd -# config: /etc/zebra/bgpd.conf +# chkconfig: - 16 84 +# config: /etc/quagga/bgpd.conf + +### BEGIN INIT INFO +# Provides: bgpd +# Short-Description: BGP routing engine +# Description: BGP routing engine for use with Zebra +### END INIT INFO # source function library . /etc/rc.d/init.d/functions @@ -15,49 +17,56 @@ # quagga command line options . /etc/sysconfig/quagga -# Check that networking is up. -[ "${NETWORKING}" = "no" ] && exit 0 - -# The process must be configured first. -[ -f /etc/quagga/bgpd.conf ] || exit 0 - RETVAL=0 -prog="bgpd" +PROG="bgpd" +cmd=bgpd +LOCK_FILE=/var/lock/subsys/bgpd +CONF_FILE=/etc/quagga/bgpd.conf case "$1" in start) - echo -n $"Starting $prog: " - daemon /usr/sbin/bgpd -d $BGPD_OPTS + # Check that networking is up. + [ "${NETWORKING}" = "no" ] && exit 1 + + # The process must be configured first. + [ -f $CONF_FILE ] || exit 6 + if [ `id -u` -ne 0 ]; then + echo $"Insufficient privilege" 1>&2 + exit 4 + fi + + echo -n $"Starting $PROG: " + daemon $cmd -d $BGPD_OPTS -f $CONF_FILE RETVAL=$? - [ $RETVAL -eq 0 ] && touch /var/lock/subsys/bgpd + [ $RETVAL -eq 0 ] && touch $LOCK_FILE echo ;; stop) - echo -n $"Shutting down $prog: " - killproc bgpd + echo -n $"Shutting down $PROG: " + killproc $cmd RETVAL=$? - [ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/bgpd + [ $RETVAL -eq 0 ] && rm -f $LOCK_FILE echo ;; - restart|reload) - $0 stop - $0 start + restart|reload|force-reload) + $0 stop + $0 start RETVAL=$? - ;; - condrestart) - if [ -f /var/lock/subsys/bgpd ]; then - $0 stop + ;; + condrestart|try-restart) + if [ -f $LOCK_FILE ]; then + $0 stop $0 start - fi + fi RETVAL=$? - ;; + ;; status) - status bgpd + status $cmd RETVAL=$? - ;; + ;; *) - echo $"Usage: $0 {start|stop|restart|reload|condrestart|status}" - exit 1 + echo $"Usage: $0 {start|stop|restart|reload|force-reload|condrestart|try-restart|status}" + exit 2 esac exit $RETVAL diff --git a/redhat/bgpd.service b/redhat/bgpd.service new file mode 100644 index 000000000..ef2484162 --- /dev/null +++ b/redhat/bgpd.service @@ -0,0 +1,16 @@ +[Unit] +Description=BGP routing daemon +BindsTo=zebra.service +Wants=network.target +After=zebra.service network-pre.target +Before=network.target +ConditionPathExists=/etc/quagga/bgpd.conf + +[Service] +Type=forking +EnvironmentFile=/etc/sysconfig/quagga +ExecStart=/usr/sbin/bgpd -d $BGPD_OPTS -f /etc/quagga/bgpd.conf +Restart=on-abort + +[Install] +WantedBy=multi-user.target diff --git a/redhat/isisd.init b/redhat/isisd.init index 0d762c043..9e805300d 100644 --- a/redhat/isisd.init +++ b/redhat/isisd.init @@ -1,61 +1,72 @@ #!/bin/bash -# -# chkconfig: 2345 16 84 -# description: An ISIS routing engine for use with Quagga -# -# processname: isisd +# chkconfig: - 16 84 # config: /etc/quagga/isisd.conf +### BEGIN INIT INFO +# Provides: isisd +# Short-Description: IS-IS routing engine +# Description: IS-IS routing engine for use with Zebra +### END INIT INFO + # source function library . /etc/rc.d/init.d/functions # Get network config . /etc/sysconfig/network -# Check that networking is up. -[ "${NETWORKING}" = "no" ] && exit 0 - -# The process must be configured first. -[ -f /etc/quagga/isisd.conf ] || exit 0 +# quagga command line options +. /etc/sysconfig/quagga RETVAL=0 - -prog="isisd" +PROG="isisd" +cmd=isisd +LOCK_FILE=/var/lock/subsys/isisd +CONF_FILE=/etc/quagga/isisd.conf case "$1" in start) - echo -n $"Starting $prog: " - daemon /usr/sbin/isisd -d + # Check that networking is up. + [ "${NETWORKING}" = "no" ] && exit 1 + + # The process must be configured first. + [ -f $CONF_FILE ] || exit 6 + if [ `id -u` -ne 0 ]; then + echo $"Insufficient privilege" 1>&2 + exit 4 + fi + + echo -n $"Starting $PROG: " + daemon $cmd -d $ISISD_OPTS -f $CONF_FILE RETVAL=$? - [ $RETVAL -eq 0 ] && touch /var/lock/subsys/isisd + [ $RETVAL -eq 0 ] && touch $LOCK_FILE echo ;; stop) - echo -n $"Shutting down $prog: " - killproc isisd + echo -n $"Shutting down $PROG: " + killproc $cmd RETVAL=$? - [ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/isisd + [ $RETVAL -eq 0 ] && rm -f $LOCK_FILE echo ;; - restart|reload) - $0 stop - $0 start + restart|reload|force-reload) + $0 stop + $0 start RETVAL=$? - ;; - condrestart) - if [ -f /var/lock/subsys/isisd ]; then - $0 stop + ;; + condrestart|try-restart) + if [ -f $LOCK_FILE ]; then + $0 stop $0 start - fi + fi RETVAL=$? - ;; + ;; status) - status isisd + status $cmd RETVAL=$? - ;; + ;; *) - echo $"Usage: $0 {start|stop|restart|reload|condrestart|status}" - exit 1 + echo $"Usage: $0 {start|stop|restart|reload|force-reload|condrestart|try-restart|status}" + exit 2 esac exit $RETVAL diff --git a/redhat/isisd.service b/redhat/isisd.service new file mode 100644 index 000000000..edb6eea5f --- /dev/null +++ b/redhat/isisd.service @@ -0,0 +1,16 @@ +[Unit] +Description=IS-IS routing daemon +BindsTo=zebra.service +Wants=network.target +After=zebra.service network-pre.target +Before=network.target +ConditionPathExists=/etc/quagga/isisd.conf + +[Service] +Type=forking +EnvironmentFile=/etc/sysconfig/quagga +ExecStart=/usr/sbin/isisd -d $ISISD_OPTS -f /etc/quagga/isisd.conf +Restart=on-abort + +[Install] +WantedBy=multi-user.target diff --git a/redhat/nhrpd.service b/redhat/nhrpd.service new file mode 100644 index 000000000..63f138cd8 --- /dev/null +++ b/redhat/nhrpd.service @@ -0,0 +1,16 @@ +[Unit] +Description=Quagga NHRP daemon +BindsTo=zebra.service +Wants=network.target +After=zebra.service network-pre.target +Before=network.target +ConditionPathExists=/etc/quagga/nhrpd.conf + +[Service] +Type=forking +EnvironmentFile=/etc/sysconfig/quagga +ExecStart=/usr/sbin/nhrpd -d $NHRPD_OPTS -f /etc/quagga/nhrpdd.conf +Restart=on-abort + +[Install] +WantedBy=multi-user.target diff --git a/redhat/ospf6d.init b/redhat/ospf6d.init index c84ce678f..4133b4a14 100644 --- a/redhat/ospf6d.init +++ b/redhat/ospf6d.init @@ -1,11 +1,13 @@ #!/bin/bash -# -# chkconfig: 2345 16 84 -# description: An OSPF routing engine for use with Zebra and IPv6 -# -# processname: ospf6d +# chkconfig: - 16 84 # config: /etc/quagga/ospf6d.conf +### BEGIN INIT INFO +# Provides: ospf6d +# Short-Description: OSPF routing engine for IPv6 +# Description: OSPF routing engine for use with Zebra and IPv6 +### END INIT INFO + # source function library . /etc/rc.d/init.d/functions @@ -15,49 +17,56 @@ # quagga command line options . /etc/sysconfig/quagga -# Check that networking is up. -[ "${NETWORKING_IPV6}" = "no" ] && exit 0 - -# The process must be configured first. -[ -f /etc/quagga/ospf6d.conf ] || exit 0 - RETVAL=0 -prog="ospf6d" +PROG="ospf6d" +cmd=ospf6d +LOCK_FILE=/var/lock/subsys/ospf6d +CONF_FILE=/etc/quagga/ospf6d.conf case "$1" in start) - echo -n $"Starting $prog: " - daemon /usr/sbin/ospf6d -d $OSPF6D_OPTS + # Check that networking is up. + [ "${NETWORKING}" = "no" ] && exit 1 + + # The process must be configured first. + [ -f $CONF_FILE ] || exit 6 + if [ `id -u` -ne 0 ]; then + echo $"Insufficient privilege" 1>&2 + exit 4 + fi + + echo -n $"Starting $PROG: " + daemon $cmd -d $OSPF6D_OPTS -f $CONF_FILE RETVAL=$? - [ $RETVAL -eq 0 ] && touch /var/lock/subsys/ospf6d + [ $RETVAL -eq 0 ] && touch $LOCK_FILE echo ;; stop) - echo -n $"Shutting down $prog: " - killproc ospf6d + echo -n $"Shutting down $PROG: " + killproc $cmd RETVAL=$? - [ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/ospf6d + [ $RETVAL -eq 0 ] && rm -f $LOCK_FILE echo ;; - restart|reload) - $0 stop - $0 start + restart|reload|force-reload) + $0 stop + $0 start RETVAL=$? - ;; - condrestart) - if [ -f /var/lock/subsys/ospf6d ]; then - $0 stop + ;; + condrestart|try-restart) + if [ -f $LOCK_FILE ]; then + $0 stop $0 start - fi + fi RETVAL=$? - ;; + ;; status) - status ospf6d + status $cmd RETVAL=$? - ;; + ;; *) - echo $"Usage: $prog {start|stop|restart|reload|condrestart|status}" - exit 1 + echo $"Usage: $0 {start|stop|restart|reload|force-reload|condrestart|try-restart|status}" + exit 2 esac exit $RETVAL diff --git a/redhat/ospf6d.service b/redhat/ospf6d.service new file mode 100644 index 000000000..b53b97091 --- /dev/null +++ b/redhat/ospf6d.service @@ -0,0 +1,16 @@ +[Unit] +Description=OSPF routing daemon for IPv6 +BindsTo=zebra.service +Wants=network.target +After=zebra.service network-pre.target +Before=network.target +ConditionPathExists=/etc/quagga/ospf6d.conf + +[Service] +Type=forking +EnvironmentFile=/etc/sysconfig/quagga +ExecStart=/usr/sbin/ospf6d -d $OSPF6D_OPTS -f /etc/quagga/ospf6d.conf +Restart=on-abort + +[Install] +WantedBy=multi-user.target diff --git a/redhat/ospfd.init b/redhat/ospfd.init index 911f7aa59..d964f38df 100644 --- a/redhat/ospfd.init +++ b/redhat/ospfd.init @@ -1,11 +1,13 @@ #!/bin/bash -# -# chkconfig: 2345 16 84 -# description: An OSPF v2 routing engine for use with Zebra -# -# processname: ospfd +# chkconfig: - 16 84 # config: /etc/quagga/ospfd.conf +### BEGIN INIT INFO +# Provides: ospfd +# Short-Description: OSPF routing engine +# Description: OSPF routing engine for use with Zebra +### END INIT INFO + # source function library . /etc/rc.d/init.d/functions @@ -15,50 +17,56 @@ # quagga command line options . /etc/sysconfig/quagga -# Check that networking is up. -[ "${NETWORKING}" = "no" ] && exit 0 - -# The process must be configured first. -[ -f /etc/quagga/ospfd.conf ] || exit 0 - RETVAL=0 - -prog="ospfd" +PROG="ospfd" +cmd=ospfd +LOCK_FILE=/var/lock/subsys/ospfd +CONF_FILE=/etc/quagga/ospfd.conf case "$1" in start) - echo -n $"Starting $prog: " - daemon /usr/sbin/ospfd -d $OSPFD_OPTS + # Check that networking is up. + [ "${NETWORKING}" = "no" ] && exit 1 + + # The process must be configured first. + [ -f $CONF_FILE ] || exit 6 + if [ `id -u` -ne 0 ]; then + echo $"Insufficient privilege" 1>&2 + exit 4 + fi + + echo -n $"Starting $PROG: " + daemon $cmd -d $OSPFD_OPTS -f $CONF_FILE RETVAL=$? - [ $RETVAL -eq 0 ] && touch /var/lock/subsys/ospfd + [ $RETVAL -eq 0 ] && touch $LOCK_FILE echo ;; stop) - echo -n $"Shutting down $prog: " - killproc ospfd + echo -n $"Shutting down $PROG: " + killproc $cmd RETVAL=$? - [ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/ospfd + [ $RETVAL -eq 0 ] && rm -f $LOCK_FILE echo ;; - restart|reload) - $0 stop - $0 start + restart|reload|force-reload) + $0 stop + $0 start RETVAL=$? - ;; - condrestart) - if [ -f /var/lock/subsys/ospfd ]; then - $0 stop + ;; + condrestart|try-restart) + if [ -f $LOCK_FILE ]; then + $0 stop $0 start - fi + fi RETVAL=$? - ;; + ;; status) - status ospfd + status $cmd RETVAL=$? - ;; + ;; *) - echo $"Usage: $0 {start|stop|restart|reload|condrestart|status}" - exit 1 + echo $"Usage: $0 {start|stop|restart|reload|force-reload|condrestart|try-restart|status}" + exit 2 esac exit $RETVAL diff --git a/redhat/ospfd.service b/redhat/ospfd.service new file mode 100644 index 000000000..5d6c5bb01 --- /dev/null +++ b/redhat/ospfd.service @@ -0,0 +1,16 @@ +[Unit] +Description=OSPF routing daemon +BindsTo=zebra.service +Wants=network.target +After=zebra.service network-pre.target +Before=network.target +ConditionPathExists=/etc/quagga/ospfd.conf + +[Service] +Type=forking +EnvironmentFile=/etc/sysconfig/quagga +ExecStart=/usr/sbin/ospfd -d $OSPFD_OPTS -f /etc/quagga/ospfd.conf +Restart=on-abort + +[Install] +WantedBy=multi-user.target diff --git a/redhat/pimd.init b/redhat/pimd.init new file mode 100644 index 000000000..49f90755d --- /dev/null +++ b/redhat/pimd.init @@ -0,0 +1,72 @@ +#!/bin/bash +# chkconfig: - 16 84 +# config: /etc/quagga/pimd.conf + +### BEGIN INIT INFO +# Provides: pimd +# Short-Description: PIM multicast routing engine +# Description: PIM routing engine for use with Zebra +### END INIT INFO + +# source function library +. /etc/rc.d/init.d/functions + +# Get network config +. /etc/sysconfig/network + +# quagga command line options +. /etc/sysconfig/quagga + +RETVAL=0 +PROG="pimd" +cmd=pimd +LOCK_FILE=/var/lock/subsys/pimd +CONF_FILE=/etc/quagga/pimd.conf + +case "$1" in + start) + # Check that networking is up. + [ "${NETWORKING}" = "no" ] && exit 1 + + # The process must be configured first. + [ -f $CONF_FILE ] || exit 6 + if [ `id -u` -ne 0 ]; then + echo $"Insufficient privilege" 1>&2 + exit 4 + fi + + echo -n $"Starting $PROG: " + daemon $cmd -d $PIMD_OPTS -f $CONF_FILE + RETVAL=$? + [ $RETVAL -eq 0 ] && touch $LOCK_FILE + echo + ;; + stop) + echo -n $"Shutting down $PROG: " + killproc $cmd + RETVAL=$? + [ $RETVAL -eq 0 ] && rm -f $LOCK_FILE + echo + ;; + restart|reload|force-reload) + $0 stop + $0 start + RETVAL=$? + ;; + condrestart|try-restart) + if [ -f $LOCK_FILE ]; then + $0 stop + $0 start + fi + RETVAL=$? + ;; + status) + status $cmd + RETVAL=$? + ;; + *) + echo $"Usage: $0 {start|stop|restart|reload|force-reload|condrestart|try-restart|status}" + exit 2 +esac + +exit $RETVAL diff --git a/redhat/pimd.service b/redhat/pimd.service new file mode 100644 index 000000000..d62fe6488 --- /dev/null +++ b/redhat/pimd.service @@ -0,0 +1,14 @@ +[Unit] +Description=PIM multicast routing engine +BindTo=zebra.service +After=syslog.target network.target zebra.service +ConditionPathExists=/etc/quagga/pimd.conf + +[Service] +Type=forking +EnvironmentFile=/etc/sysconfig/quagga +ExecStart=/usr/sbin/pimd -d $PIMD_OPTS -f /etc/quagga/pimd.conf +Restart=on-abort + +[Install] +WantedBy=network.target diff --git a/redhat/quagga-filter-perl-requires.sh b/redhat/quagga-filter-perl-requires.sh new file mode 100755 index 000000000..db82cfd87 --- /dev/null +++ b/redhat/quagga-filter-perl-requires.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +/usr/lib/rpm/perl.req $* | grep -v "Net::Telnet" diff --git a/redhat/quagga-tmpfs.conf b/redhat/quagga-tmpfs.conf new file mode 100644 index 000000000..8974b64f8 --- /dev/null +++ b/redhat/quagga-tmpfs.conf @@ -0,0 +1 @@ +d /var/run/quagga 0755 quagga quagga diff --git a/redhat/quagga.logrotate b/redhat/quagga.logrotate index 1b453d16d..afbd40c02 100644 --- a/redhat/quagga.logrotate +++ b/redhat/quagga.logrotate @@ -14,6 +14,14 @@ endscript } +/var/log/quagga/isisd.log { + notifempty + missingok + postrotate + /bin/kill -USR1 `cat /var/run/quagga/isisd.pid 2> /dev/null` 2> /dev/null || true + endscript +} + /var/log/quagga/ospfd.log { notifempty missingok diff --git a/redhat/quagga.pam.stack b/redhat/quagga.pam.stack deleted file mode 100644 index 8ddc2bbe3..000000000 --- a/redhat/quagga.pam.stack +++ /dev/null @@ -1,26 +0,0 @@ -#%PAM-1.0 -# - -##### if running quagga as root: -# Only allow root (and possibly wheel) to use this because enable access -# is unrestricted. -auth sufficient /lib/security/$ISA/pam_rootok.so - -# Uncomment the following line to implicitly trust users in the "wheel" group. -#auth sufficient /lib/security/$ISA/pam_wheel.so trust use_uid -# Uncomment the following line to require a user to be in the "wheel" group. -#auth required /lib/security/$ISA/pam_wheel.so use_uid -########################################################### - -# If using quagga privileges and with a seperate group for vty access, then -# access can be controlled via the vty access group, and pam can simply -# check for valid user/password, eg: -# -# only allow local users. -#auth required /lib/security/$ISA/pam_securetty.so -#auth required /lib/security/$ISA/pam_stack.so service=system-auth -#auth required /lib/security/$ISA/pam_nologin.so -#account required /lib/security/$ISA/pam_stack.so service=system-auth -#password required /lib/security/$ISA/pam_stack.so service=system-auth -#session required /lib/security/$ISA/pam_stack.so service=system-auth -#session optional /lib/security/$ISA/pam_console.so diff --git a/redhat/quagga.spec.in b/redhat/quagga.spec.in index a8c7fbf36..3bf5b9d05 100644 --- a/redhat/quagga.spec.in +++ b/redhat/quagga.spec.in @@ -2,128 +2,168 @@ # # Some can be overriden on rpmbuild commandline with: # rpmbuild --define 'variable value' +# (use any value, ie 1 for flag "with_XXXX" definitions) # +# E.g. rpmbuild --define 'release_rev 02' may be useful if building +# rpms again and again on the same day, so the newer rpms can be installed. +# bumping the number each time. ####################### Quagga configure options ######################### # with-feature options -%{!?with_snmp: %define with_snmp 1 } -%{!?with_vtysh: %define with_vtysh 1 } -%{!?with_ospf_te: %define with_ospf_te 1 } -%{!?with_nssa: %define with_nssa 1 } -%{!?with_opaque_lsa: %define with_opaque_lsa 1 } -%{!?with_tcp_zebra: %define with_tcp_zebra 0 } -%{!?with_vtysh: %define with_vtysh 1 } -%{!?with_pam: %define with_pam 1 } -%{!?with_ipv6: %define with_ipv6 1 } -%{!?with_ospfclient: %define with_ospfclient 1 } -%{!?with_ospfapi: %define with_ospfapi 1 } -%{!?with_irdp: %define with_irdp 1 } -%{!?with_rtadv: %define with_rtadv 1 } -%{!?with_isisd: %define with_isisd 1 } -%{!?with_shared: %define with_shared 1 } -%{!?with_multipath: %define with_multipath 64 } -%{!?quagga_user: %define quagga_user quagga } -%{!?vty_group: %define vty_group quaggavty } +%{!?with_snmp: %global with_snmp 1 } +%{!?with_vtysh: %global with_vtysh 1 } +%{!?with_tcp_zebra: %global with_tcp_zebra 0 } +%{!?with_vtysh: %global with_vtysh 1 } +%{!?with_pam: %global with_pam 1 } +%{!?with_ospfclient: %global with_ospfclient 1 } +%{!?with_ospfapi: %global with_ospfapi 1 } +%{!?with_irdp: %global with_irdp 1 } +%{!?with_rtadv: %global with_rtadv 1 } +%{!?with_isisd: %global with_isisd 1 } +%{!?with_pimd: %global with_pimd 1 } +%{!?with_nhrpd: %global with_nhrpd 1 } +%{!?with_shared: %global with_shared 1 } +%{!?with_multipath: %global with_multipath 64 } +%{!?quagga_user: %global quagga_user quagga } +%{!?vty_group: %global vty_group quaggavt } +%{!?with_fpm: %global with_fpm 0 } +%{!?with_watchquagga: %global with_watchquagga 1 } +# whether to build doc/quagga.html - requires a lot of TeX packages +%{!?with_texi2html: %global with_texi2html 0 } # path defines %define _sysconfdir /etc/quagga -%define zeb_src %{_builddir}/%{name}-%{version} +%define zeb_src %{_builddir}/%{name}-%{quaggaversion} %define zeb_rh_src %{zeb_src}/redhat %define zeb_docs %{zeb_src}/doc # defines for configure -%define _libexecdir %{_exec_prefix}/libexec/quagga -%define _libdir %{_exec_prefix}/%{_lib}/quagga -%define _includedir %{_prefix}/include %define _localstatedir /var/run/quagga ############################################################################ -####################### distro specific tweaks ############################# -# default distro. Override with rpmbuild -D "dist XXX" -%{expand: %%define default_dist %(rpm -q --qf 'fc%%{VERSION}' fedora-release | grep -v 'not installed')} -%{!?dist: %define dist %{default_dist}} +#### Version String tweak +# Remove invalid characters form version string and replace with _ +%{expand: %%global rpmversion %(echo '@VERSION@' | tr [:blank:]- _ )} +%define quaggaversion @VERSION@ -# as distros change packages we depend on, our Requires have to change, sadly. -%define quagga_buildreqs texinfo tetex autoconf pam-devel -%define quagga_buildreqs %{quagga_buildreqs} patch libcap-devel - -# FC4 and 5 split texi2html out of tetex package. -%if "%dist" != "fc2" || "%dist" != "fc3" -%define quagga_buildreqs %{quagga_buildreqs} texi2html +#### Check version of texi2html +# Old versions don't support "--number-footnotes" option. +%if %{with_texi2html} + %{expand: %%global texi2htmlversion %(type texi2html >/dev/null 2>&1 && (rpm -q --qf '%%{VERSION}' texi2html | cut -d. -f1) || echo 0 )} %endif -# pam_stack is deprecated in FC5 -# default to pam_stack, default should be changed later. -%if "%dist" == "fc4" || "%dist" == "fc3" -%define quagga_pam_source quagga.pam.stack -%else -%define quagga_pam_source quagga.pam +#### Check for systemd or init.d (upstart) +# Check for init.d (upstart) as used in CentOS 6 or systemd (ie CentOS 7) +%{expand: %%global initsystem %(if [[ `/sbin/init --version 2> /dev/null` =~ upstart ]]; then echo upstart; elif [[ `systemctl` =~ -\.mount ]]; then echo systemd; fi)} +# +# If init system is systemd, then always disable watchquagga +# +%if "%{initsystem}" == "systemd" + # Note: For systems with systemd, watchquagga will NOT be built. Systemd + # takes over the role of restarting crashed processes. Value will + # be overwritten with 0 below for systemd independent on the setting here + %global with_watchquagga 0 %endif -############################################################################ +# if FPM is enabled, then enable tcp_zebra as well +# +%if %{with_fpm} + %global with_tcp_zebra 1 +%endif # misc internal defines -%{!?quagga_uid: %define quagga_uid 92 } -%{!?quagga_gid: %define quagga_gid 92 } +%{!?quagga_uid: %global quagga_uid 92 } +%{!?quagga_gid: %global quagga_gid 92 } +%{!?vty_gid: %global vty_gid 85 } + %define daemon_list zebra ripd ospfd bgpd -%if %{with_ipv6} %define daemonv6_list ripngd ospf6d + +%if %{with_isisd} +%define daemon_isisd isisd %else -%define daemonv6_list "" +%define daemon_isisd "" %endif -%if %{with_isisd} -%define daemon_other isisd +%if %{with_pimd} +%define daemon_pimd pimd %else -%define daemon_other "" +%define daemon_pimd "" %endif -%define all_daemons %{daemon_list} %{daemonv6_list} %{daemon_other} watchquagga +%if %{with_nhrpd} +%define daemon_nhrpd nhrpd +%else +%define daemon_nhrpd "" +%endif + +%if %{with_watchquagga} +%define daemon_watchquagga watchquagga +%else +%define daemon_watchquagga "" +%endif + +%define all_daemons %{daemon_list} %{daemonv6_list} %{daemon_isisd} %{daemon_pimd} %{daemon_nhrpd} %{daemon_watchquagga} # allow build dir to be kept -%{!?keep_build: %define keep_build 0 } +%{!?keep_build: %global keep_build 0 } #release sub-revision (the two digits after the CONFDATE) -%{!?release_rev: %define release_rev 01 } +%{!?release_rev: %global release_rev 01 } Summary: Routing daemon -Name: quagga -Version: @VERSION@ -Release: @CONFDATE@%{release_rev} -License: GPL -Group: System Environment/Daemons -Source0: http://www.quagga.net/snapshots/cvs/%{name}-%{version}.tar.gz -URL: http://www.quagga.net +Name: quagga +Version: %{rpmversion} +Release: @CONFDATE@%{release_rev}%{?dist} +License: GPLv2+ +Group: System Environment/Daemons +Source0: https://download.savannah.gnu.org/releases/quagga/%{name}-%{quaggaversion}.tar.gz +URL: https://www.quagga.net +Requires: ncurses +Requires(pre): /sbin/install-info +Requires(preun): /sbin/install-info +Requires(post): /sbin/install-info +BuildRequires: autoconf patch libcap-devel groff +BuildRequires: perl-generators pkgconfig +%if %{with_texi2html} +BuildRequires: texi2html +%endif %if %{with_snmp} BuildRequires: net-snmp-devel -Prereq: net-snmp +Requires: net-snmp %endif %if %{with_vtysh} BuildRequires: readline readline-devel ncurses ncurses-devel -Prereq: ncurses +Requires: ncurses %endif -BuildRequires: texinfo tetex autoconf pam-devel patch libcap-devel tetex +%if %{with_pam} +BuildRequires: pam-devel +Requires: pam +%endif +%if %{with_nhrpd} +BuildRequires: c-ares-devel +Requires: c-ares +%endif +%if "%{initsystem}" == "systemd" +BuildRequires: systemd +Requires(post): systemd +Requires(preun): systemd +Requires(postun): systemd +%else # Initscripts > 5.60 is required for IPv6 support -Prereq: initscripts >= 5.60 -Prereq: ncurses pam -Prereq: /sbin/install-info -Provides: routingdaemon -BuildRoot: %{_tmppath}/%{name}-%{version}-root -Obsoletes: bird gated mrt zebra - -%description -Quagga is a free software that manages TCP/IP based routing -protocol. It takes multi-server and multi-thread approach to resolve -the current complexity of the Internet. +Requires(pre): initscripts >= 5.60 +%endif +Provides: routingdaemon = %{version}-%{release} +BuildRoot: %{_tmppath}/%{name}-%{version}-root +#Obsoletes: mrt zebra quagga-sysvinit -Quagga supports BGP4, BGP4+, OSPFv2, OSPFv3, RIPv1, RIPv2, and RIPng. +%define __perl_requires %{zeb_rh_src}/quagga-filter-perl-requires.sh -Quagga is intended to be used as a Route Server and a Route Reflector. It is -not a toolkit, it provides full routing power under a new architecture. -Quagga by design has a process for each protocol. +%description +Quagga is a free software routing protocol suite. -Quagga is a fork of GNU Zebra. +Quagga supports BGP, OSPFv2, OSPFv3, ISIS, RIP, RIPng, PIM-SSM and NHRP. %package contrib Summary: contrib tools for quagga @@ -135,33 +175,26 @@ Contributed/3rd party tools which may be of use with quagga. %package devel Summary: Header and object files for quagga development Group: System Environment/Daemons +Requires: %{name} = %{version}-%{release} %description devel The quagga-devel package contains the header and object files neccessary for developing OSPF-API and quagga applications. %prep -%setup -q +%setup -q -n quagga-%{quaggaversion} %build -# For standard gcc verbosity, uncomment these lines: -#CFLAGS="%{optflags} -Wall -Wsign-compare -Wpointer-arith" -#CFLAGS="${CFLAGS} -Wbad-function-cast -Wwrite-strings" - -# For ultra gcc verbosity, uncomment these lines also: -#CFLAGS="${CFLAGS} -W -Wcast-qual -Wstrict-prototypes" -#CFLAGS="${CFLAGS} -Wmissing-declarations -Wmissing-noreturn" -#CFLAGS="${CFLAGS} -Wmissing-format-attribute -Wunreachable-code" -#CFLAGS="${CFLAGS} -Wpacked -Wpadded" - %configure \ + --sysconfdir=%{_sysconfdir} \ + --libdir=%{_libdir}/quagga \ + --libexecdir=%{_libexecdir} \ + --localstatedir=%{_localstatedir} \ + --disable-werror \ %if !%{with_shared} --disable-shared \ %endif -%if %{with_ipv6} - --enable-ipv6 \ -%endif %if %{with_snmp} --enable-snmp \ %endif @@ -171,15 +204,6 @@ developing OSPF-API and quagga applications. %if %{with_tcp_zebra} --enable-tcp-zebra \ %endif -%if %{with_nssa} - --enable-nssa \ -%endif -%if %{with_opaque_lsa} - --enable-opaque-lsa \ -%endif -%if %{with_ospf_te} - --enable-ospf-te \ -%endif %if %{with_vtysh} --enable-vtysh \ %endif @@ -208,68 +232,104 @@ developing OSPF-API and quagga applications. %else --disable-isisd \ %endif +%if %{with_nhrpd} + --enable-nhrpd \ +%else + --disable-nhrpd \ +%endif %if %{with_pam} --with-libpam \ %endif -%if %quagga_user +%if 0%{?quagga_user:1} --enable-user=%quagga_user \ --enable-group=%quagga_user \ %endif -%if %vty_group +%if 0%{?vty_group:1} --enable-vty-group=%vty_group \ %endif ---enable-netlink --enable-gcc-rdynamic +%if %{with_fpm} + --enable-fpm \ +%else + --disable-fpm \ +%endif +%if %{with_watchquagga} + --enable-watchquagga \ +%else + --disable-watchquagga \ +%endif + --enable-gcc-rdynamic -make %{?_smp_mflags} MAKEINFO="makeinfo --no-split" +make %{?_smp_mflags} -pushd doc -texi2html -number quagga.texi -popd +%if %{with_texi2html} + pushd doc + %if %{texi2htmlversion} < 5 + texi2html --number-sections quagga.texi + %else + texi2html --number-footnotes --number-sections quagga.texi + %endif + popd +%endif %install -rm -rf $RPM_BUILD_ROOT - -install -d $RPM_BUILD_ROOT/etc/{rc.d/init.d,sysconfig,logrotate.d,pam.d} \ - $RPM_BUILD_ROOT/var/log/quagga $RPM_BUILD_ROOT%{_infodir} - -make install \ - DESTDIR=$RPM_BUILD_ROOT +mkdir -p %{buildroot}/etc/{quagga,sysconfig,logrotate.d,pam.d} \ + %{buildroot}/var/log/quagga %{buildroot}%{_infodir} +make DESTDIR=%{buildroot} INSTALL="install -p" CP="cp -p" install # Remove this file, as it is uninstalled and causes errors when building on RH9 -rm -rf $RPM_BUILD_ROOT/usr/share/info/dir +rm -rf %{buildroot}/usr/share/info/dir -# install etc sources +# install /etc sources +%if "%{initsystem}" == "systemd" +mkdir -p %{buildroot}%{_unitdir} +for daemon in %{all_daemons} ; do + if [ x"${daemon}" != x"" ] ; then + install %{zeb_rh_src}/${daemon}.service \ + %{buildroot}%{_unitdir}/${daemon}.service + fi +done +%else +mkdir -p %{buildroot}/etc/rc.d/init.d for daemon in %{all_daemons} ; do if [ x"${daemon}" != x"" ] ; then install %{zeb_rh_src}/${daemon}.init \ - $RPM_BUILD_ROOT/etc/rc.d/init.d/${daemon} + %{buildroot}/etc/rc.d/init.d/${daemon} fi done -install -m644 %{zeb_rh_src}/%{quagga_pam_source} \ - $RPM_BUILD_ROOT/etc/pam.d/quagga +%endif + +install -m644 %{zeb_rh_src}/quagga.pam \ + %{buildroot}/etc/pam.d/quagga install -m644 %{zeb_rh_src}/quagga.logrotate \ - $RPM_BUILD_ROOT/etc/logrotate.d/quagga + %{buildroot}/etc/logrotate.d/quagga install -m644 %{zeb_rh_src}/quagga.sysconfig \ - $RPM_BUILD_ROOT/etc/sysconfig/quagga -install -d -m750 $RPM_BUILD_ROOT/var/run/quagga + %{buildroot}/etc/sysconfig/quagga +install -d -m750 %{buildroot}/var/run/quagga + + +%if 0%{?_tmpfilesdir:1} + install -d -m 755 %{buildroot}/%{_tmpfilesdir} + install -p -m 644 %{zeb_rh_src}/quagga-tmpfs.conf \ + %{buildroot}/%{_tmpfilesdir}/quagga.conf +%endif %pre # add vty_group -%if %vty_group +%if 0%{?vty_group:1} if getent group %vty_group > /dev/null ; then : ; else \ - /usr/sbin/groupadd -r %vty_group > /dev/null || : ; fi + /usr/sbin/groupadd -r -g %vty_gid %vty_group > /dev/null || : ; fi %endif # add quagga user and group -%if %quagga_user +%if 0%{?quagga_user:1} # Ensure that quagga_gid gets correctly allocated if getent group %quagga_user >/dev/null; then : ; else \ - /usr/sbin/groupadd -g %quagga_gid %quagga_user > /dev/null || : ; \ + /usr/sbin/groupadd -g %quagga_gid %quagga_user > /dev/null || : ; \ fi if getent passwd %quagga_user >/dev/null ; then : ; else \ - /usr/sbin/useradd -u %quagga_uid -g %quagga_gid \ - -M -r -s /sbin/nologin -c "Quagga routing suite" \ - -d %_localstatedir %quagga_user 2> /dev/null || : ; \ + /usr/sbin/useradd -u %quagga_uid -g %quagga_gid -G %vty_group \ + -M -r -s /sbin/nologin -c "Quagga routing suite" \ + -d %_localstatedir %quagga_user 2> /dev/null || : ; \ fi %endif @@ -287,155 +347,263 @@ zebra_spec_add_service () } zebra_spec_add_service zebrasrv 2600/tcp "zebra service" -zebra_spec_add_service zebra 2601/tcp "zebra vty" -zebra_spec_add_service ripd 2602/tcp "RIPd vty" -%if %{with_ipv6} -zebra_spec_add_service ripngd 2603/tcp "RIPngd vty" -%endif -zebra_spec_add_service ospfd 2604/tcp "OSPFd vty" -zebra_spec_add_service bgpd 2605/tcp "BGPd vty" -%if %{with_ipv6} -zebra_spec_add_service ospf6d 2606/tcp "OSPF6d vty" -%endif +zebra_spec_add_service zebra 2601/tcp "zebra vty" +zebra_spec_add_service ripd 2602/tcp "RIPd vty" +zebra_spec_add_service ripngd 2603/tcp "RIPngd vty" +zebra_spec_add_service ospfd 2604/tcp "OSPFd vty" +zebra_spec_add_service bgpd 2605/tcp "BGPd vty" +zebra_spec_add_service ospf6d 2606/tcp "OSPF6d vty" %if %{with_ospfapi} -zebra_spec_add_service ospfapi 2607/tcp "OSPF-API" +zebra_spec_add_service ospfapi 2607/tcp "OSPF-API" %endif %if %{with_isisd} -zebra_spec_add_service isisd 2608/tcp "ISISd vty" +zebra_spec_add_service isisd 2608/tcp "ISISd vty" +%endif +%if %{with_pimd} +zebra_spec_add_service pimd 2611/tcp "PIMd vty" +%endif +%if %{with_nhrpd} +zebra_spec_add_service nhrpd 2612/tcp "NHRPd vty" %endif -for daemon in %daemon_list ; do +%if "%{initsystem}" == "systemd" +for daemon in %all_daemons ; do + %systemd_post ${daemon}.service +done +%else +for daemon in %all_daemons ; do /sbin/chkconfig --add ${daemon} done +%endif -/sbin/install-info %{_infodir}/quagga.info.gz %{_infodir}/dir +if [ -f %{_infodir}/%{name}.inf* ]; then + /sbin/install-info %{_infodir}/quagga.info %{_infodir}/dir +fi # Create dummy files if they don't exist so basic functions can be used. if [ ! -e %{_sysconfdir}/zebra.conf ]; then echo "hostname `hostname`" > %{_sysconfdir}/zebra.conf -%if %{quagga_user} - chown %quagga_user:%quagga_user %{_sysconfdir}/zebra.conf +%if 0%{?quagga_user:1} + chown %quagga_user:%quagga_user %{_sysconfdir}/zebra.conf* %endif chmod 640 %{_sysconfdir}/zebra.conf fi +for daemon in %{all_daemons} ; do + if [ ! -e %{_sysconfdir}/${daemon}.conf ]; then + touch %{_sysconfdir}/${daemon}.conf + %if 0%{?quagga_user:1} + chown %quagga_user:%quagga_user %{_sysconfdir}/${daemon}.conf* + %endif + fi +done +%if %{with_watchquagga} + # No config for watchquagga - this is part of /etc/sysconfig/quagga + rm -f %{_sysconfdir}/watchquagga.* +%endif + if [ ! -e %{_sysconfdir}/vtysh.conf ]; then touch %{_sysconfdir}/vtysh.conf chmod 640 %{_sysconfdir}/vtysh.conf +%if 0%{?vty_group:1} + chown quagga:%{vty_group} %{_sysconfdir}/vtysh.conf* +%endif fi %postun if [ "$1" -ge 1 ]; then # Find out which daemons need to be restarted. for daemon in %all_daemons ; do - if [ -f /var/lock/subsys/$daemon ]; then - eval restart_$daemon=yes + if [ -f /var/lock/subsys/${daemon} ]; then + eval restart_${daemon}=yes else - eval restart_$daemon=no + eval restart_${daemon}=no fi done # Rename restart flags for daemons handled specially. running_zebra="$restart_zebra" restart_zebra=no - running_watchquagga="$restart_watchquagga" - restart_watchquagga=no - # Stop watchquagga first. - [ "$running_watchquagga" = yes ] && \ - /etc/rc.d/init.d/watchquagga stop >/dev/null 2>&1 - # Stop all daemons other than zebra and watchquagga. - for daemon in %all_daemons ; do - eval restart=\$restart_${daemon} - [ "$restart" = yes ] && \ - /etc/rc.d/init.d/$daemon stop >/dev/null 2>&1 - done - # Restart zebra. - [ "$running_zebra" = yes ] && \ - /etc/rc.d/init.d/zebra restart >/dev/null 2>&1 - # Start all daemons other than zebra and watchquagga. - for daemon in %all_daemons ; do - eval restart=\$restart_${daemon} - [ "$restart" = yes ] && \ - /etc/rc.d/init.d/$daemon start >/dev/null 2>&1 - done - # Start watchquagga last. - # Avoid postun scriptlet error if watchquagga is not running. - [ "$running_watchquagga" = yes ] && \ - /etc/rc.d/init.d/watchquagga start >/dev/null 2>&1 || : + %if %{with_watchquagga} + running_watchquagga="$restart_watchquagga" + restart_watchquagga=no + %endif + + %if "%{initsystem}" == "systemd" + ## + ## Systemd Version + ## + # No watchquagga for systemd version + # + # Stop all daemons other than zebra. + for daemon in %all_daemons ; do + eval restart=\$restart_${daemon} + [ "$restart" = yes ] && \ + %systemd_postun_with_restart ${daemon}.service + done + # Restart zebra. + [ "$running_zebra" = yes ] && \ + %systemd_postun_with_restart $daemon.service + # Start all daemons other than zebra. + for daemon in %all_daemons ; do + eval restart=\$restart_${daemon} + [ "$restart" = yes ] && \ + %systemd_post ${daemon}.service + done + %else + ## + ## init.d Version + ## + %if %{with_watchquagga} + # Stop watchquagga first. + [ "$running_watchquagga" = yes ] && \ + /etc/rc.d/init.d/watchquagga stop >/dev/null 2>&1 + %endif + # Stop all daemons other than zebra and watchquagga. + for daemon in %all_daemons ; do + eval restart=\$restart_${daemon} + [ "$restart" = yes ] && \ + /etc/rc.d/init.d/${daemon} stop >/dev/null 2>&1 + done + # Restart zebra. + [ "$running_zebra" = yes ] && \ + /etc/rc.d/init.d/zebra restart >/dev/null 2>&1 + # Start all daemons other than zebra and watchquagga. + for daemon in %all_daemons ; do + eval restart=\$restart_${daemon} + [ "$restart" = yes ] && \ + /etc/rc.d/init.d/${daemon} start >/dev/null 2>&1 + done + %if %{with_watchquagga} + # Start watchquagga last. + # Avoid postun scriptlet error if watchquagga is not running. + [ "$running_watchquagga" = yes ] && \ + /etc/rc.d/init.d/watchquagga start >/dev/null 2>&1 || : + %endif + %endif fi -/sbin/install-info --delete %{_infodir}/quagga.info.gz %{_infodir}/dir -%preun -if [ "$1" = "0" ]; then - for daemon in %all_daemons ; do - /etc/rc.d/init.d/${daemon} stop >/dev/null 2>&1 - /sbin/chkconfig --del ${daemon} - done - /sbin/install-info --delete %{_infodir}/quagga.info.gz %{_infodir}/dir +if [ -f %{_infodir}/%{name}.inf* ]; then + /sbin/install-info --delete %{_infodir}/quagga.info %{_infodir}/dir fi +%preun +%if "%{initsystem}" == "systemd" + ## + ## Systemd Version + ## + if [ "$1" = "0" ]; then + for daemon in %all_daemons ; do + %systemd_preun ${daemon}.service + done + fi +%else + ## + ## init.d Version + ## + if [ "$1" = "0" ]; then + for daemon in %all_daemons ; do + /etc/rc.d/init.d/${daemon} stop >/dev/null 2>&1 + /sbin/chkconfig --del ${daemon} + done + fi +%endif + %clean -%if !%{keep_build} -rm -rf $RPM_BUILD_ROOT +%if !0%{?keep_build:1} +rm -rf %{buildroot} %endif %files %defattr(-,root,root) %doc */*.sample* AUTHORS COPYING -%doc doc/quagga.html +%if %{with_texi2html} + %doc doc/quagga.html +%endif %doc doc/mpls %doc ChangeLog INSTALL NEWS README REPORTING-BUGS SERVICES TODO -%if %{quagga_user} +%if 0%{?quagga_user:1} %dir %attr(751,%quagga_user,%quagga_user) %{_sysconfdir} %dir %attr(750,%quagga_user,%quagga_user) /var/log/quagga %dir %attr(751,%quagga_user,%quagga_user) /var/run/quagga %else %dir %attr(750,root,root) %{_sysconfdir} %dir %attr(750,root,root) /var/log/quagga -%dir %attr(755,root,root) /usr/share/info %dir %attr(750,root,root) /var/run/quagga %endif -%if %{vty_group} +%if 0%{?vty_group:1} %attr(750,%quagga_user,%vty_group) %{_sysconfdir}/vtysh.conf.sample %endif -%{_infodir}/*info* +%{_infodir}/quagga.info*.gz %{_mandir}/man*/* +%if %{with_watchquagga} + %{_mandir}/man*/watchquagga.* +%endif %{_sbindir}/zebra %{_sbindir}/ospfd %{_sbindir}/ripd %{_sbindir}/bgpd -%{_sbindir}/watchquagga -%if %{with_ipv6} +%if %{with_watchquagga} + %{_sbindir}/watchquagga +%endif %{_sbindir}/ripngd %{_sbindir}/ospf6d +%if %{with_pimd} +%{_sbindir}/pimd %endif %if %{with_isisd} %{_sbindir}/isisd %endif -%dir %attr(755,root,root) %{_libdir} +%if %{with_nhrpd} +%{_sbindir}/nhrpd +%endif %if %{with_shared} -%dir %{_libdir} -%{_libdir}/lib*.so -%{_libdir}/lib*.so.* +%{_libdir}/quagga/lib*.so +%{_libdir}/quagga/lib*.so.? +%attr(755,root,root) %{_libdir}/quagga/lib*.so.?.?.? %endif %if %{with_vtysh} %{_bindir}/* %endif %config /etc/quagga/[!v]* -%config /etc/rc.d/init.d/* +%if "%{initsystem}" == "systemd" + %{_unitdir}/*.service +%else + %config /etc/rc.d/init.d/zebra + %if %{with_watchquagga} + %config /etc/rc.d/init.d/watchquagga + %endif + %config /etc/rc.d/init.d/ripd + %config /etc/rc.d/init.d/ospfd + %config /etc/rc.d/init.d/bgpd + %config /etc/rc.d/init.d/ripngd + %config /etc/rc.d/init.d/ospf6d + %if %{with_isisd} + %config /etc/rc.d/init.d/isisd + %endif + %if %{with_pimd} + %config /etc/rc.d/init.d/pimd + %endif + %if %{with_nhrpd} + %config /etc/rc.d/init.d/nhrpd + %endif +%endif %config(noreplace) /etc/sysconfig/quagga %config(noreplace) /etc/pam.d/quagga %config(noreplace) %attr(640,root,root) /etc/logrotate.d/* +%{_tmpfilesdir}/quagga.conf %files contrib %defattr(-,root,root) -%doc tools +%doc AUTHORS COPYING %attr(0644,root,root) tools %files devel %defattr(-,root,root) +%doc AUTHORS COPYING %if %{with_ospfclient} %{_sbindir}/ospfclient %endif -%{_libdir}/*.a -%{_libdir}/*.la +%dir %{_libdir}/quagga/ +%{_libdir}/quagga/*.a +%{_libdir}/quagga/*.la %dir %attr(755,root,root) %{_includedir}/%{name} %{_includedir}/%name/*.h %dir %attr(755,root,root) %{_includedir}/%{name}/ospfd @@ -446,7 +614,43 @@ rm -rf $RPM_BUILD_ROOT %endif %changelog -* Thu Sep 12 2005 Paul Jakma +* Sun Mar 5 2017 Paul Jakma +- Fix lint errors +- Make texi2html conditional, disable by default to avoid needing TeX + by default + +* Mon Feb 27 2017 Paul Jakma +- Apply 0001-systemd-various-service-file-improvements.patch from Fedora +- Review Fedora spec file and sync up with any useful differences, inc: +- Add tmpfiles.d config for Quagga from Fedora +- Add quagga-filter-perl-requires.sh from Fedora. +- Move libs to %%{_libdir}/quagga as per Fedora +- use systemd_postun_with_restart for postun + +* Tue Feb 14 2017 Timo Teräs +- add nhrpd + +* Thu Feb 11 2016 Paul Jakma +- remove with_ipv6 conditionals, always build v6 +- Fix UTF-8 char in spec changelog +- remove quagga.pam.stack, long deprecated. + +* Thu Oct 22 2015 Martin Winter +- Cleanup configure: remove --enable-ipv6 (default now), --enable-nssa, + --enable-netlink +- Remove support for old fedora 4/5 +- Fix for package nameing +- Fix Weekdays of previous changelogs (bogus dates) +- Add conditional logic to only build tex footnotes with supported texi2html +- Added pimd to files section and fix double listing of /var/lib*/quagga +- Numerous fixes to unify upstart/systemd startup into same spec file +- Only allow use of watchquagga for non-systemd systems. no need with systemd + +* Fri Sep 4 2015 Paul Jakma +- buildreq updates +- add a default define for with_pimd + +* Mon Sep 12 2005 Paul Jakma - Steal some changes from Fedora spec file: - Add with_rtadv variable - Test for groups/users with getent before group/user adding @@ -499,7 +703,7 @@ rm -rf $RPM_BUILD_ROOT - add user with fixed UID/GID (RH) - create user with shell /sbin/nologin rather than /bin/false (RH) - stop daemons on uninstall (RH) -- delete info file on %preun, not %postun to avoid deletion on upgrade. (RH) +- delete info file on preun, not postun to avoid deletion on upgrade. (RH) - isisd added - cleanup tasks carried out for every daemon @@ -511,10 +715,10 @@ rm -rf $RPM_BUILD_ROOT - Renamed to Quagga - Sync to Quagga release 0.96 -* Tue Mar 20 2003 Paul Jakma +* Thu Mar 20 2003 Paul Jakma - zebra privileges support -* Mon Mar 18 2003 Paul Jakma +* Tue Mar 18 2003 Paul Jakma - Fix mem leak in 'show thread cpu' - Ralph Keller's OSPF-API - Amir: Fix configure.ac for net-snmp @@ -538,7 +742,7 @@ rm -rf $RPM_BUILD_ROOT - Added conditionals for building with(out) IPv6, vtysh, RIP, BGP - Fixed up some build requirements (patch) - Added conditional build requirements for vtysh / snmp -- Added conditional to %files for %_bindir depending on vtysh +- Added conditional to files for _bindir depending on vtysh * Mon Nov 11 2002 Paul Jakma - update to latest CVS @@ -578,7 +782,7 @@ rm -rf $RPM_BUILD_ROOT * Thu Aug 09 2001 Elliot Lee 0.91a-6 - Fix bug #51336 -* Wed Aug 1 2001 Trond Eivind Glomsrd 0.91a-5 +* Wed Aug 1 2001 Trond Eivind Glomsrød 0.91a-5 - Use generic initscript strings instead of initscript specific ( "Starting foo: " -> "Starting $prog:" ) diff --git a/redhat/quagga.sysconfig b/redhat/quagga.sysconfig index 9e9da5ef3..caa0fffe2 100644 --- a/redhat/quagga.sysconfig +++ b/redhat/quagga.sysconfig @@ -1,18 +1,25 @@ # # Default: Bind all daemon vtys to the loopback(s) only # -QCONFDIR="/etc/quagga" -BGPD_OPTS="-A 127.0.0.1 -f ${QCONFDIR}/bgpd.conf" -OSPF6D_OPTS="-A ::1 -f ${QCONFDIR}/ospf6d.conf" -OSPFD_OPTS="-A 127.0.0.1 -f ${QCONFDIR}/ospfd.conf" -RIPD_OPTS="-A 127.0.0.1 -f ${QCONFDIR}/ripd.conf" -RIPNGD_OPTS="-A ::1 -f ${QCONFDIR}/ripngd.conf" -ZEBRA_OPTS="-A 127.0.0.1 -f ${QCONFDIR}/zebra.conf" -ISISD_OPTS="-A ::1 -f ${QCONFDIR}/isisd.conf" +BABELD_OPTS="-A 127.0.0.1" +BGPD_OPTS="-A 127.0.0.1" +ISISD_OPTS="-A ::1" +OSPF6D_OPTS="-A ::1" +OSPFD_OPTS="-A 127.0.0.1" +RIPD_OPTS="-A 127.0.0.1" +RIPNGD_OPTS="-A ::1" +ZEBRA_OPTS="-A 127.0.0.1" +PIMD_OPTS="-A 127.0.0.1" -# Watchquagga configuration (please check timer values before using): -WATCH_OPTS="" -WATCH_DAEMONS="zebra bgpd ospfd ospf6d ripd ripngd" -# To enable restarts, uncomment this line (but first be sure to edit -# the WATCH_DAEMONS line to reflect the daemons you are actually using): -#WATCH_OPTS="-Az -b_ -r/sbin/service_%s_restart -s/sbin/service_%s_start -k/sbin/service_%s_stop" +# Watchquagga configuration for LSB initscripts +# +# (Not needed with systemd: the service files are configured to automatically +# restart any daemon on failure. If zebra fails, all running daemons will be +# stopped; zebra will be started again; and then the previously running daemons +# will be started again.) +# +# Uncomment and edit this line to reflect the daemons you are actually using: +#WATCH_DAEMONS="zebra bgpd ospfd ospf6d ripd ripngd" +# +# Timer values can be adjusting by editing this line: +WATCH_OPTS="-Az -b_ -r/sbin/service_%s_restart -s/sbin/service_%s_start -k/sbin/service_%s_stop" diff --git a/redhat/ripd.init b/redhat/ripd.init index 766422194..9b412cb96 100644 --- a/redhat/ripd.init +++ b/redhat/ripd.init @@ -1,11 +1,13 @@ #!/bin/bash -# -# chkconfig: 2345 16 84 -# description: A RIP routing engine for use with Zebra -# -# processname: ripd +# chkconfig: - 16 84 # config: /etc/quagga/ripd.conf +### BEGIN INIT INFO +# Provides: ripd +# Short-Description: RIP routing engine +# Description: RIP routing engine for use with Zebra +### END INIT INFO + # source function library . /etc/rc.d/init.d/functions @@ -15,49 +17,56 @@ # quagga command line options . /etc/sysconfig/quagga -# Check that networking is up. -[ "${NETWORKING}" = "no" ] && exit 0 - -# The process must be configured first. -[ -f /etc/quagga/ripd.conf ] || exit 0 - RETVAL=0 -prog="ripd" +PROG="ripd" +cmd=ripd +LOCK_FILE=/var/lock/subsys/ripd +CONF_FILE=/etc/quagga/ripd.conf case "$1" in start) - echo -n $"Starting $prog: " - daemon /usr/sbin/ripd -d $RIPD_OPTS + # Check that networking is up. + [ "${NETWORKING}" = "no" ] && exit 1 + + # The process must be configured first. + [ -f $CONF_FILE ] || exit 6 + if [ `id -u` -ne 0 ]; then + echo $"Insufficient privilege" 1>&2 + exit 4 + fi + + echo -n $"Starting $PROG: " + daemon $cmd -d $RIPD_OPTS -f $CONF_FILE RETVAL=$? - [ $RETVAL -eq 0 ] && touch /var/lock/subsys/ripd + [ $RETVAL -eq 0 ] && touch $LOCK_FILE echo ;; stop) - echo -n $"Shutting down $prog: " - killproc ripd + echo -n $"Shutting down $PROG: " + killproc $cmd RETVAL=$? - [ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/ripd + [ $RETVAL -eq 0 ] && rm -f $LOCK_FILE echo ;; - restart|reload) - $0 stop - $0 start + restart|reload|force-reload) + $0 stop + $0 start RETVAL=$? - ;; - condrestart) - if [ -f /var/lock/subsys/ripd ]; then - $0 stop + ;; + condrestart|try-restart) + if [ -f $LOCK_FILE ]; then + $0 stop $0 start - fi + fi RETVAL=$? - ;; + ;; status) - status ripd + status $cmd RETVAL=$? - ;; + ;; *) - echo $"Usage: $0 {start|stop|restart|reload|condrestart|status}" - exit 1 + echo $"Usage: $0 {start|stop|restart|reload|force-reload|condrestart|try-restart|status}" + exit 2 esac exit $RETVAL diff --git a/redhat/ripd.service b/redhat/ripd.service new file mode 100644 index 000000000..ed7f922cc --- /dev/null +++ b/redhat/ripd.service @@ -0,0 +1,16 @@ +[Unit] +Description=RIP routing daemon +BindsTo=zebra.service +Wants=network.target +After=zebra.service network-pre.target +Before=network.target +ConditionPathExists=/etc/quagga/ripd.conf + +[Service] +Type=forking +EnvironmentFile=/etc/sysconfig/quagga +ExecStart=/usr/sbin/ripd -d $RIPD_OPTS -f /etc/quagga/ripd.conf +Restart=on-abort + +[Install] +WantedBy=multi-user.target diff --git a/redhat/ripngd.init b/redhat/ripngd.init index 57ae928c7..88f346f86 100644 --- a/redhat/ripngd.init +++ b/redhat/ripngd.init @@ -1,11 +1,13 @@ #!/bin/bash -# -# chkconfig: 2345 16 84 -# description: A RIP routing engine for use with Zebra and IPv6 -# -# processname: ripngd +# chkconfig: - 16 84 # config: /etc/quagga/ripngd.conf +### BEGIN INIT INFO +# Provides: ripngd +# Short-Description: RIP routing engine for IPv6 +# Description: RIP routing engine for use with Zebra and IPv6 +### END INIT INFO + # source function library . /etc/rc.d/init.d/functions @@ -15,49 +17,56 @@ # quagga command line options . /etc/sysconfig/quagga -# Check that networking is up. -[ "${NETWORKING_IPV6}" = "no" ] && exit 0 - -# The process must be configured first. -[ -f /etc/quagga/ripngd.conf ] || exit 0 - RETVAL=0 -prog="ripngd" +PROG="ripngd" +cmd=ripngd +LOCK_FILE=/var/lock/subsys/ripngd +CONF_FILE=/etc/quagga/ripngd.conf case "$1" in start) - echo -n $"Starting $prog: " - daemon /usr/sbin/ripngd -d $RIPNGD_OPTS + # Check that networking is up. + [ "${NETWORKING}" = "no" ] && exit 1 + + # The process must be configured first. + [ -f $CONF_FILE ] || exit 6 + if [ `id -u` -ne 0 ]; then + echo $"Insufficient privilege" 1>&2 + exit 4 + fi + + echo -n $"Starting $PROG: " + daemon $cmd -d $RIPNGD_OPTS -f $CONF_FILE RETVAL=$? - [ $RETVAL -eq 0 ] && touch /var/lock/subsys/ripngd + [ $RETVAL -eq 0 ] && touch $LOCK_FILE echo ;; stop) - echo -n $"Shutting down $prog: " - killproc ripngd + echo -n $"Shutting down $PROG: " + killproc $cmd RETVAL=$? - [ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/ripngd + [ $RETVAL -eq 0 ] && rm -f $LOCK_FILE echo ;; - restart|reload) - $0 stop - $0 start + restart|reload|force-reload) + $0 stop + $0 start RETVAL=$? - ;; - condrestart) - if [ -f /var/lock/subsys/ripngd ]; then - $0 stop + ;; + condrestart|try-restart) + if [ -f $LOCK_FILE ]; then + $0 stop $0 start - fi + fi RETVAL=$? - ;; + ;; status) - status ripngd + status $cmd RETVAL=$? - ;; + ;; *) - echo $"Usage: $0 {start|stop|restart|reload|condrestart|status}" - exit 1 + echo $"Usage: $0 {start|stop|restart|reload|force-reload|condrestart|try-restart|status}" + exit 2 esac exit $RETVAL diff --git a/redhat/ripngd.service b/redhat/ripngd.service new file mode 100644 index 000000000..2519b31e8 --- /dev/null +++ b/redhat/ripngd.service @@ -0,0 +1,16 @@ +[Unit] +Description=RIP routing daemon for IPv6 +BindsTo=zebra.service +Wants=network.target +After=zebra.service network-pre.target +Before=network.target +ConditionPathExists=/etc/quagga/ripngd.conf + +[Service] +Type=forking +EnvironmentFile=/etc/sysconfig/quagga +ExecStart=/usr/sbin/ripngd -d $RIPNGD_OPTS -f /etc/quagga/ripngd.conf +Restart=on-abort + +[Install] +WantedBy=multi-user.target diff --git a/redhat/watchquagga.init b/redhat/watchquagga.init index 74299e373..dda350662 100644 --- a/redhat/watchquagga.init +++ b/redhat/watchquagga.init @@ -1,9 +1,11 @@ #!/bin/bash -# # chkconfig: 2345 17 83 -# description: A Quagga watchdog for use with Zebra -# -# processname: watchquagga + +### BEGIN INIT INFO +# Provides: watchquagga +# Short-Description: Quagga watchdog +# Description: Quagga watchdog for use with Zebra +### END INIT INFO # source function library . /etc/rc.d/init.d/functions @@ -14,49 +16,51 @@ # quagga command line options . /etc/sysconfig/quagga -# Check that networking is up. -[ "${NETWORKING}" = "no" ] && exit 0 - -# Check that there are daemons to be monitored. -[ -z "$WATCH_DAEMONS" ] && exit 0 - RETVAL=0 -prog="watchquagga" +PROG="watchquagga" +cmd=watchquagga +LOCK_FILE=/var/lock/subsys/watchquagga case "$1" in start) - echo -n $"Starting $prog: " - daemon /usr/sbin/watchquagga -d $WATCH_OPTS $WATCH_DAEMONS + # Check that networking is up. + [ "${NETWORKING}" = "no" ] && exit 1 + + # Check that there are daemons to be monitored. + [ -z "$WATCH_DAEMONS" ] && exit 1 + + echo -n $"Starting $PROG: " + daemon $cmd -d $WATCH_OPTS $WATCH_DAEMONS RETVAL=$? - [ $RETVAL -eq 0 ] && touch /var/lock/subsys/watchquagga + [ $RETVAL -eq 0 ] && touch $LOCK_FILE echo ;; stop) - echo -n $"Shutting down $prog: " - killproc watchquagga + echo -n $"Shutting down $PROG: " + killproc $cmd RETVAL=$? - [ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/watchquagga + [ $RETVAL -eq 0 ] && rm -f $LOCK_FILE echo ;; - restart|reload) - $0 stop - $0 start + restart|reload|force-reload) + $0 stop + $0 start RETVAL=$? - ;; - condrestart) - if [ -f /var/lock/subsys/watchquagga ]; then - $0 stop + ;; + condrestart|try-restart) + if [ -f $LOCK_FILE ]; then + $0 stop $0 start - fi + fi RETVAL=$? - ;; + ;; status) - status watchquagga + status $cmd RETVAL=$? - ;; + ;; *) - echo $"Usage: $0 {start|stop|restart|reload|condrestart|status}" - exit 1 + echo $"Usage: $0 {start|stop|restart|reload|force-reload|condrestart|try-restart|status}" + exit 2 esac exit $RETVAL diff --git a/redhat/zebra.init b/redhat/zebra.init index 8caae1f16..4242b16c4 100644 --- a/redhat/zebra.init +++ b/redhat/zebra.init @@ -1,64 +1,73 @@ #!/bin/bash -# -# chkconfig: 2345 15 85 -# description: GNU Zebra routing manager -# -# processname: zebra +# chkconfig: - 15 85 # config: /etc/quagga/zebra.conf +### BEGIN INIT INFO +# Provides: zebra +# Short-Description: GNU Zebra routing manager +# Description: GNU Zebra routing manager +### END INIT INFO + # source function library . /etc/rc.d/init.d/functions +# Get network config +. /etc/sysconfig/network + # quagga command line options . /etc/sysconfig/quagga -# Check that networking is up. -[ "${NETWORKING}" = "no" ] && exit 0 - -# The process must be configured first. -[ -f /etc/quagga/zebra.conf ] || exit 0 - RETVAL=0 - -prog="zebra" +PROG="zebra" +cmd=zebra +LOCK_FILE=/var/lock/subsys/zebra +CONF_FILE=/etc/quagga/zebra.conf case "$1" in start) - echo -n $"Starting $prog: " + # Check that networking is up. + [ "${NETWORKING}" = "no" ] && exit 1 + # The process must be configured first. + [ -f $CONF_FILE ] || exit 6 + if [ `id -u` -ne 0 ]; then + echo $"Insufficient privilege" 1>&2 + exit 4 + fi + + echo -n $"Starting $PROG: " /sbin/ip route flush proto zebra - - daemon /usr/sbin/zebra -d $ZEBRA_OPTS + daemon $cmd -d $ZEBRA_OPTS -f $CONF_FILE RETVAL=$? - [ $RETVAL -eq 0 ] && touch /var/lock/subsys/zebra + [ $RETVAL -eq 0 ] && touch $LOCK_FILE echo ;; stop) - echo -n $"Shutting down $prog: " - killproc zebra + echo -n $"Shutting down $PROG: " + killproc $cmd RETVAL=$? - [ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/zebra + [ $RETVAL -eq 0 ] && rm -f $LOCK_FILE echo ;; - restart|reload) - $0 stop - $0 start + restart|reload|force-reload) + $0 stop + $0 start RETVAL=$? - ;; - condrestart) - if [ -f /var/lock/subsys/zebra ]; then - $0 stop + ;; + condrestart|try-restart) + if [ -f $LOCK_FILE ]; then + $0 stop $0 start - fi + fi RETVAL=$? - ;; + ;; status) - status zebra + status $cmd RETVAL=$? - ;; + ;; *) - echo $"Usage: $0 {start|stop|restart|reload|condrestart|status}" - exit 1 + echo $"Usage: $0 {start|stop|restart|reload|force-reload|condrestart|try-restart|status}" + exit 2 esac exit $RETVAL diff --git a/redhat/zebra.service b/redhat/zebra.service new file mode 100644 index 000000000..f9107f1e2 --- /dev/null +++ b/redhat/zebra.service @@ -0,0 +1,16 @@ +[Unit] +Description=GNU Zebra routing manager +Wants=network.target +Before=network.target +After=network-pre.target +ConditionPathExists=/etc/quagga/zebra.conf + +[Service] +Type=forking +EnvironmentFile=-/etc/sysconfig/quagga +ExecStartPre=/sbin/ip route flush proto zebra +ExecStart=/usr/sbin/zebra -d $ZEBRA_OPTS -f /etc/quagga/zebra.conf +Restart=on-abort + +[Install] +WantedBy=multi-user.target diff --git a/release.sh b/release.sh new file mode 100755 index 000000000..d57ea981b --- /dev/null +++ b/release.sh @@ -0,0 +1,93 @@ +#!/bin/bash + +if [ $# -ne 2 ] ; then + echo "usage: $0 " + exit +fi + +errmsg () { + echo "Error occurred. To rerun you may first need to delete the tag". + exit 1 +} + +trap errmsg ERR + +REL=${1:?Release version must be given as first argument!} +PREV=${2:?Previous release version must be given as second argument!} + +TMPDIR=`mktemp -d /tmp/quagga-rel-XXXXXXXXX` + +if [ ! -d $TMPDIR ] ; then + echo "Problem making temp directory ${TMPDIR}!" + exit 1; +fi + +echo "Tagging branch head as release ${REL}" + +git tag -u 0x6FE57CA8C1A4AEA6 -m "Quagga release $REL" ${REL} + +mkdir -p ${TMPDIR}/a || exit 1 +mkdir -p ${TMPDIR}/verify || exit 1 + +echo "Making git archive" + +( git archive ${REL} | tar xC ${TMPDIR}/a ) || exit 1 + +git log ${PREV}..${REL} > ${TMPDIR}/a/${REL}.changelog.txt || exit 1 +git log --pretty=%s ${PREV}..${REL} > ${TMPDIR}/a/${REL}.subjects.txt || exit 1 + +cd ${TMPDIR}/a || exit 1 + +echo "Doing test build of archive file and making dist tarball" + +(autoreconf -i && ./configure && make -j && make dist-gzip) || exit 1 + +echo "Verifying dist tarball" + +cp ${REL}.tar.gz ${TMPDIR}/verify || exit 1 + +cd ${TMPDIR}/verify || exit 1 +tar -zxf ${REL}.tar.gz || exit 1 +cd ${REL} || exit 1 +autoreconf -i && ./configure && make -j + +cd ${TMPDIR}/a || exit 1 +gpg -u 0x6FE57CA8C1A4AEA6 -a --detach-sign ${REL}.tar.gz + +cat <<- EOF + +Release tagged as: ${REL} + +Release files are in ${TMPDIR}/a: + + ${TMPDIR}/a/${REL}.tar.gz + ${TMPDIR}/a/${REL}.tar.gz.asc + ${TMPDIR}/a/${REL}.changelog.txt + +If you need to redo the release, you must delete the tag first: + + git tag -d ${REL} + +To finish the release: + +* push the tag to savannah: + + git push tag ${REL} + +* Upload the 3 files to the savannah releases area: + + scp ${TMPDIR}/a/${REL}.tar.gz \ + ${TMPDIR}/a/${REL}.tar.gz.asc \ + ${TMPDIR}/a/${REL}.changelog.txt + @dl.sv.nongnu.org:/releases/quagga + +* Update the version list in bugzilla: + + https://bugzilla.quagga.net/editversions.cgi?action=add&product=Quagga + +* Add a news entry to the Savannah front page. The short list of commit + subjects (${TMPDIR}/a/${REL}.subjects.txt) may be useful here. + +* Email the quagga-dev and quagga-users lists + +EOF diff --git a/ripd/Makefile.am b/ripd/Makefile.am index 2fa26659e..571a4993d 100644 --- a/ripd/Makefile.am +++ b/ripd/Makefile.am @@ -1,11 +1,10 @@ ## Process this file with automake to produce Makefile.in. -INCLUDES = @INCLUDES@ -I.. -I$(top_srcdir) -I$(top_srcdir)/lib @SNMP_INCLUDES@ +AM_CPPFLAGS = -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib DEFS = @DEFS@ -DSYSCONFDIR=\"$(sysconfdir)/\" INSTALL_SDATA=@INSTALL@ -m 600 -AM_CFLAGS = $(PICFLAGS) -AM_LDFLAGS = $(PILDFLAGS) +AM_CFLAGS = $(WERROR) noinst_LIBRARIES = librip.a sbin_PROGRAMS = ripd diff --git a/ripd/rip_debug.c b/ripd/rip_debug.c index 662e5843a..a267874ad 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 810b71c0d..7521fc79d 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,13 +68,13 @@ 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, struct in_addr group, struct in_addr ifa, - unsigned int ifindex) + ifindex_t ifindex) { int ret; @@ -95,7 +95,7 @@ static int ipv4_multicast_leave (int sock, struct in_addr group, struct in_addr ifa, - unsigned int ifindex) + ifindex_t ifindex) { int ret; @@ -109,7 +109,9 @@ ipv4_multicast_leave (int sock, return ret; } - + +static void rip_interface_reset (struct rip_interface *); + /* Allocate new RIP's interface configuration. */ static struct rip_interface * rip_interface_new (void) @@ -117,19 +119,9 @@ rip_interface_new (void) struct rip_interface *ri; ri = XCALLOC (MTYPE_RIP_INTERFACE, sizeof (struct rip_interface)); - - /* Default authentication type is simple password for Cisco - compatibility. */ - ri->auth_type = RIP_NO_AUTH; - ri->md5_auth_len = RIP_AUTH_MD5_COMPAT_SIZE; - - /* Set default split-horizon behavior. If the interface is Frame - Relay or SMDS is enabled, the default value for split-horizon is - off. But currently Zebra does detect Frame Relay or SMDS - interface. So all interface is set to split horizon. */ - ri->split_horizon_default = RIP_SPLIT_HORIZON; - ri->split_horizon = ri->split_horizon_default; - + + rip_interface_reset (ri); + return ri; } @@ -380,7 +372,8 @@ if_check_address (struct in_addr addr) /* Inteface link down message processing. */ int -rip_interface_down (int command, struct zclient *zclient, zebra_size_t length) +rip_interface_down (int command, struct zclient *zclient, zebra_size_t length, + vrf_id_t vrf_id) { struct interface *ifp; struct stream *s; @@ -389,7 +382,7 @@ rip_interface_down (int command, struct zclient *zclient, zebra_size_t length) /* zebra_interface_state_read() updates interface structure in iflist. */ - ifp = zebra_interface_state_read(s); + ifp = zebra_interface_state_read (s, vrf_id); if (ifp == NULL) return 0; @@ -406,13 +399,14 @@ rip_interface_down (int command, struct zclient *zclient, zebra_size_t length) /* Inteface link up message processing */ int -rip_interface_up (int command, struct zclient *zclient, zebra_size_t length) +rip_interface_up (int command, struct zclient *zclient, zebra_size_t length, + vrf_id_t vrf_id) { struct interface *ifp; /* zebra_interface_state_read () updates interface structure in iflist. */ - ifp = zebra_interface_state_read (zclient->ibuf); + ifp = zebra_interface_state_read (zclient->ibuf, vrf_id); if (ifp == NULL) return 0; @@ -436,11 +430,12 @@ rip_interface_up (int command, struct zclient *zclient, zebra_size_t length) /* Inteface addition message from zebra. */ int -rip_interface_add (int command, struct zclient *zclient, zebra_size_t length) +rip_interface_add (int command, struct zclient *zclient, zebra_size_t length, + vrf_id_t vrf_id) { struct interface *ifp; - ifp = zebra_interface_add_read (zclient->ibuf); + ifp = zebra_interface_add_read (zclient->ibuf, vrf_id); if (IS_RIP_DEBUG_ZEBRA) zlog_debug ("interface add %s index %d flags %#llx metric %d mtu %d", @@ -466,7 +461,7 @@ rip_interface_add (int command, struct zclient *zclient, zebra_size_t length) int rip_interface_delete (int command, struct zclient *zclient, - zebra_size_t length) + zebra_size_t length, vrf_id_t vrf_id) { struct interface *ifp; struct stream *s; @@ -474,7 +469,7 @@ rip_interface_delete (int command, struct zclient *zclient, s = zclient->ibuf; /* zebra_interface_state_read() updates interface structure in iflist */ - ifp = zebra_interface_state_read(s); + ifp = zebra_interface_state_read (s, vrf_id); if (ifp == NULL) return 0; @@ -494,81 +489,82 @@ rip_interface_delete (int command, struct zclient *zclient, return 0; } -void -rip_interface_clean (void) +static void +rip_interface_clean (struct rip_interface *ri) { - struct listnode *node; - struct interface *ifp; - struct rip_interface *ri; + ri->enable_network = 0; + ri->enable_interface = 0; + ri->running = 0; - for (ALL_LIST_ELEMENTS_RO (iflist, node, ifp)) + if (ri->t_wakeup) { - ri = ifp->info; - - ri->enable_network = 0; - ri->enable_interface = 0; - ri->running = 0; - - if (ri->t_wakeup) - { - thread_cancel (ri->t_wakeup); - ri->t_wakeup = NULL; - } + thread_cancel (ri->t_wakeup); + ri->t_wakeup = NULL; } } void -rip_interface_reset (void) +rip_interfaces_clean (void) { struct listnode *node; struct interface *ifp; - struct rip_interface *ri; for (ALL_LIST_ELEMENTS_RO (iflist, node, ifp)) - { - ri = ifp->info; - - ri->enable_network = 0; - ri->enable_interface = 0; - ri->running = 0; + rip_interface_clean (ifp->info); +} - ri->ri_send = RI_RIP_UNSPEC; - ri->ri_receive = RI_RIP_UNSPEC; +static void +rip_interface_reset (struct rip_interface *ri) +{ + /* Default authentication type is simple password for Cisco + compatibility. */ + ri->auth_type = RIP_NO_AUTH; + ri->md5_auth_len = RIP_AUTH_MD5_COMPAT_SIZE; - ri->auth_type = RIP_NO_AUTH; + /* Set default split-horizon behavior. If the interface is Frame + Relay or SMDS is enabled, the default value for split-horizon is + off. But currently Zebra does detect Frame Relay or SMDS + interface. So all interface is set to split horizon. */ + ri->split_horizon_default = RIP_SPLIT_HORIZON; + ri->split_horizon = ri->split_horizon_default; - if (ri->auth_str) - { - free (ri->auth_str); - ri->auth_str = NULL; - } - if (ri->key_chain) - { - free (ri->key_chain); - ri->key_chain = NULL; - } + ri->ri_send = RI_RIP_UNSPEC; + ri->ri_receive = RI_RIP_UNSPEC; + + if (ri->auth_str) + { + free (ri->auth_str); + ri->auth_str = NULL; + } + if (ri->key_chain) + { + free (ri->key_chain); + ri->key_chain = NULL; + } - ri->split_horizon = RIP_NO_SPLIT_HORIZON; - ri->split_horizon_default = RIP_NO_SPLIT_HORIZON; + ri->list[RIP_FILTER_IN] = NULL; + ri->list[RIP_FILTER_OUT] = NULL; - ri->list[RIP_FILTER_IN] = NULL; - ri->list[RIP_FILTER_OUT] = NULL; + ri->prefix[RIP_FILTER_IN] = NULL; + ri->prefix[RIP_FILTER_OUT] = NULL; + + ri->recv_badpackets = 0; + ri->recv_badroutes = 0; + ri->sent_updates = 0; - ri->prefix[RIP_FILTER_IN] = NULL; - ri->prefix[RIP_FILTER_OUT] = NULL; - - if (ri->t_wakeup) - { - thread_cancel (ri->t_wakeup); - ri->t_wakeup = NULL; - } + ri->passive = 0; + + rip_interface_clean (ri); +} - ri->recv_badpackets = 0; - ri->recv_badroutes = 0; - ri->sent_updates = 0; +void +rip_interfaces_reset (void) +{ + struct listnode *node; + struct interface *ifp; - ri->passive = 0; - } + for (ALL_LIST_ELEMENTS_RO (iflist, node, ifp)) + rip_interface_reset (ifp->info); } int @@ -577,37 +573,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) @@ -660,19 +634,19 @@ rip_apply_address_add (struct connected *ifc) if ((rip_enable_if_lookup(ifc->ifp->name) >= 0) || (rip_enable_network_lookup2(ifc) >= 0)) rip_redistribute_add(ZEBRA_ROUTE_CONNECT, RIP_ROUTE_INTERFACE, - &address, ifc->ifp->ifindex, NULL, 0, 0); + &address, ifc->ifp->ifindex, NULL, 0, 0, 0); } int rip_interface_address_add (int command, struct zclient *zclient, - zebra_size_t length) + zebra_size_t length, vrf_id_t vrf_id) { struct connected *ifc; struct prefix *p; ifc = zebra_interface_address_read (ZEBRA_INTERFACE_ADDRESS_ADD, - zclient->ibuf); + zclient->ibuf, vrf_id); if (ifc == NULL) return 0; @@ -722,13 +696,13 @@ rip_apply_address_del (struct connected *ifc) { int rip_interface_address_delete (int command, struct zclient *zclient, - zebra_size_t length) + zebra_size_t length, vrf_id_t vrf_id) { struct connected *ifc; struct prefix *p; ifc = zebra_interface_address_read (ZEBRA_INTERFACE_ADDRESS_DELETE, - zclient->ibuf); + zclient->ibuf, vrf_id); if (ifc) { @@ -754,7 +728,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. */ @@ -971,7 +945,7 @@ rip_connect_set (struct interface *ifp, int set) (rip_enable_network_lookup2(connected) >= 0)) rip_redistribute_add (ZEBRA_ROUTE_CONNECT, RIP_ROUTE_INTERFACE, &address, connected->ifp->ifindex, - NULL, 0, 0); + NULL, 0, 0, 0); } else { rip_redistribute_delete (ZEBRA_ROUTE_CONNECT, RIP_ROUTE_INTERFACE, @@ -979,7 +953,7 @@ rip_connect_set (struct interface *ifp, int set) if (rip_redistribute_check (ZEBRA_ROUTE_CONNECT)) rip_redistribute_add (ZEBRA_ROUTE_CONNECT, RIP_ROUTE_REDISTRIBUTE, &address, connected->ifp->ifindex, - NULL, 0, 0); + NULL, 0, 0, 0); } } } @@ -1142,7 +1116,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 +1203,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 +1887,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) @@ -2077,7 +2051,6 @@ void rip_if_init (void) { /* Default initial size of interface vector. */ - if_init(); if_add_hook (IF_NEW_HOOK, rip_interface_new_hook); if_add_hook (IF_DELETE_HOOK, rip_interface_delete_hook); diff --git a/ripd/rip_interface.h b/ripd/rip_interface.h index 8926cce7b..d9dfbb7cc 100644 --- a/ripd/rip_interface.h +++ b/ripd/rip_interface.h @@ -21,11 +21,17 @@ #ifndef _QUAGGA_RIP_INTERFACE_H #define _QUAGGA_RIP_INTERFACE_H -extern int rip_interface_down (int , struct zclient *, zebra_size_t); -extern int rip_interface_up (int , struct zclient *, zebra_size_t); -extern int rip_interface_add (int , struct zclient *, zebra_size_t); -extern int rip_interface_delete (int , struct zclient *, zebra_size_t); -extern int rip_interface_address_add (int , struct zclient *, zebra_size_t); -extern int rip_interface_address_delete (int , struct zclient *, zebra_size_t); +extern int rip_interface_down (int , struct zclient *, zebra_size_t, + vrf_id_t); +extern int rip_interface_up (int , struct zclient *, zebra_size_t, + vrf_id_t); +extern int rip_interface_add (int , struct zclient *, zebra_size_t, + vrf_id_t); +extern int rip_interface_delete (int , struct zclient *, zebra_size_t, + vrf_id_t); +extern int rip_interface_address_add (int , struct zclient *, zebra_size_t, + vrf_id_t); +extern int rip_interface_address_delete (int , struct zclient *, zebra_size_t, + vrf_id_t); #endif /* _QUAGGA_RIP_INTERFACE_H */ diff --git a/ripd/rip_main.c b/ripd/rip_main.c index ccb5fa012..4ead9b0ed 100644 --- a/ripd/rip_main.c +++ b/ripd/rip_main.c @@ -33,6 +33,7 @@ #include "privs.h" #include "sigevent.h" #include "zclient.h" +#include "vrf.h" #include "ripd/ripd.h" @@ -126,7 +127,7 @@ Report bugs to %s\n", progname, ZEBRA_BUG_ADDRESS); exit (status); } - + /* SIGHUP handler. */ static void sighup (void) @@ -183,7 +184,7 @@ static struct quagga_signal_t ripd_signals[] = .handler = &sigint, }, }; - + /* Main routine of ripd. */ int main (int argc, char **argv) @@ -275,21 +276,19 @@ main (int argc, char **argv) /* Library initialization. */ zprivs_init (&ripd_privs); - signal_init (master, Q_SIGC(ripd_signals), ripd_signals); + signal_init (master, array_size(ripd_signals), ripd_signals); cmd_init (1); vty_init (master); memory_init (); keychain_init (); + vrf_init (); /* RIP related initialization. */ rip_init (); rip_if_init (); - rip_zclient_init (); + rip_zclient_init (master); 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 fd912ebad..6a3add640 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 cb87ea555..4e736944c 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 @@ -58,10 +58,10 @@ rip_route_match_add (struct vty *vty, struct route_map_index *index, switch (ret) { case RMAP_RULE_MISSING: - vty_out (vty, "%% Can't find rule.%s", VTY_NEWLINE); + vty_out (vty, "%% RIP Can't find rule.%s", VTY_NEWLINE); return CMD_WARNING; case RMAP_COMPILE_ERROR: - vty_out (vty, "%% Argument is malformed.%s", VTY_NEWLINE); + vty_out (vty, "%% RIP Argument is malformed.%s", VTY_NEWLINE); return CMD_WARNING; } } @@ -81,10 +81,10 @@ rip_route_match_delete (struct vty *vty, struct route_map_index *index, switch (ret) { case RMAP_RULE_MISSING: - vty_out (vty, "%% Can't find rule.%s", VTY_NEWLINE); + vty_out (vty, "%% RIP Can't find rule.%s", VTY_NEWLINE); return CMD_WARNING; case RMAP_COMPILE_ERROR: - vty_out (vty, "%% Argument is malformed.%s", VTY_NEWLINE); + vty_out (vty, "%% RIP Argument is malformed.%s", VTY_NEWLINE); return CMD_WARNING; } } @@ -104,7 +104,7 @@ rip_route_set_add (struct vty *vty, struct route_map_index *index, switch (ret) { case RMAP_RULE_MISSING: - vty_out (vty, "%% Can't find rule.%s", VTY_NEWLINE); + vty_out (vty, "%% RIP Can't find rule.%s", VTY_NEWLINE); return CMD_WARNING; case RMAP_COMPILE_ERROR: /* rip, ripng and other protocols share the set metric command @@ -112,7 +112,7 @@ rip_route_set_add (struct vty *vty, struct route_map_index *index, if metric is out of range for rip and ripng, it is not for other protocols. Do not return an error */ if (strcmp(command, "metric")) { - vty_out (vty, "%% Argument is malformed.%s", VTY_NEWLINE); + vty_out (vty, "%% RIP Argument is malformed.%s", VTY_NEWLINE); return CMD_WARNING; } } @@ -133,10 +133,10 @@ rip_route_set_delete (struct vty *vty, struct route_map_index *index, switch (ret) { case RMAP_RULE_MISSING: - vty_out (vty, "%% Can't find rule.%s", VTY_NEWLINE); + vty_out (vty, "%% RIP Can't find rule.%s", VTY_NEWLINE); return CMD_WARNING; case RMAP_COMPILE_ERROR: - vty_out (vty, "%% Argument is malformed.%s", VTY_NEWLINE); + vty_out (vty, "%% RIP Argument is malformed.%s", VTY_NEWLINE); return CMD_WARNING; } } @@ -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 @@ -463,7 +463,7 @@ static route_map_result_t route_match_tag (void *rule, struct prefix *prefix, route_map_object_t type, void *object) { - u_short *tag; + route_tag_t *tag; struct rip_info *rinfo; if (type == RMAP_RIP) @@ -480,34 +480,15 @@ route_match_tag (void *rule, struct prefix *prefix, return RMAP_NOMATCH; } -/* Route map `match tag' match statement. `arg' is TAG value */ -static void * -route_match_tag_compile (const char *arg) -{ - u_short *tag; - - tag = XMALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof (u_short)); - *tag = atoi (arg); - - return tag; -} - -/* Free route map's compiled `match tag' value. */ -static void -route_match_tag_free (void *rule) -{ - XFREE (MTYPE_ROUTE_MAP_COMPILED, rule); -} - /* Route map commands for tag matching. */ -struct route_map_rule_cmd route_match_tag_cmd = +static struct route_map_rule_cmd route_match_tag_cmd = { "tag", route_match_tag, - route_match_tag_compile, - route_match_tag_free + route_map_rule_tag_compile, + route_map_rule_tag_free, }; - + /* `set metric METRIC' */ /* Set metric to attribute. */ @@ -674,7 +655,7 @@ static route_map_result_t route_set_tag (void *rule, struct prefix *prefix, route_map_object_t type, void *object) { - u_short *tag; + route_tag_t *tag; struct rip_info *rinfo; if(type == RMAP_RIP) @@ -690,35 +671,15 @@ route_set_tag (void *rule, struct prefix *prefix, return RMAP_OKAY; } -/* Route map `tag' compile function. Given string is converted - to u_short. */ -static void * -route_set_tag_compile (const char *arg) -{ - u_short *tag; - - tag = XMALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof (u_short)); - *tag = atoi (arg); - - return tag; -} - -/* Free route map's compiled `ip nexthop' value. */ -static void -route_set_tag_free (void *rule) -{ - XFREE (MTYPE_ROUTE_MAP_COMPILED, rule); -} - /* Route map commands for tag set. */ static struct route_map_rule_cmd route_set_tag_cmd = { "tag", route_set_tag, - route_set_tag_compile, - route_set_tag_free + route_map_rule_tag_compile, + route_map_rule_tag_free }; - + #define MATCH_STR "Match values from routing table\n" #define SET_STR "Set values in destination routing protocol\n" @@ -937,7 +898,7 @@ ALIAS (no_match_ip_address_prefix_list, DEFUN (match_tag, match_tag_cmd, - "match tag <0-65535>", + "match tag <1-4294967295>", MATCH_STR "Match tag of route\n" "Metric value\n") @@ -960,7 +921,7 @@ DEFUN (no_match_tag, ALIAS (no_match_tag, no_match_tag_val_cmd, - "no match tag <0-65535>", + "no match tag <1-4294967295>", NO_STR MATCH_STR "Match tag of route\n" @@ -1000,11 +961,18 @@ DEFUN (no_set_metric, ALIAS (no_set_metric, no_set_metric_val_cmd, - "no set metric (<0-4294967295>|<+/-metric>)", + "no set metric <0-4294967295>", + NO_STR + SET_STR + "Metric value for destination routing protocol\n" + "Metric value\n") + +ALIAS (no_set_metric, + no_set_metric_addsub_cmd, + "no set metric <+/-metric>", NO_STR SET_STR "Metric value for destination routing protocol\n" - "Metric value\n" "Add or subtract metric\n") DEFUN (set_ip_nexthop, @@ -1053,7 +1021,7 @@ ALIAS (no_set_ip_nexthop, DEFUN (set_tag, set_tag_cmd, - "set tag <0-65535>", + "set tag <1-4294967295>", SET_STR "Tag value for routing protocol\n" "Tag value\n") @@ -1076,7 +1044,7 @@ DEFUN (no_set_tag, ALIAS (no_set_tag, no_set_tag_val_cmd, - "no set tag <0-65535>", + "no set tag <1-4294967295>", NO_STR SET_STR "Tag value for routing protocol\n" @@ -1135,6 +1103,7 @@ rip_route_map_init () install_element (RMAP_NODE, &set_metric_addsub_cmd); install_element (RMAP_NODE, &no_set_metric_cmd); install_element (RMAP_NODE, &no_set_metric_val_cmd); + install_element (RMAP_NODE, &no_set_metric_addsub_cmd); install_element (RMAP_NODE, &set_ip_nexthop_cmd); install_element (RMAP_NODE, &no_set_ip_nexthop_cmd); install_element (RMAP_NODE, &no_set_ip_nexthop_val_cmd); diff --git a/ripd/rip_snmp.c b/ripd/rip_snmp.c index 61c47c71d..2c7cd2c12 100644 --- a/ripd/rip_snmp.c +++ b/ripd/rip_snmp.c @@ -22,14 +22,8 @@ #include #ifdef HAVE_SNMP -#ifdef HAVE_NETSNMP #include #include -#else -#include -#include -#include -#endif #include "if.h" #include "log.h" @@ -39,7 +33,7 @@ #include "smux.h" #include "ripd/ripd.h" - + /* RIPv2-MIB. */ #define RIPV2MIB 1,3,6,1,2,1,23 @@ -82,7 +76,7 @@ #define TIMETICKS ASN_TIMETICKS #define IPADDRESS ASN_IPADDRESS #define STRING ASN_OCTET_STR - + /* Define SNMP local variables. */ SNMP_LOCAL_VARIABLES @@ -155,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) @@ -301,7 +295,7 @@ rip2PeerLookup (struct variable *v, oid name[], size_t *length, peer = rip_peer_lookup (addr); - if (peer->domain == name[v->namelen + sizeof (struct in_addr)]) + if (peer->domain == (int)name[v->namelen + sizeof (struct in_addr)]) return peer; return NULL; @@ -317,8 +311,8 @@ rip2PeerLookup (struct variable *v, oid name[], size_t *length, peer = rip_peer_lookup (addr); if (peer) { - if ((len < sizeof (struct in_addr) + 1) || - (peer->domain > name[v->namelen + sizeof (struct in_addr)])) + if ((len < (int)sizeof (struct in_addr) + 1) || + (peer->domain > (int)name[v->namelen + sizeof (struct in_addr)])) { oid_copy_addr (name + v->namelen, &peer->addr, sizeof (struct in_addr)); @@ -351,6 +345,10 @@ rip2IfStatEntry (struct variable *v, oid name[], size_t *length, static struct in_addr addr; static long valid = SNMP_VALID; + if (smux_header_table(v, name, length, exact, var_len, write_method) + == MATCH_FAILED) + return NULL; + memset (&addr, 0, sizeof (struct in_addr)); /* Lookup interface. */ @@ -454,6 +452,10 @@ rip2IfConfAddress (struct variable *v, oid name[], size_t *length, struct interface *ifp; struct rip_interface *ri; + if (smux_header_table(v, name, length, exact, val_len, write_method) + == MATCH_FAILED) + return NULL; + memset (&addr, 0, sizeof (struct in_addr)); /* Lookup interface. */ @@ -524,6 +526,10 @@ rip2PeerTable (struct variable *v, oid name[], size_t *length, struct rip_peer *peer; + if (smux_header_table(v, name, length, exact, val_len, write_method) + == MATCH_FAILED) + return NULL; + memset (&addr, 0, sizeof (struct in_addr)); /* Lookup interface. */ diff --git a/ripd/rip_zebra.c b/ripd/rip_zebra.c index 199e85e8c..0b51af5a8 100644 --- a/ripd/rip_zebra.c +++ b/ripd/rip_zebra.c @@ -23,84 +23,126 @@ #include "command.h" #include "prefix.h" +#include "table.h" #include "stream.h" +#include "memory.h" #include "routemap.h" #include "zclient.h" #include "log.h" +#include "vrf.h" #include "ripd/ripd.h" #include "ripd/rip_debug.h" #include "ripd/rip_interface.h" /* 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]) + if (vrf_bitmap_check (zclient->redist[ZEBRA_ROUTE_RIP], VRF_DEFAULT)) { + api.vrf_id = VRF_DEFAULT; api.type = ZEBRA_ROUTE_RIP; 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; + } + + if (rinfo->tag) + { + SET_FLAG (api.message, ZAPI_MESSAGE_TAG); + api.tag = rinfo->tag; + } - 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. */ static int -rip_zebra_read_ipv4 (int command, struct zclient *zclient, zebra_size_t length) +rip_zebra_read_ipv4 (int command, struct zclient *zclient, zebra_size_t length, + vrf_id_t vrf_id) { struct stream *s; struct zapi_ipv4 api; unsigned long ifindex; struct in_addr nexthop; struct prefix_ipv4 p; - + unsigned char plength = 0; + s = zclient->ibuf; ifindex = 0; nexthop.s_addr = 0; @@ -113,7 +155,8 @@ rip_zebra_read_ipv4 (int command, struct zclient *zclient, zebra_size_t length) /* IPv4 prefix. */ memset (&p, 0, sizeof (struct prefix_ipv4)); p.family = AF_INET; - p.prefixlen = stream_getc (s); + plength = stream_getc (s); + p.prefixlen = MIN(IPV4_MAX_PREFIXLEN, plength); stream_get (&p.prefix, s, PSIZE (p.prefixlen)); /* Nexthop, ifindex, distance, metric. */ @@ -136,11 +179,16 @@ rip_zebra_read_ipv4 (int command, struct zclient *zclient, zebra_size_t length) else api.metric = 0; + if (CHECK_FLAG (api.message, ZAPI_MESSAGE_TAG)) + api.tag = stream_getl (s); + else + api.tag = 0; + /* Then fetch IPv4 prefixes. */ if (command == ZEBRA_IPV4_ROUTE_ADD) rip_redistribute_add (api.type, RIP_ROUTE_REDISTRIBUTE, &p, ifindex, - &nexthop, api.metric, api.distance); - else + &nexthop, api.metric, api.distance, api.tag); + else rip_redistribute_delete (api.type, RIP_ROUTE_REDISTRIBUTE, &p, ifindex); return 0; @@ -196,7 +244,7 @@ rip_routemap_unset (int type, const char *name) return 0; } - + /* Redistribution types */ static struct { int type; @@ -240,10 +288,10 @@ DEFUN (no_router_zebra, static int rip_redistribute_set (int type) { - if (zclient->redist[type]) + if (vrf_bitmap_check (zclient->redist[type], VRF_DEFAULT)) return CMD_SUCCESS; - zclient->redist[type] = 1; + vrf_bitmap_set (zclient->redist[type], VRF_DEFAULT); if (zclient->sock > 0) zebra_redistribute_send (ZEBRA_REDISTRIBUTE_ADD, zclient, type); @@ -255,13 +303,14 @@ rip_redistribute_set (int type) static int rip_redistribute_unset (int type) { - if (! zclient->redist[type]) + if (! vrf_bitmap_check (zclient->redist[type], VRF_DEFAULT)) return CMD_SUCCESS; - zclient->redist[type] = 0; + vrf_bitmap_unset (zclient->redist[type], VRF_DEFAULT); if (zclient->sock > 0) - zebra_redistribute_send (ZEBRA_REDISTRIBUTE_DELETE, zclient, type); + zebra_redistribute_send (ZEBRA_REDISTRIBUTE_DELETE, zclient, type, + VRF_DEFAULT); /* Remove the routes from RIP table. */ rip_redistribute_withdraw (type); @@ -272,7 +321,7 @@ rip_redistribute_unset (int type) int rip_redistribute_check (int type) { - return (zclient->redist[type]); + return vrf_bitmap_check (zclient->redist[type], VRF_DEFAULT); } void @@ -282,13 +331,14 @@ rip_redistribute_clean (void) for (i = 0; redist_type[i].str; i++) { - if (zclient->redist[redist_type[i].type]) + if (vrf_bitmap_check (zclient->redist[redist_type[i].type], VRF_DEFAULT)) { if (zclient->sock > 0) zebra_redistribute_send (ZEBRA_REDISTRIBUTE_DELETE, - zclient, redist_type[i].type); + zclient, redist_type[i].type, + VRF_DEFAULT); - zclient->redist[redist_type[i].type] = 0; + vrf_bitmap_unset (zclient->redist[redist_type[i].type], VRF_DEFAULT); /* Remove the routes from RIP table. */ rip_redistribute_withdraw (redist_type[i].type); @@ -302,7 +352,7 @@ DEFUN (rip_redistribute_rip, "Redistribute information from another routing protocol\n" "Routing Information Protocol (RIP)\n") { - zclient->redist[ZEBRA_ROUTE_RIP] = 1; + vrf_bitmap_set (zclient->redist[ZEBRA_ROUTE_RIP], VRF_DEFAULT); return CMD_SUCCESS; } @@ -313,7 +363,7 @@ DEFUN (no_rip_redistribute_rip, "Redistribute information from another routing protocol\n" "Routing Information Protocol (RIP)\n") { - zclient->redist[ZEBRA_ROUTE_RIP] = 0; + vrf_bitmap_unset (zclient->redist[ZEBRA_ROUTE_RIP], VRF_DEFAULT); return CMD_SUCCESS; } @@ -331,7 +381,7 @@ DEFUN (rip_redistribute_type, redist_type[i].str_min_len) == 0) { zclient_redistribute (ZEBRA_REDISTRIBUTE_ADD, zclient, - redist_type[i].type); + redist_type[i].type, VRF_DEFAULT); return CMD_SUCCESS; } } @@ -384,7 +434,8 @@ DEFUN (rip_redistribute_type_routemap, redist_type[i].str_min_len) == 0) { rip_routemap_set (redist_type[i].type, argv[1]); - zclient_redistribute (ZEBRA_REDISTRIBUTE_ADD, zclient, redist_type[i].type); + zclient_redistribute (ZEBRA_REDISTRIBUTE_ADD, zclient, redist_type[i].type, + VRF_DEFAULT); return CMD_SUCCESS; } } @@ -442,7 +493,8 @@ DEFUN (rip_redistribute_type_metric, redist_type[i].str_min_len) == 0) { rip_redistribute_metric_set (redist_type[i].type, metric); - zclient_redistribute (ZEBRA_REDISTRIBUTE_ADD, zclient, redist_type[i].type); + zclient_redistribute (ZEBRA_REDISTRIBUTE_ADD, zclient, redist_type[i].type, + VRF_DEFAULT); return CMD_SUCCESS; } } @@ -503,7 +555,8 @@ DEFUN (rip_redistribute_type_metric_routemap, { rip_redistribute_metric_set (redist_type[i].type, metric); rip_routemap_set (redist_type[i].type, argv[2]); - zclient_redistribute (ZEBRA_REDISTRIBUTE_ADD, zclient, redist_type[i].type); + zclient_redistribute (ZEBRA_REDISTRIBUTE_ADD, zclient, redist_type[i].type, + VRF_DEFAULT); return CMD_SUCCESS; } } @@ -551,7 +604,7 @@ DEFUN (no_rip_redistribute_type_metric_routemap, return CMD_WARNING; } - + /* Default information originate. */ DEFUN (rip_default_information_originate, @@ -570,7 +623,7 @@ DEFUN (rip_default_information_originate, rip->default_information = 1; rip_redistribute_add (ZEBRA_ROUTE_RIP, RIP_ROUTE_DEFAULT, &p, 0, - NULL, 0, 0); + NULL, 0, 0, 0); } return CMD_SUCCESS; @@ -597,7 +650,7 @@ DEFUN (no_rip_default_information_originate, return CMD_SUCCESS; } - + /* RIP configuration write function. */ static int config_write_zebra (struct vty *vty) @@ -607,7 +660,7 @@ config_write_zebra (struct vty *vty) vty_out (vty, "no router zebra%s", VTY_NEWLINE); return 1; } - else if (! zclient->redist[ZEBRA_ROUTE_RIP]) + else if (! vrf_bitmap_check (zclient->redist[ZEBRA_ROUTE_RIP], VRF_DEFAULT)) { vty_out (vty, "router zebra%s", VTY_NEWLINE); vty_out (vty, " no redistribute rip%s", VTY_NEWLINE); @@ -622,7 +675,8 @@ config_write_rip_redistribute (struct vty *vty, int config_mode) int i; for (i = 0; i < ZEBRA_ROUTE_MAX; i++) - if (i != zclient->redist_default && zclient->redist[i]) + if (i != zclient->redist_default && + vrf_bitmap_check (zclient->redist[i], VRF_DEFAULT)) { if (config_mode) { @@ -662,12 +716,19 @@ static struct cmd_node zebra_node = "%s(config-router)# ", }; +static void +rip_zebra_connected (struct zclient *zclient) +{ + zclient_send_requests (zclient, VRF_DEFAULT); +} + void -rip_zclient_init () +rip_zclient_init (struct thread_master *master) { /* Set default value to the zebra client structure. */ - zclient = zclient_new (); + zclient = zclient_new (master); zclient_init (zclient, ZEBRA_ROUTE_RIP); + zclient->zebra_connected = rip_zebra_connected; zclient->interface_add = rip_interface_add; zclient->interface_delete = rip_interface_delete; zclient->interface_address_add = rip_interface_address_add; diff --git a/ripd/ripd.c b/ripd/ripd.c index 5a6dbc8c9..c073ecab6 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,146 +152,198 @@ 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 0; + return rinfo; } -static void -rip_timeout_update (struct rip_info *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) { - if (rinfo->metric != RIP_METRIC_INFINITY) + 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_TIMER_OFF (rinfo->t_timeout); - RIP_TIMER_ON (rinfo->t_timeout, rip_timeout, rip->timeout_time); + 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; } -static int -rip_incoming_filter (struct prefix_ipv4 *p, struct rip_interface *ri) +/* 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 distribute *dist; - struct access_list *alist; - struct prefix_list *plist; + struct route_node *rp = rinfo->rp; + struct list *list = (struct list *)rp->info; - /* Input distribute-list filtering. */ - if (ri->list[RIP_FILTER_IN]) + RIP_TIMER_OFF (rinfo->t_timeout); + + if (listcount (list) > 1) { - if (access_list_apply (ri->list[RIP_FILTER_IN], - (struct prefix *) p) == FILTER_DENY) - { - if (IS_RIP_DEBUG_PACKET) - zlog_debug ("%s/%d filtered by distribute in", - inet_ntoa (p->prefix), p->prefixlen); - return -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; } - if (ri->prefix[RIP_FILTER_IN]) + else { - if (prefix_list_apply (ri->prefix[RIP_FILTER_IN], - (struct prefix *) p) == PREFIX_DENY) - { - if (IS_RIP_DEBUG_PACKET) - zlog_debug ("%s/%d filtered by prefix-list in", - inet_ntoa (p->prefix), p->prefixlen); - return -1; - } + 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); } - /* All interface filter check. */ - dist = distribute_lookup (NULL); - if (dist) + /* 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; +} + +static void +rip_timeout_update (struct rip_info *rinfo) +{ + if (rinfo->metric != RIP_METRIC_INFINITY) { - if (dist->list[DISTRIBUTE_IN]) - { - alist = access_list_lookup (AFI_IP, dist->list[DISTRIBUTE_IN]); - - if (alist) - { - if (access_list_apply (alist, - (struct prefix *) p) == FILTER_DENY) - { - if (IS_RIP_DEBUG_PACKET) - zlog_debug ("%s/%d filtered by distribute in", - inet_ntoa (p->prefix), p->prefixlen); - return -1; - } - } - } - if (dist->prefix[DISTRIBUTE_IN]) - { - plist = prefix_list_lookup (AFI_IP, dist->prefix[DISTRIBUTE_IN]); - - if (plist) - { - if (prefix_list_apply (plist, - (struct prefix *) p) == PREFIX_DENY) - { - if (IS_RIP_DEBUG_PACKET) - zlog_debug ("%s/%d filtered by prefix-list in", - inet_ntoa (p->prefix), p->prefixlen); - return -1; - } - } - } + RIP_TIMER_OFF (rinfo->t_timeout); + RIP_TIMER_ON (rinfo->t_timeout, rip_timeout, rip->timeout_time); } - return 0; } static int -rip_outgoing_filter (struct prefix_ipv4 *p, struct rip_interface *ri) +rip_filter (int rip_distribute, struct prefix_ipv4 *p, struct rip_interface *ri) { struct distribute *dist; struct access_list *alist; struct prefix_list *plist; + int distribute = rip_distribute == RIP_FILTER_OUT ? + DISTRIBUTE_V4_OUT : DISTRIBUTE_V4_IN; + const char *inout = rip_distribute == RIP_FILTER_OUT ? "out" : "in"; - if (ri->list[RIP_FILTER_OUT]) + /* Input distribute-list filtering. */ + if (ri->list[rip_distribute]) { - if (access_list_apply (ri->list[RIP_FILTER_OUT], + if (access_list_apply (ri->list[rip_distribute], (struct prefix *) p) == FILTER_DENY) { if (IS_RIP_DEBUG_PACKET) - zlog_debug ("%s/%d is filtered by distribute out", - inet_ntoa (p->prefix), p->prefixlen); + zlog_debug ("%s/%d filtered by distribute %s", + inet_ntoa (p->prefix), p->prefixlen, inout); return -1; } } - if (ri->prefix[RIP_FILTER_OUT]) + if (ri->prefix[rip_distribute]) { - if (prefix_list_apply (ri->prefix[RIP_FILTER_OUT], + if (prefix_list_apply (ri->prefix[rip_distribute], (struct prefix *) p) == PREFIX_DENY) { if (IS_RIP_DEBUG_PACKET) - zlog_debug ("%s/%d is filtered by prefix-list out", - inet_ntoa (p->prefix), p->prefixlen); + zlog_debug ("%s/%d filtered by prefix-list %s", + inet_ntoa (p->prefix), p->prefixlen, inout); return -1; } } @@ -295,34 +352,33 @@ rip_outgoing_filter (struct prefix_ipv4 *p, struct rip_interface *ri) dist = distribute_lookup (NULL); if (dist) { - if (dist->list[DISTRIBUTE_OUT]) + if (dist->list[distribute]) { - alist = access_list_lookup (AFI_IP, dist->list[DISTRIBUTE_OUT]); - + alist = access_list_lookup (AFI_IP, dist->list[distribute]); + if (alist) { - if (access_list_apply (alist, - (struct prefix *) p) == FILTER_DENY) + if (access_list_apply (alist, (struct prefix *) p) == FILTER_DENY) { if (IS_RIP_DEBUG_PACKET) - zlog_debug ("%s/%d filtered by distribute out", - inet_ntoa (p->prefix), p->prefixlen); + zlog_debug ("%s/%d filtered by distribute %s", + inet_ntoa (p->prefix), p->prefixlen, inout); return -1; } } } - if (dist->prefix[DISTRIBUTE_OUT]) + if (dist->prefix[distribute]) { - plist = prefix_list_lookup (AFI_IP, dist->prefix[DISTRIBUTE_OUT]); - + plist = prefix_list_lookup (AFI_IP, dist->prefix[distribute]); + if (plist) { if (prefix_list_apply (plist, (struct prefix *) p) == PREFIX_DENY) { if (IS_RIP_DEBUG_PACKET) - zlog_debug ("%s/%d filtered by prefix-list out", - inet_ntoa (p->prefix), p->prefixlen); + zlog_debug ("%s/%d filtered by prefix-list %s", + inet_ntoa (p->prefix), p->prefixlen, inout); return -1; } } @@ -365,13 +421,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)); @@ -385,25 +441,24 @@ rip_rte_process (struct rte *rte, struct sockaddr_in *from, /* Apply input filters. */ ri = ifp->info; - ret = rip_incoming_filter (&p, ri); + ret = rip_filter (RIP_FILTER_IN, &p, ri); 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 +488,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 +510,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 +583,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 +622,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 +635,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); } @@ -753,9 +746,9 @@ rip_packet_dump (struct rip_packet *packet, int size, const char *sndrcv) zlog_debug (" family 0x%X type %d (MD5 data)", ntohs (rte->family), ntohs (rte->tag)); zlog_debug (" MD5: %02X%02X%02X%02X%02X%02X%02X%02X" - "%02X%02X%02X%02X%02X%02X%02X", + "%02X%02X%02X%02X%02X%02X%02X%02X", p[0], p[1], p[2], p[3], p[4], p[5], p[6], - p[7], p[9], p[10], p[11], p[12], p[13], + p[7], p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]); } else @@ -1107,7 +1100,8 @@ rip_response_process (struct rip_packet *packet, int size, struct prefix_ipv4 ifaddr; struct prefix_ipv4 ifaddrclass; int subnetted; - + + memset(&ifaddr, 0, sizeof(ifaddr)); /* We don't know yet. */ subnetted = -1; @@ -1318,17 +1312,19 @@ rip_response_process (struct rip_packet *packet, int size, rip_peer_bad_route (from); continue; } - - /* Default route's netmask is ignored. */ + + /* Default route sanity check */ if (packet->version == RIPv2 - && (rte->prefix.s_addr == 0) - && (rte->mask.s_addr != 0)) - { - if (IS_RIP_DEBUG_EVENT) - zlog_debug ("Default route with non-zero netmask. Set zero to netmask"); - rte->mask.s_addr = 0; - } - + && (rte->mask.s_addr == 0) + && (rte->prefix.s_addr != 0)) + { + if (IS_RIP_DEBUG_EVENT) + zlog_warn ("Malformed route, zero netmask " + "with non-zero addr - dropping route!"); + rip_peer_bad_route (from); + continue; + } + /* Routing table updates. */ rip_rte_process (rte, from, ifc->ifp); } @@ -1475,6 +1471,7 @@ rip_send_packet (u_char * buf, int size, struct sockaddr_in *to, sin.sin_addr.s_addr = htonl (INADDR_RIP_GROUP); /* multicast send should bind to local interface address */ + memset (&from, 0, sizeof (from)); from.sin_family = AF_INET; from.sin_port = htons (RIP_PORT_DEFAULT); from.sin_addr = ifc->address->u.prefix4; @@ -1514,12 +1511,14 @@ rip_send_packet (u_char * buf, int size, struct sockaddr_in *to, /* Add redistributed route to RIP table. */ void rip_redistribute_add (int type, int sub_type, struct prefix_ipv4 *p, - unsigned int ifindex, struct in_addr *nexthop, - unsigned int metric, unsigned char distance) + ifindex_t ifindex, struct in_addr *nexthop, + unsigned int metric, unsigned char distance, + route_tag_t tag) { 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); @@ -1528,10 +1527,23 @@ 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; + if (tag <= UINT16_MAX) /* RIP only supports 16 bit tags */ + newinfo.tag = tag; + 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) @@ -1553,35 +1565,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) @@ -1594,14 +1582,13 @@ rip_redistribute_add (int type, int sub_type, struct prefix_ipv4 *p, ifindex2ifname(ifindex)); } - rip_event (RIP_TRIGGERED_UPDATE, 0); } /* Delete redistributed route from RIP table. */ void rip_redistribute_delete (int type, int sub_type, struct prefix_ipv4 *p, - unsigned int ifindex) + ifindex_t ifindex) { int ret; struct route_node *rp; @@ -1614,27 +1601,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); } } @@ -1682,16 +1675,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); } @@ -1716,7 +1699,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); } @@ -1747,7 +1730,7 @@ setsockopt_pktinfo (int sock) /* Read RIP packet by recvmsg function. */ int rip_recvmsg (int sock, u_char *buf, int size, struct sockaddr_in *from, - int *ifindex) + ifindex_t *ifindex) { int ret; struct msghdr msg; @@ -1788,7 +1771,7 @@ rip_read_new (struct thread *t) int sock; char buf[RIP_PACKET_MAXSIZ]; struct sockaddr_in from; - unsigned int ifindex; + ifindex_t ifindex; /* Fetch socket then register myself. */ sock = THREAD_FD (t); @@ -2151,6 +2134,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) @@ -2167,7 +2152,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; @@ -2208,8 +2193,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. */ @@ -2245,7 +2231,7 @@ rip_output_process (struct connected *ifc, struct sockaddr_in *to, p = (struct prefix_ipv4 *) &rp->p; /* Apply output filters. */ - ret = rip_outgoing_filter (p, ri); + ret = rip_filter (RIP_FILTER_OUT, p, ri); if (ret < 0) continue; @@ -2268,11 +2254,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; } @@ -2305,7 +2302,7 @@ rip_output_process (struct connected *ifc, struct sockaddr_in *to, if (IS_RIP_DEBUG_PACKET) zlog_debug ("RIP %s/%d is filtered by route-map out", inet_ntoa (p->prefix), p->prefixlen); - continue; + continue; } } @@ -2366,12 +2363,17 @@ 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 && - prefix_match((struct prefix *)p, ifc->address)) - rinfo->metric_out = RIP_METRIC_INFINITY; + 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; + } } /* Prepare preamble, auth headers, if needs be */ @@ -2511,7 +2513,7 @@ rip_update_process (int route_type) if (IS_RIP_DEBUG_EVENT) zlog_debug("SEND UPDATE to %s ifindex %d", - (ifp->name ? ifp->name : "_unknown_"), ifp->ifindex); + ifp->name, ifp->ifindex); /* send update on each connected network */ for (ALL_LIST_ELEMENTS (ifp->connected, ifnode, ifnnode, connected)) @@ -2520,7 +2522,7 @@ rip_update_process (int route_type) { if (vsend & RIPv1) rip_update_interface (connected, RIPv1, route_type); - if ((vsend & RIPv2) && if_is_multicast(ifp)) + if (vsend & RIPv2) rip_update_interface (connected, RIPv2, route_type); } } @@ -2591,12 +2593,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. */ @@ -2661,14 +2669,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) { @@ -2772,7 +2782,7 @@ rip_request_send (struct sockaddr_in *to, struct interface *ifp, } return sizeof (rip_packet); } - + static int rip_update_jitter (unsigned long time) { @@ -2789,7 +2799,7 @@ rip_update_jitter (unsigned long time) if (jitter_input < JITTER_BOUND) jitter_input = JITTER_BOUND; - jitter = (((rand () % ((jitter_input * 2) + 1)) - jitter_input)); + jitter = (((random () % ((jitter_input * 2) + 1)) - jitter_input)); return jitter/JITTER_BOUND; } @@ -2826,7 +2836,7 @@ rip_event (enum rip_event event, int sock) break; } } - + DEFUN (router_rip, router_rip_cmd, "router rip", @@ -2934,7 +2944,7 @@ DEFUN (rip_route, node->info = (char *)"static"; - rip_redistribute_add (ZEBRA_ROUTE_RIP, RIP_ROUTE_STATIC, &p, 0, NULL, 0, 0); + rip_redistribute_add (ZEBRA_ROUTE_RIP, RIP_ROUTE_STATIC, &p, 0, NULL, 0, 0, 0); return CMD_SUCCESS; } @@ -2981,12 +2991,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 @@ -3102,7 +3115,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 @@ -3179,7 +3192,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; @@ -3190,8 +3202,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) { @@ -3370,7 +3380,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) @@ -3425,7 +3509,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; @@ -3438,7 +3524,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; @@ -3675,6 +3762,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) @@ -3694,7 +3785,7 @@ static struct cmd_node rip_node = "%s(config-router)# ", 1 }; - + /* Distribute-list update functions. */ static void rip_distribute_update (struct distribute *dist) @@ -3713,9 +3804,9 @@ rip_distribute_update (struct distribute *dist) ri = ifp->info; - if (dist->list[DISTRIBUTE_IN]) + if (dist->list[DISTRIBUTE_V4_IN]) { - alist = access_list_lookup (AFI_IP, dist->list[DISTRIBUTE_IN]); + alist = access_list_lookup (AFI_IP, dist->list[DISTRIBUTE_V4_IN]); if (alist) ri->list[RIP_FILTER_IN] = alist; else @@ -3724,9 +3815,9 @@ rip_distribute_update (struct distribute *dist) else ri->list[RIP_FILTER_IN] = NULL; - if (dist->list[DISTRIBUTE_OUT]) + if (dist->list[DISTRIBUTE_V4_OUT]) { - alist = access_list_lookup (AFI_IP, dist->list[DISTRIBUTE_OUT]); + alist = access_list_lookup (AFI_IP, dist->list[DISTRIBUTE_V4_OUT]); if (alist) ri->list[RIP_FILTER_OUT] = alist; else @@ -3735,9 +3826,9 @@ rip_distribute_update (struct distribute *dist) else ri->list[RIP_FILTER_OUT] = NULL; - if (dist->prefix[DISTRIBUTE_IN]) + if (dist->prefix[DISTRIBUTE_V4_IN]) { - plist = prefix_list_lookup (AFI_IP, dist->prefix[DISTRIBUTE_IN]); + plist = prefix_list_lookup (AFI_IP, dist->prefix[DISTRIBUTE_V4_IN]); if (plist) ri->prefix[RIP_FILTER_IN] = plist; else @@ -3746,9 +3837,9 @@ rip_distribute_update (struct distribute *dist) else ri->prefix[RIP_FILTER_IN] = NULL; - if (dist->prefix[DISTRIBUTE_OUT]) + if (dist->prefix[DISTRIBUTE_V4_OUT]) { - plist = prefix_list_lookup (AFI_IP, dist->prefix[DISTRIBUTE_OUT]); + plist = prefix_list_lookup (AFI_IP, dist->prefix[DISTRIBUTE_V4_OUT]); if (plist) ri->prefix[RIP_FILTER_OUT] = plist; else @@ -3785,34 +3876,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); @@ -3868,7 +3962,7 @@ rip_clean (void) rip_clean_network (); rip_passive_nondefault_clean (); rip_offset_clean (); - rip_interface_clean (); + rip_interfaces_clean (); rip_distance_reset (); rip_redistribute_clean (); } @@ -3892,7 +3986,7 @@ rip_reset (void) distribute_list_reset (); - rip_interface_reset (); + rip_interfaces_reset (); rip_distance_reset (); rip_zclient_reset (); @@ -3978,7 +4072,7 @@ void rip_init (void) { /* Randomize for triggered update random(). */ - srand (time (NULL)); + srandom (time (NULL)); /* Install top nodes. */ install_node (&rip_node, config_write_rip); @@ -3986,8 +4080,6 @@ rip_init (void) /* Install rip commands. */ install_element (VIEW_NODE, &show_ip_rip_cmd); install_element (VIEW_NODE, &show_ip_rip_status_cmd); - install_element (ENABLE_NODE, &show_ip_rip_cmd); - install_element (ENABLE_NODE, &show_ip_rip_status_cmd); install_element (CONFIG_NODE, &router_rip_cmd); install_element (CONFIG_NODE, &no_router_rip_cmd); @@ -4009,6 +4101,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 45b07b9cb..4f8252526 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 { @@ -195,7 +198,7 @@ struct rip_info struct in_addr from; /* Which interface does this route come from. */ - unsigned int ifindex; + ifindex_t ifindex; /* Metric of this route. */ u_int32_t metric; @@ -220,8 +223,8 @@ struct rip_info struct in_addr nexthop_out; u_char metric_set; u_int32_t metric_out; - u_short tag_out; - unsigned int ifindex_out; + u_int16_t tag_out; + ifindex_t ifindex_out; struct route_node *rp; @@ -378,15 +381,15 @@ extern void rip_init (void); extern void rip_reset (void); extern void rip_clean (void); extern void rip_clean_network (void); -extern void rip_interface_clean (void); -extern void rip_interface_reset (void); +extern void rip_interfaces_clean (void); +extern void rip_interfaces_reset (void); extern void rip_passive_nondefault_clean (void); extern void rip_if_init (void); extern void rip_if_down_all (void); extern void rip_route_map_init (void); extern void rip_route_map_reset (void); extern void rip_snmp_init (void); -extern void rip_zclient_init (void); +extern void rip_zclient_init (struct thread_master *); extern void rip_zclient_start (void); extern void rip_zclient_reset (void); extern void rip_offset_init (void); @@ -397,12 +400,13 @@ extern int rip_request_send (struct sockaddr_in *, struct interface *, u_char, extern int rip_neighbor_lookup (struct sockaddr_in *); extern int rip_redistribute_check (int); -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_add (int, int, struct prefix_ipv4 *, ifindex_t, + struct in_addr *, unsigned int, unsigned char, + route_tag_t); +extern void rip_redistribute_delete (int, int, struct prefix_ipv4 *, ifindex_t); 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 +433,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 c6bd4868d..df0f7d377 100644 --- a/ripngd/Makefile.am +++ b/ripngd/Makefile.am @@ -1,11 +1,10 @@ ## Process this file with automake to produce Makefile.in. -INCLUDES = @INCLUDES@ -I.. -I$(top_srcdir) -I$(top_srcdir)/lib @SNMP_INCLUDES@ +AM_CPPFLAGS = -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib DEFS = @DEFS@ -DSYSCONFDIR=\"$(sysconfdir)/\" INSTALL_SDATA=@INSTALL@ -m 600 -AM_CFLAGS = $(PICFLAGS) -AM_LDFLAGS = $(PILDFLAGS) +AM_CFLAGS = $(WERROR) noinst_LIBRARIES = libripng.a sbin_PROGRAMS = ripngd diff --git a/ripngd/ripng_debug.c b/ripngd/ripng_debug.c index 33a7cf69f..eb8ff5dc6 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", @@ -266,7 +266,6 @@ ripng_debug_init () install_element (VIEW_NODE, &show_debugging_ripng_cmd); - install_element (ENABLE_NODE, &show_debugging_ripng_cmd); install_element (ENABLE_NODE, &debug_ripng_events_cmd); install_element (ENABLE_NODE, &debug_ripng_packet_cmd); install_element (ENABLE_NODE, &debug_ripng_packet_direct_cmd); diff --git a/ripngd/ripng_interface.c b/ripngd/ripng_interface.c index 6718fff21..1d3757a95 100644 --- a/ripngd/ripng_interface.c +++ b/ripngd/ripng_interface.c @@ -35,10 +35,11 @@ #include "table.h" #include "thread.h" #include "privs.h" +#include "vrf.h" #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 @@ -163,38 +164,15 @@ ripng_if_down (struct interface *ifp) struct route_node *rp; struct ripng_info *rinfo; struct ripng_interface *ri; + struct list *list = NULL; + struct listnode *listnode = NULL, *nextnode = NULL; if (ripng) - { - for (rp = route_top (ripng->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_RIPNG - && rinfo->sub_type == RIPNG_ROUTE_RTE) - { - ripng_zebra_ipv6_delete ((struct prefix_ipv6 *) &rp->p, - &rinfo->nexthop, - rinfo->ifindex); - - ripng_redistribute_delete (rinfo->type, rinfo->sub_type, - (struct prefix_ipv6 *)&rp->p, - rinfo->ifindex); - } - else - { - /* All redistributed routes got through this interface, - * but the static and system ones are kept. */ - if ((rinfo->ifindex == ifp->ifindex) && - (rinfo->type != ZEBRA_ROUTE_STATIC) && - (rinfo->type != ZEBRA_ROUTE_SYSTEM)) - ripng_redistribute_delete (rinfo->type, rinfo->sub_type, - (struct prefix_ipv6 *) &rp->p, - rinfo->ifindex); - } - } - } + for (rp = route_top (ripng->table); rp; rp = route_next (rp)) + if ((list = rp->info) != NULL) + for (ALL_LIST_ELEMENTS (list, listnode, nextnode, rinfo)) + if (rinfo->ifindex == ifp->ifindex) + ripng_ecmp_delete (rinfo); ri = ifp->info; @@ -214,14 +192,15 @@ ripng_if_down (struct interface *ifp) /* Inteface link up message processing. */ int -ripng_interface_up (int command, struct zclient *zclient, zebra_size_t length) +ripng_interface_up (int command, struct zclient *zclient, zebra_size_t length, + vrf_id_t vrf_id) { struct stream *s; struct interface *ifp; /* zebra_interface_state_read() updates interface structure in iflist. */ s = zclient->ibuf; - ifp = zebra_interface_state_read (s); + ifp = zebra_interface_state_read (s, vrf_id); if (ifp == NULL) return 0; @@ -246,14 +225,14 @@ ripng_interface_up (int command, struct zclient *zclient, zebra_size_t length) /* Inteface link down message processing. */ int ripng_interface_down (int command, struct zclient *zclient, - zebra_size_t length) + zebra_size_t length, vrf_id_t vrf_id) { struct stream *s; struct interface *ifp; /* zebra_interface_state_read() updates interface structure in iflist. */ s = zclient->ibuf; - ifp = zebra_interface_state_read (s); + ifp = zebra_interface_state_read (s, vrf_id); if (ifp == NULL) return 0; @@ -270,11 +249,12 @@ ripng_interface_down (int command, struct zclient *zclient, /* Inteface addition message from zebra. */ int -ripng_interface_add (int command, struct zclient *zclient, zebra_size_t length) +ripng_interface_add (int command, struct zclient *zclient, zebra_size_t length, + vrf_id_t vrf_id) { struct interface *ifp; - ifp = zebra_interface_add_read (zclient->ibuf); + ifp = zebra_interface_add_read (zclient->ibuf, vrf_id); if (IS_RIPNG_DEBUG_ZEBRA) zlog_debug ("RIPng interface add %s index %d flags %#llx metric %d mtu %d", @@ -295,14 +275,14 @@ ripng_interface_add (int command, struct zclient *zclient, zebra_size_t length) int ripng_interface_delete (int command, struct zclient *zclient, - zebra_size_t length) + zebra_size_t length, vrf_id_t vrf_id) { struct interface *ifp; struct stream *s; s = zclient->ibuf; /* zebra_interface_state_read() updates interface structure in iflist */ - ifp = zebra_interface_state_read(s); + ifp = zebra_interface_state_read (s, vrf_id); if (ifp == NULL) return 0; @@ -403,19 +383,19 @@ ripng_apply_address_add (struct connected *ifc) { if ((ripng_enable_if_lookup(ifc->ifp->name) >= 0) || (ripng_enable_network_lookup2(ifc) >= 0)) ripng_redistribute_add(ZEBRA_ROUTE_CONNECT, RIPNG_ROUTE_INTERFACE, - &address, ifc->ifp->ifindex, NULL); + &address, ifc->ifp->ifindex, NULL, 0); } int ripng_interface_address_add (int command, struct zclient *zclient, - zebra_size_t length) + zebra_size_t length, vrf_id_t vrf_id) { struct connected *c; struct prefix *p; c = zebra_interface_address_read (ZEBRA_INTERFACE_ADDRESS_ADD, - zclient->ibuf); + zclient->ibuf, vrf_id); if (c == NULL) return 0; @@ -476,14 +456,14 @@ ripng_apply_address_del (struct connected *ifc) { int ripng_interface_address_delete (int command, struct zclient *zclient, - zebra_size_t length) + zebra_size_t length, vrf_id_t vrf_id) { struct connected *ifc; struct prefix *p; char buf[INET6_ADDRSTRLEN]; ifc = zebra_interface_address_read (ZEBRA_INTERFACE_ADDRESS_DELETE, - zclient->ibuf); + zclient->ibuf, vrf_id); if (ifc) { @@ -505,7 +485,7 @@ ripng_interface_address_delete (int command, struct zclient *zclient, return 0; } - + /* RIPng enable interface vector. */ vector ripng_enable_if; @@ -724,13 +704,13 @@ ripng_connect_set (struct interface *ifp, int set) if ((ripng_enable_if_lookup(connected->ifp->name) >= 0) || (ripng_enable_network_lookup2(connected) >= 0)) ripng_redistribute_add (ZEBRA_ROUTE_CONNECT, RIPNG_ROUTE_INTERFACE, - &address, connected->ifp->ifindex, NULL); + &address, connected->ifp->ifindex, NULL, 0); } else { ripng_redistribute_delete (ZEBRA_ROUTE_CONNECT, RIPNG_ROUTE_INTERFACE, &address, connected->ifp->ifindex); if (ripng_redistribute_check (ZEBRA_ROUTE_CONNECT)) ripng_redistribute_add (ZEBRA_ROUTE_CONNECT, RIPNG_ROUTE_REDISTRIBUTE, - &address, connected->ifp->ifindex, NULL); + &address, connected->ifp->ifindex, NULL, 0); } } } @@ -812,7 +792,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 +815,7 @@ ripng_clean_network () vector_slot (ripng_enable_if, i) = NULL; } } - + /* Vector to store passive-interface name. */ vector Vripng_passive_interface; @@ -1103,7 +1083,7 @@ DEFUN (no_ripng_passive_interface, { return ripng_passive_interface_unset (vty, argv[0]); } - + static struct ripng_interface * ri_new (void) { @@ -1200,7 +1180,6 @@ void ripng_if_init () { /* Interface initialize. */ - iflist = list_new (); if_add_hook (IF_NEW_HOOK, ripng_if_new_hook); if_add_hook (IF_DELETE_HOOK, ripng_if_delete_hook); diff --git a/ripngd/ripng_main.c b/ripngd/ripng_main.c index f4a62440a..1c184e2c2 100644 --- a/ripngd/ripng_main.c +++ b/ripngd/ripng_main.c @@ -34,6 +34,7 @@ #include "if.h" #include "privs.h" #include "sigevent.h" +#include "vrf.h" #include "ripngd/ripngd.h" @@ -127,7 +128,7 @@ Report bugs to %s\n", progname, ZEBRA_BUG_ADDRESS); } exit (status); } - + /* SIGHUP handler. */ static void sighup (void) @@ -182,7 +183,7 @@ struct quagga_signal_t ripng_signals[] = .handler = &sigint, }, }; - + /* RIPngd main routine. */ int main (int argc, char **argv) @@ -272,19 +273,17 @@ main (int argc, char **argv) /* Library inits. */ zprivs_init (&ripngd_privs); - signal_init (master, Q_SIGC(ripng_signals), ripng_signals); + signal_init (master, array_size(ripng_signals), ripng_signals); cmd_init (1); vty_init (master); memory_init (); + vrf_init (); /* RIPngd inits. */ ripng_init (); - zebra_init (); + zebra_init (master); 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 05f190e27..b966af001 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_nexthop.h b/ripngd/ripng_nexthop.h index 7c0410592..19bd32b50 100644 --- a/ripngd/ripng_nexthop.h +++ b/ripngd/ripng_nexthop.h @@ -41,17 +41,8 @@ extern void ripng_rte_send(struct list *ripng_rte_list, struct interface *ifp, * -1 if A < B **/ static inline int -addr6_cmp(struct in6_addr *A, struct in6_addr *B) { - -#ifndef s6_addr32 -#if defined(SUNOS_5) -/* Some SunOS define s6_addr32 only to kernel */ -#define s6_addr32 _S6_un._S6_u32 -#else -#define s6_addr32 __u6_addr.__u6_addr32 -#endif /* SUNOS_5 */ -#endif /*s6_addr32*/ - +addr6_cmp(struct in6_addr *A, struct in6_addr *B) +{ #define a(i) A->s6_addr32[i] #define b(i) B->s6_addr32[i] diff --git a/ripngd/ripng_peer.c b/ripngd/ripng_peer.c index c04456b8a..b12e14604 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_route.c b/ripngd/ripng_route.c index d4bf0262b..f26302e88 100644 --- a/ripngd/ripng_route.c +++ b/ripngd/ripng_route.c @@ -40,7 +40,7 @@ ripng_aggregate_new () return new; } -static void +void ripng_aggregate_free (struct ripng_aggregate *aggregate) { XFREE (MTYPE_RIPNG_AGGREGATE, aggregate); @@ -76,6 +76,23 @@ ripng_aggregate_decrement (struct route_node *child, struct ripng_info *rinfo) } } +/* Aggregate count decrement check for a list. */ +void +ripng_aggregate_decrement_list (struct route_node *child, struct list *list) +{ + struct route_node *np; + struct ripng_aggregate *aggregate; + struct ripng_info *rinfo = NULL; + struct listnode *node = NULL; + + for (np = child; np; np = np->parent) + if ((aggregate = np->aggregate) != NULL) + aggregate->count -= listcount (list); + + for (ALL_LIST_ELEMENTS_RO (list, node, rinfo)) + rinfo->suppress--; +} + /* RIPng routes treatment. */ int ripng_aggregate_add (struct prefix *p) @@ -85,6 +102,8 @@ ripng_aggregate_add (struct prefix *p) struct ripng_info *rinfo; struct ripng_aggregate *aggregate; struct ripng_aggregate *sub; + struct list *list = NULL; + struct listnode *node = NULL; /* Get top node for aggregation. */ top = route_node_get (ripng->table, p); @@ -99,11 +118,12 @@ ripng_aggregate_add (struct prefix *p) for (rp = route_lock_node (top); rp; rp = route_next_until (rp, top)) { /* Suppress normal route. */ - if ((rinfo = rp->info) != NULL) - { - aggregate->count++; - rinfo->suppress++; - } + if ((list = rp->info) != NULL) + for (ALL_LIST_ELEMENTS_RO (list, node, rinfo)) + { + aggregate->count++; + rinfo->suppress++; + } /* Suppress aggregate route. This may not need. */ if (rp != top && (sub = rp->aggregate) != NULL) { @@ -124,6 +144,8 @@ ripng_aggregate_delete (struct prefix *p) struct ripng_info *rinfo; struct ripng_aggregate *aggregate; struct ripng_aggregate *sub; + struct list *list = NULL; + struct listnode *node = NULL; /* Get top node for aggregation. */ top = route_node_get (ripng->table, p); @@ -135,11 +157,12 @@ ripng_aggregate_delete (struct prefix *p) for (rp = route_lock_node (top); rp; rp = route_next_until (rp, top)) { /* Suppress normal route. */ - if ((rinfo = rp->info) != NULL) - { - aggregate->count--; - rinfo->suppress--; - } + if ((list = rp->info) != NULL) + for (ALL_LIST_ELEMENTS_RO (list, node, rinfo)) + { + aggregate->count--; + rinfo->suppress--; + } if (rp != top && (sub = rp->aggregate) != NULL) { diff --git a/ripngd/ripng_route.h b/ripngd/ripng_route.h index 2f5b75754..9ff90aa8d 100644 --- a/ripngd/ripng_route.h +++ b/ripngd/ripng_route.h @@ -35,20 +35,23 @@ struct ripng_aggregate u_char metric; /* Tag field of RIPng packet.*/ - u_short tag; + u_int16_t tag; /* Route-map futures - this variables can be changed. */ struct in6_addr nexthop_out; u_char metric_set; u_char metric_out; - u_short tag_out; + u_int16_t tag_out; }; extern void ripng_aggregate_increment (struct route_node *rp, struct ripng_info *rinfo); extern void ripng_aggregate_decrement (struct route_node *rp, struct ripng_info *rinfo); +extern void ripng_aggregate_decrement_list (struct route_node *rp, + struct list *list); extern int ripng_aggregate_add (struct prefix *p); extern int ripng_aggregate_delete (struct prefix *p); +extern void ripng_aggregate_free (struct ripng_aggregate *aggregate); #endif /* _ZEBRA_RIPNG_ROUTE_H */ diff --git a/ripngd/ripng_routemap.c b/ripngd/ripng_routemap.c index 0f5cca350..c596aec15 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) @@ -55,10 +55,10 @@ ripng_route_match_add (struct vty *vty, struct route_map_index *index, switch (ret) { case RMAP_RULE_MISSING: - vty_out (vty, "Can't find rule.%s", VTY_NEWLINE); + vty_out (vty, "RIPng Can't find rule.%s", VTY_NEWLINE); return CMD_WARNING; case RMAP_COMPILE_ERROR: - vty_out (vty, "Argument is malformed.%s", VTY_NEWLINE); + vty_out (vty, "RIPng Argument is malformed.%s", VTY_NEWLINE); return CMD_WARNING; } } @@ -77,10 +77,10 @@ ripng_route_match_delete (struct vty *vty, struct route_map_index *index, switch (ret) { case RMAP_RULE_MISSING: - vty_out (vty, "Can't find rule.%s", VTY_NEWLINE); + vty_out (vty, "RIPng Can't find rule.%s", VTY_NEWLINE); return CMD_WARNING; case RMAP_COMPILE_ERROR: - vty_out (vty, "Argument is malformed.%s", VTY_NEWLINE); + vty_out (vty, "RIPng Argument is malformed.%s", VTY_NEWLINE); return CMD_WARNING; } } @@ -99,10 +99,10 @@ ripng_route_set_add (struct vty *vty, struct route_map_index *index, switch (ret) { case RMAP_RULE_MISSING: - vty_out (vty, "Can't find rule.%s", VTY_NEWLINE); + vty_out (vty, "RIPng Can't find rule.%s", VTY_NEWLINE); return CMD_WARNING; case RMAP_COMPILE_ERROR: - vty_out (vty, "Argument is malformed.%s", VTY_NEWLINE); + vty_out (vty, "RIPng Argument is malformed.%s", VTY_NEWLINE); return CMD_WARNING; } } @@ -121,16 +121,16 @@ ripng_route_set_delete (struct vty *vty, struct route_map_index *index, switch (ret) { case RMAP_RULE_MISSING: - vty_out (vty, "Can't find rule.%s", VTY_NEWLINE); + vty_out (vty, "RIPng Can't find rule.%s", VTY_NEWLINE); return CMD_WARNING; case RMAP_COMPILE_ERROR: - vty_out (vty, "Argument is malformed.%s", VTY_NEWLINE); + vty_out (vty, "RIPng Argument is malformed.%s", VTY_NEWLINE); return CMD_WARNING; } } 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 @@ -240,7 +240,7 @@ static route_map_result_t route_match_tag (void *rule, struct prefix *prefix, route_map_object_t type, void *object) { - u_short *tag; + route_tag_t *tag; struct ripng_info *rinfo; if (type == RMAP_RIPNG) @@ -257,34 +257,14 @@ route_match_tag (void *rule, struct prefix *prefix, return RMAP_NOMATCH; } -/* Route map `match tag' match statement. `arg' is TAG value */ -static void * -route_match_tag_compile (const char *arg) -{ - u_short *tag; - - tag = XMALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof (u_short)); - *tag = atoi (arg); - - return tag; -} - -/* Free route map's compiled `match tag' value. */ -static void -route_match_tag_free (void *rule) -{ - XFREE (MTYPE_ROUTE_MAP_COMPILED, rule); -} - -/* Route map commands for tag matching. */ static struct route_map_rule_cmd route_match_tag_cmd = { "tag", route_match_tag, - route_match_tag_compile, - route_match_tag_free + route_map_rule_tag_compile, + route_map_rule_tag_free, }; - + /* `set metric METRIC' */ /* Set metric to attribute. */ @@ -452,7 +432,7 @@ static route_map_result_t route_set_tag (void *rule, struct prefix *prefix, route_map_object_t type, void *object) { - u_short *tag; + route_tag_t *tag; struct ripng_info *rinfo; if(type == RMAP_RIPNG) @@ -460,7 +440,7 @@ route_set_tag (void *rule, struct prefix *prefix, /* Fetch routemap's rule information. */ tag = rule; rinfo = object; - + /* Set next hop value. */ rinfo->tag_out = *tag; } @@ -468,35 +448,15 @@ route_set_tag (void *rule, struct prefix *prefix, return RMAP_OKAY; } -/* Route map `tag' compile function. Given string is converted - to u_short. */ -static void * -route_set_tag_compile (const char *arg) -{ - u_short *tag; - - tag = XMALLOC (MTYPE_ROUTE_MAP_COMPILED, sizeof (u_short)); - *tag = atoi (arg); - - return tag; -} - -/* Free route map's compiled `ip nexthop' value. */ -static void -route_set_tag_free (void *rule) -{ - XFREE (MTYPE_ROUTE_MAP_COMPILED, rule); -} - /* Route map commands for tag set. */ static struct route_map_rule_cmd route_set_tag_cmd = { "tag", route_set_tag, - route_set_tag_compile, - route_set_tag_free + route_map_rule_tag_compile, + route_map_rule_tag_free }; - + #define MATCH_STR "Match values from routing table\n" #define SET_STR "Set values in destination routing protocol\n" @@ -564,7 +524,7 @@ ALIAS (no_match_interface, DEFUN (match_tag, match_tag_cmd, - "match tag <0-65535>", + "match tag <1-4294967295>", MATCH_STR "Match tag of route\n" "Metric value\n") @@ -587,7 +547,7 @@ DEFUN (no_match_tag, ALIAS (no_match_tag, no_match_tag_val_cmd, - "no match tag <0-65535>", + "no match tag <1-4294967295>", NO_STR MATCH_STR "Match tag of route\n" @@ -675,7 +635,7 @@ ALIAS (no_set_ipv6_nexthop_local, DEFUN (set_tag, set_tag_cmd, - "set tag <0-65535>", + "set tag <1-4294967295>", SET_STR "Tag value for routing protocol\n" "Tag value\n") @@ -698,7 +658,7 @@ DEFUN (no_set_tag, ALIAS (no_set_tag, no_set_tag_val_cmd, - "no set tag <0-65535>", + "no set tag <1-4294967295>", NO_STR SET_STR "Tag value for routing protocol\n" diff --git a/ripngd/ripng_zebra.c b/ripngd/ripng_zebra.c index 8e7660626..a35bc990f 100644 --- a/ripngd/ripng_zebra.c +++ b/ripngd/ripng_zebra.c @@ -24,82 +24,123 @@ #include "command.h" #include "prefix.h" +#include "table.h" #include "stream.h" +#include "memory.h" #include "routemap.h" #include "zclient.h" #include "log.h" #include "ripngd/ripngd.h" +#include "ripngd/ripng_debug.h" /* All information about zebra. */ struct zclient *zclient = NULL; -/* Callback prototypes for zebra client service. */ -int ripng_interface_up (int, struct zclient *, zebra_size_t); -int ripng_interface_down (int, struct zclient *, zebra_size_t); -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) +/* Send ECMP routes to zebra. */ +static void +ripng_zebra_ipv6_send (struct route_node *rp, u_char cmd) { + static struct in6_addr **nexthops = NULL; + static ifindex_t *ifindexes = NULL; + static unsigned int nexthops_len = 0; + + struct list *list = (struct list *)rp->info; struct zapi_ipv6 api; + struct listnode *listnode = NULL; + struct ripng_info *rinfo = NULL; + int count = 0; - if (zclient->redist[ZEBRA_ROUTE_RIPNG]) + if (vrf_bitmap_check (zclient->redist[ZEBRA_ROUTE_RIPNG], VRF_DEFAULT)) { + api.vrf_id = VRF_DEFAULT; api.type = ZEBRA_ROUTE_RIPNG; 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 in6_addr *)); + ifindexes = XREALLOC (MTYPE_TMP, ifindexes, + nexthops_len * sizeof (unsigned int)); + } + SET_FLAG (api.message, ZAPI_MESSAGE_NEXTHOP); - api.nexthop_num = 1; - api.nexthop = &nexthop; SET_FLAG (api.message, ZAPI_MESSAGE_IFINDEX); - api.ifindex_num = 1; - api.ifindex = &ifindex; + for (ALL_LIST_ELEMENTS_RO (list, listnode, rinfo)) + { + nexthops[count] = &rinfo->nexthop; + ifindexes[count] = rinfo->ifindex; + count++; + if (cmd == ZEBRA_IPV6_ROUTE_ADD) + SET_FLAG (rinfo->flags, RIPNG_RTF_FIB); + else + UNSET_FLAG (rinfo->flags, RIPNG_RTF_FIB); + } + + api.nexthop = nexthops; + api.nexthop_num = count; + api.ifindex = ifindexes; + api.ifindex_num = count; + + rinfo = listgetdata (listhead (list)); + SET_FLAG (api.message, ZAPI_MESSAGE_METRIC); - api.metric = metric; - - zapi_ipv6_route (ZEBRA_IPV6_ROUTE_ADD, zclient, p, &api); + api.metric = rinfo->metric; + + if (rinfo->tag) + { + SET_FLAG (api.message, ZAPI_MESSAGE_TAG); + api.tag = rinfo->tag; + } + + zapi_ipv6_route (cmd, zclient, + (struct prefix_ipv6 *)&rp->p, &api); + + if (IS_RIPNG_DEBUG_ZEBRA) + { + if (ripng->ecmp) + zlog_debug ("%s: %s/%d nexthops %d", + (cmd == ZEBRA_IPV6_ROUTE_ADD) ? \ + "Install into zebra" : "Delete from zebra", + inet6_ntoa (rp->p.u.prefix6), rp->p.prefixlen, count); + else + zlog_debug ("%s: %s/%d", + (cmd == ZEBRA_IPV6_ROUTE_ADD) ? \ + "Install into zebra" : "Delete from zebra", + inet6_ntoa (rp->p.u.prefix6), rp->p.prefixlen); + } } } +/* Add/update ECMP routes to zebra. */ void -ripng_zebra_ipv6_delete (struct prefix_ipv6 *p, struct in6_addr *nexthop, - unsigned int ifindex) +ripng_zebra_ipv6_add (struct route_node *rp) { - struct zapi_ipv6 api; - - if (zclient->redist[ZEBRA_ROUTE_RIPNG]) - { - api.type = ZEBRA_ROUTE_RIPNG; - api.flags = 0; - api.message = 0; - api.safi = SAFI_UNICAST; - SET_FLAG (api.message, ZAPI_MESSAGE_NEXTHOP); - api.nexthop_num = 1; - api.nexthop = &nexthop; - SET_FLAG (api.message, ZAPI_MESSAGE_IFINDEX); - api.ifindex_num = 1; - api.ifindex = &ifindex; + ripng_zebra_ipv6_send (rp, ZEBRA_IPV6_ROUTE_ADD); +} - zapi_ipv6_route (ZEBRA_IPV6_ROUTE_DELETE, zclient, p, &api); - } +/* Delete ECMP routes from zebra. */ +void +ripng_zebra_ipv6_delete (struct route_node *rp) +{ + ripng_zebra_ipv6_send (rp, ZEBRA_IPV6_ROUTE_DELETE); } /* Zebra route add and delete treatment. */ static int ripng_zebra_read_ipv6 (int command, struct zclient *zclient, - zebra_size_t length) + zebra_size_t length, vrf_id_t vrf_id) { struct stream *s; struct zapi_ipv6 api; unsigned long ifindex; struct in6_addr nexthop; struct prefix_ipv6 p; + unsigned char plength = 0; s = zclient->ibuf; ifindex = 0; @@ -113,7 +154,8 @@ ripng_zebra_read_ipv6 (int command, struct zclient *zclient, /* IPv6 prefix. */ memset (&p, 0, sizeof (struct prefix_ipv6)); p.family = AF_INET6; - p.prefixlen = stream_getc (s); + plength = stream_getc (s); + p.prefixlen = MIN(IPV6_MAX_PREFIXLEN, plength); stream_get (&p.prefix, s, PSIZE (p.prefixlen)); /* Nexthop, ifindex, distance, metric. */ @@ -136,8 +178,14 @@ ripng_zebra_read_ipv6 (int command, struct zclient *zclient, else api.metric = 0; + if (CHECK_FLAG (api.message, ZAPI_MESSAGE_TAG)) + api.tag = stream_getl (s); + else + api.tag = 0; + if (command == ZEBRA_IPV6_ROUTE_ADD) - ripng_redistribute_add (api.type, RIPNG_ROUTE_REDISTRIBUTE, &p, ifindex, &nexthop); + ripng_redistribute_add (api.type, RIPNG_ROUTE_REDISTRIBUTE, &p, + ifindex, &nexthop, api.tag); else ripng_redistribute_delete (api.type, RIPNG_ROUTE_REDISTRIBUTE, &p, ifindex); @@ -153,13 +201,14 @@ ripng_zclient_reset (void) static int ripng_redistribute_unset (int type) { - if (! zclient->redist[type]) + if (! vrf_bitmap_check (zclient->redist[type], VRF_DEFAULT)) return CMD_SUCCESS; - zclient->redist[type] = 0; + vrf_bitmap_set (zclient->redist[type], VRF_DEFAULT); if (zclient->sock > 0) - zebra_redistribute_send (ZEBRA_REDISTRIBUTE_DELETE, zclient, type); + zebra_redistribute_send (ZEBRA_REDISTRIBUTE_DELETE, zclient, type, + VRF_DEFAULT); ripng_redistribute_withdraw (type); @@ -169,7 +218,7 @@ ripng_redistribute_unset (int type) int ripng_redistribute_check (int type) { - return (zclient->redist[type]); + return vrf_bitmap_check (zclient->redist[type], VRF_DEFAULT); } static void @@ -206,7 +255,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; @@ -229,13 +278,14 @@ ripng_redistribute_clean () for (i = 0; redist_type[i].str; i++) { - if (zclient->redist[redist_type[i].type]) + if (vrf_bitmap_check (zclient->redist[redist_type[i].type], VRF_DEFAULT)) { if (zclient->sock > 0) zebra_redistribute_send (ZEBRA_REDISTRIBUTE_DELETE, - zclient, redist_type[i].type); + zclient, redist_type[i].type, + VRF_DEFAULT); - zclient->redist[redist_type[i].type] = 0; + vrf_bitmap_unset (zclient->redist[redist_type[i].type], VRF_DEFAULT); /* Remove the routes from RIPng table. */ ripng_redistribute_withdraw (redist_type[i].type); @@ -273,7 +323,7 @@ DEFUN (ripng_redistribute_ripng, "Redistribute information from another routing protocol\n" "RIPng route\n") { - zclient->redist[ZEBRA_ROUTE_RIPNG] = 1; + vrf_bitmap_set (zclient->redist[ZEBRA_ROUTE_RIPNG], VRF_DEFAULT); return CMD_SUCCESS; } @@ -284,7 +334,7 @@ DEFUN (no_ripng_redistribute_ripng, "Redistribute information from another routing protocol\n" "RIPng route\n") { - zclient->redist[ZEBRA_ROUTE_RIPNG] = 0; + vrf_bitmap_unset (zclient->redist[ZEBRA_ROUTE_RIPNG], VRF_DEFAULT); return CMD_SUCCESS; } @@ -304,7 +354,7 @@ DEFUN (ripng_redistribute_type, return CMD_WARNING; } - zclient_redistribute (ZEBRA_REDISTRIBUTE_ADD, zclient, type); + zclient_redistribute (ZEBRA_REDISTRIBUTE_ADD, zclient, type, VRF_DEFAULT); return CMD_SUCCESS; } @@ -352,7 +402,7 @@ DEFUN (ripng_redistribute_type_metric, } ripng_redistribute_metric_set (type, metric); - zclient_redistribute (ZEBRA_REDISTRIBUTE_ADD, zclient, type); + zclient_redistribute (ZEBRA_REDISTRIBUTE_ADD, zclient, type, VRF_DEFAULT); return CMD_SUCCESS; } @@ -384,7 +434,7 @@ DEFUN (ripng_redistribute_type_routemap, } ripng_redistribute_routemap_set (type, argv[1]); - zclient_redistribute (ZEBRA_REDISTRIBUTE_ADD, zclient, type); + zclient_redistribute (ZEBRA_REDISTRIBUTE_ADD, zclient, type, VRF_DEFAULT); return CMD_SUCCESS; } @@ -421,7 +471,7 @@ DEFUN (ripng_redistribute_type_metric_routemap, ripng_redistribute_metric_set (type, metric); ripng_redistribute_routemap_set (type, argv[2]); - zclient_redistribute (ZEBRA_REDISTRIBUTE_ADD, zclient, type); + zclient_redistribute (ZEBRA_REDISTRIBUTE_ADD, zclient, type, VRF_DEFAULT); return CMD_SUCCESS; } @@ -440,7 +490,8 @@ ripng_redistribute_write (struct vty *vty, int config_mode) int i; for (i = 0; i < ZEBRA_ROUTE_MAX; i++) - if (i != zclient->redist_default && zclient->redist[i]) + if (i != zclient->redist_default && + vrf_bitmap_check (zclient->redist[i], VRF_DEFAULT)) { if (config_mode) { @@ -480,7 +531,7 @@ zebra_config_write (struct vty *vty) vty_out (vty, "no router zebra%s", VTY_NEWLINE); return 1; } - else if (! zclient->redist[ZEBRA_ROUTE_RIPNG]) + else if (! vrf_bitmap_check (zclient->redist[ZEBRA_ROUTE_RIPNG], VRF_DEFAULT)) { vty_out (vty, "router zebra%s", VTY_NEWLINE); vty_out (vty, " no redistribute ripng%s", VTY_NEWLINE); @@ -496,14 +547,21 @@ static struct cmd_node zebra_node = "%s(config-router)# ", }; +static void +ripng_zebra_connected (struct zclient *zclient) +{ + zclient_send_requests (zclient, VRF_DEFAULT); +} + /* Initialize zebra structure and it's commands. */ void -zebra_init () +zebra_init (struct thread_master *master) { /* Allocate zebra structure. */ - zclient = zclient_new (); + zclient = zclient_new (master); zclient_init (zclient, ZEBRA_ROUTE_RIPNG); + zclient->zebra_connected = ripng_zebra_connected; zclient->interface_up = ripng_interface_up; zclient->interface_down = ripng_interface_down; zclient->interface_add = ripng_interface_add; diff --git a/ripngd/ripngd.c b/ripngd/ripngd.c index 8e97c2f8b..824b3a4c3 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) @@ -229,14 +229,14 @@ ripng_send_packet (caddr_t buf, int bufsize, struct sockaddr_in6 *to, /* Receive UDP RIPng packet from socket. */ static int ripng_recv_packet (int sock, u_char *buf, int bufsize, - struct sockaddr_in6 *from, unsigned int *ifindex, + struct sockaddr_in6 *from, ifindex_t *ifindex, int *hoplimit) { int ret; struct msghdr msg; struct iovec iov; struct cmsghdr *cmsgptr; - struct in6_addr dst; + struct in6_addr dst = { .s6_addr = { 0 } }; /* Ancillary data. This store cmsghdr and in6_pktinfo. But at this point I can't determine size of cmsghdr */ @@ -421,8 +421,13 @@ ripng_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 RIPng routing information. */ ripng_info_free (rinfo); @@ -430,151 +435,209 @@ ripng_garbage_collect (struct thread *t) return 0; } -/* Timeout RIPng routes. */ -static int -ripng_timeout (struct thread *t) +static void ripng_timeout_update (struct ripng_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 ripng_info * +ripng_ecmp_add (struct ripng_info *rinfo_new) { - struct ripng_info *rinfo; - struct route_node *rp; + struct route_node *rp = rinfo_new->rp; + struct ripng_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; - /* Get route_node pointer. */ - rp = rinfo->rp; + /* If ECMP is not allowed and some entry already exists in the list, + * do nothing. */ + if (listcount (list) && !ripng->ecmp) + return NULL; - /* - The garbage-collection timer is set for 120 seconds. */ - RIPNG_TIMER_ON (rinfo->t_garbage_collect, ripng_garbage_collect, - ripng->garbage_time); + rinfo = ripng_info_new (); + memcpy (rinfo, rinfo_new, sizeof (struct ripng_info)); + listnode_add (list, rinfo); - /* Delete this route from the kernel. */ - ripng_zebra_ipv6_delete ((struct prefix_ipv6 *)&rp->p, &rinfo->nexthop, - rinfo->ifindex); - /* - The metric for the route is set to 16 (infinity). This causes - the route to be removed from service. */ - rinfo->metric = RIPNG_METRIC_INFINITY; - rinfo->flags &= ~RIPNG_RTF_FIB; + if (ripng_route_rte (rinfo)) + { + ripng_timeout_update (rinfo); + ripng_zebra_ipv6_add (rp); + } - /* Aggregate count decrement. */ - ripng_aggregate_decrement (rp, rinfo); + ripng_aggregate_increment (rp, rinfo); - /* - The route change flag is to indicate that this entry has been - changed. */ - rinfo->flags |= RIPNG_RTF_CHANGED; + /* Set the route change flag on the first entry. */ + rinfo = listgetdata (listhead (list)); + SET_FLAG (rinfo->flags, RIPNG_RTF_CHANGED); - /* - The output process is signalled to trigger a response. */ + /* Signal the output process to trigger an update. */ ripng_event (RIPNG_TRIGGERED_UPDATE, 0); - return 0; + return rinfo; } -static void -ripng_timeout_update (struct ripng_info *rinfo) +/* Replace the ECMP list with the new route. + * RETURN: the new entry added in the list + */ +struct ripng_info * +ripng_ecmp_replace (struct ripng_info *rinfo_new) { + struct route_node *rp = rinfo_new->rp; + struct list *list = (struct list *)rp->info; + struct ripng_info *rinfo = NULL, *tmp_rinfo = NULL; + struct listnode *node = NULL, *nextnode = NULL; + + if (list == NULL || listcount (list) == 0) + return ripng_ecmp_add (rinfo_new); + + /* Get the first entry */ + rinfo = listgetdata (listhead (list)); + + /* Learnt route replaced by a local one. Delete it from zebra. */ + if (ripng_route_rte (rinfo) && !ripng_route_rte (rinfo_new)) + if (CHECK_FLAG (rinfo->flags, RIPNG_RTF_FIB)) + ripng_zebra_ipv6_delete (rp); + if (rinfo->metric != RIPNG_METRIC_INFINITY) + ripng_aggregate_decrement_list (rp, list); + + /* Re-use the first entry, and delete the others. */ + for (ALL_LIST_ELEMENTS (list, node, nextnode, tmp_rinfo)) + if (tmp_rinfo != rinfo) + { + RIPNG_TIMER_OFF (tmp_rinfo->t_timeout); + RIPNG_TIMER_OFF (tmp_rinfo->t_garbage_collect); + list_delete_node (list, node); + ripng_info_free (tmp_rinfo); + } + + RIPNG_TIMER_OFF (rinfo->t_timeout); + RIPNG_TIMER_OFF (rinfo->t_garbage_collect); + memcpy (rinfo, rinfo_new, sizeof (struct ripng_info)); + + if (ripng_route_rte (rinfo)) { - RIPNG_TIMER_OFF (rinfo->t_timeout); - RIPNG_TIMER_ON (rinfo->t_timeout, ripng_timeout, ripng->timeout_time); + ripng_timeout_update (rinfo); + /* The ADD message implies an update. */ + ripng_zebra_ipv6_add (rp); } + + ripng_aggregate_increment (rp, rinfo); + + /* Set the route change flag. */ + SET_FLAG (rinfo->flags, RIPNG_RTF_CHANGED); + + /* Signal the output process to trigger an update. */ + ripng_event (RIPNG_TRIGGERED_UPDATE, 0); + + return rinfo; } -static int -ripng_incoming_filter (struct prefix_ipv6 *p, struct ripng_interface *ri) +/* 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 ripng_info * +ripng_ecmp_delete (struct ripng_info *rinfo) { - struct distribute *dist; - struct access_list *alist; - struct prefix_list *plist; + struct route_node *rp = rinfo->rp; + struct list *list = (struct list *)rp->info; - /* Input distribute-list filtering. */ - if (ri->list[RIPNG_FILTER_IN]) + RIPNG_TIMER_OFF (rinfo->t_timeout); + + if (rinfo->metric != RIPNG_METRIC_INFINITY) + ripng_aggregate_decrement (rp, rinfo); + + if (listcount (list) > 1) { - if (access_list_apply (ri->list[RIPNG_FILTER_IN], - (struct prefix *) p) == FILTER_DENY) - { - if (IS_RIPNG_DEBUG_PACKET) - zlog_debug ("%s/%d filtered by distribute in", - inet6_ntoa (p->prefix), p->prefixlen); - return -1; - } + /* Some other ECMP entries still exist. Just delete this entry. */ + RIPNG_TIMER_OFF (rinfo->t_garbage_collect); + listnode_delete (list, rinfo); + if (ripng_route_rte (rinfo) && CHECK_FLAG (rinfo->flags, RIPNG_RTF_FIB)) + /* The ADD message implies the update. */ + ripng_zebra_ipv6_add (rp); + ripng_info_free (rinfo); + rinfo = NULL; } - if (ri->prefix[RIPNG_FILTER_IN]) + else { - if (prefix_list_apply (ri->prefix[RIPNG_FILTER_IN], - (struct prefix *) p) == PREFIX_DENY) - { - if (IS_RIPNG_DEBUG_PACKET) - zlog_debug ("%s/%d filtered by prefix-list in", - inet6_ntoa (p->prefix), p->prefixlen); - return -1; - } + 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 = RIPNG_METRIC_INFINITY; + RIPNG_TIMER_ON (rinfo->t_garbage_collect, + ripng_garbage_collect, ripng->garbage_time); + + if (ripng_route_rte (rinfo) && CHECK_FLAG (rinfo->flags, RIPNG_RTF_FIB)) + ripng_zebra_ipv6_delete (rp); } - /* All interface filter check. */ - dist = distribute_lookup (NULL); - if (dist) + /* Set the route change flag on the first entry. */ + rinfo = listgetdata (listhead (list)); + SET_FLAG (rinfo->flags, RIPNG_RTF_CHANGED); + + /* Signal the output process to trigger an update. */ + ripng_event (RIPNG_TRIGGERED_UPDATE, 0); + + return rinfo; +} + +/* Timeout RIPng routes. */ +static int +ripng_timeout (struct thread *t) +{ + ripng_ecmp_delete ((struct ripng_info *)THREAD_ARG (t)); + return 0; +} + +static void +ripng_timeout_update (struct ripng_info *rinfo) +{ + if (rinfo->metric != RIPNG_METRIC_INFINITY) { - if (dist->list[DISTRIBUTE_IN]) - { - alist = access_list_lookup (AFI_IP6, dist->list[DISTRIBUTE_IN]); - - if (alist) - { - if (access_list_apply (alist, - (struct prefix *) p) == FILTER_DENY) - { - if (IS_RIPNG_DEBUG_PACKET) - zlog_debug ("%s/%d filtered by distribute in", - inet6_ntoa (p->prefix), p->prefixlen); - return -1; - } - } - } - if (dist->prefix[DISTRIBUTE_IN]) - { - plist = prefix_list_lookup (AFI_IP6, dist->prefix[DISTRIBUTE_IN]); - - if (plist) - { - if (prefix_list_apply (plist, - (struct prefix *) p) == PREFIX_DENY) - { - if (IS_RIPNG_DEBUG_PACKET) - zlog_debug ("%s/%d filtered by prefix-list in", - inet6_ntoa (p->prefix), p->prefixlen); - return -1; - } - } - } + RIPNG_TIMER_OFF (rinfo->t_timeout); + RIPNG_TIMER_ON (rinfo->t_timeout, ripng_timeout, ripng->timeout_time); } - return 0; } static int -ripng_outgoing_filter (struct prefix_ipv6 *p, struct ripng_interface *ri) +ripng_filter (int ripng_distribute, struct prefix_ipv6 *p, + struct ripng_interface *ri) { struct distribute *dist; struct access_list *alist; struct prefix_list *plist; + int distribute = ripng_distribute == RIPNG_FILTER_OUT ? + DISTRIBUTE_V6_OUT : DISTRIBUTE_V6_IN; + const char *inout = ripng_distribute == RIPNG_FILTER_OUT ? "out" : "in"; - if (ri->list[RIPNG_FILTER_OUT]) + /* Input distribute-list filtering. */ + if (ri->list[ripng_distribute]) { - if (access_list_apply (ri->list[RIPNG_FILTER_OUT], + if (access_list_apply (ri->list[ripng_distribute], (struct prefix *) p) == FILTER_DENY) { if (IS_RIPNG_DEBUG_PACKET) - zlog_debug ("%s/%d is filtered by distribute out", - inet6_ntoa (p->prefix), p->prefixlen); + zlog_debug ("%s/%d filtered by distribute %s", + inet6_ntoa (p->prefix), p->prefixlen, inout); return -1; } } - if (ri->prefix[RIPNG_FILTER_OUT]) + if (ri->prefix[ripng_distribute]) { - if (prefix_list_apply (ri->prefix[RIPNG_FILTER_OUT], + if (prefix_list_apply (ri->prefix[ripng_distribute], (struct prefix *) p) == PREFIX_DENY) { if (IS_RIPNG_DEBUG_PACKET) - zlog_debug ("%s/%d is filtered by prefix-list out", - inet6_ntoa (p->prefix), p->prefixlen); + zlog_debug ("%s/%d filtered by prefix-list %s", + inet6_ntoa (p->prefix), p->prefixlen, inout); return -1; } } @@ -583,34 +646,34 @@ ripng_outgoing_filter (struct prefix_ipv6 *p, struct ripng_interface *ri) dist = distribute_lookup (NULL); if (dist) { - if (dist->list[DISTRIBUTE_OUT]) + if (dist->list[distribute]) { - alist = access_list_lookup (AFI_IP6, dist->list[DISTRIBUTE_OUT]); - + alist = access_list_lookup (AFI_IP6, dist->list[distribute]); + if (alist) { if (access_list_apply (alist, (struct prefix *) p) == FILTER_DENY) { if (IS_RIPNG_DEBUG_PACKET) - zlog_debug ("%s/%d filtered by distribute out", - inet6_ntoa (p->prefix), p->prefixlen); + zlog_debug ("%s/%d filtered by distribute %s", + inet6_ntoa (p->prefix), p->prefixlen, inout); return -1; } } } - if (dist->prefix[DISTRIBUTE_OUT]) + if (dist->prefix[distribute]) { - plist = prefix_list_lookup (AFI_IP6, dist->prefix[DISTRIBUTE_OUT]); - + plist = prefix_list_lookup (AFI_IP6, dist->prefix[distribute]); + if (plist) { if (prefix_list_apply (plist, (struct prefix *) p) == PREFIX_DENY) { if (IS_RIPNG_DEBUG_PACKET) - zlog_debug ("%s/%d filtered by prefix-list out", - inet6_ntoa (p->prefix), p->prefixlen); + zlog_debug ("%s/%d filtered by prefix-list %s", + inet6_ntoa (p->prefix), p->prefixlen, inout); return -1; } } @@ -628,11 +691,12 @@ ripng_route_process (struct rte *rte, struct sockaddr_in6 *from, int ret; struct prefix_ipv6 p; struct route_node *rp; - struct ripng_info *rinfo; + struct ripng_info *rinfo = NULL, newinfo; struct ripng_interface *ri; struct in6_addr *nexthop; - u_char oldmetric; int same = 0; + struct list *list = NULL; + struct listnode *node = NULL; /* Make prefix structure. */ memset (&p, 0, sizeof (struct prefix_ipv6)); @@ -649,28 +713,27 @@ ripng_route_process (struct rte *rte, struct sockaddr_in6 *from, /* Apply input filters. */ ri = ifp->info; - ret = ripng_incoming_filter (&p, ri); + ret = ripng_filter (RIPNG_FILTER_IN, &p, ri); if (ret < 0) return; + memset (&newinfo, 0, sizeof (newinfo)); + newinfo.type = ZEBRA_ROUTE_RIPNG; + newinfo.sub_type = RIPNG_ROUTE_RTE; + if (ripng_nexthop->flag == RIPNG_NEXTHOP_ADDRESS) + newinfo.nexthop = ripng_nexthop->address; + else + newinfo.nexthop = from->sin6_addr; + newinfo.from = from->sin6_addr; + newinfo.ifindex = ifp->ifindex; + newinfo.metric = rte->metric; + newinfo.metric_out = rte->metric; /* XXX */ + newinfo.tag = ntohs (rte->tag); /* XXX */ + /* Modify entry. */ if (ri->routemap[RIPNG_FILTER_IN]) { int ret; - struct ripng_info newinfo; - - memset (&newinfo, 0, sizeof (struct ripng_info)); - newinfo.type = ZEBRA_ROUTE_RIPNG; - newinfo.sub_type = RIPNG_ROUTE_RTE; - if (ripng_nexthop->flag == RIPNG_NEXTHOP_ADDRESS) - newinfo.nexthop = ripng_nexthop->address; - else - newinfo.nexthop = from->sin6_addr; - newinfo.from = from->sin6_addr; - newinfo.ifindex = ifp->ifindex; - newinfo.metric = rte->metric; - newinfo.metric_out = rte->metric; /* XXX */ - newinfo.tag = ntohs(rte->tag); /* XXX */ ret = route_map_apply (ri->routemap[RIPNG_FILTER_IN], (struct prefix *)&p, RMAP_RIPNG, &newinfo); @@ -717,7 +780,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; @@ -731,24 +794,66 @@ ripng_route_process (struct rte *rte, struct sockaddr_in6 *from, /* Lookup RIPng routing table. */ rp = route_node_get (ripng->table, (struct prefix *) &p); - /* Sanity check */ - rinfo = rp->info; + newinfo.rp = rp; + newinfo.nexthop = *nexthop; + newinfo.metric = rte->metric; + newinfo.tag = ntohs (rte->tag); + + /* Check to see whether there is already RIPng route on the table. */ + if ((list = rp->info) != NULL) + for (ALL_LIST_ELEMENTS_RO (list, node, rinfo)) + { + /* Need to compare with redistributed entry or local entry */ + if (!ripng_route_rte (rinfo)) + break; + + if (IPV6_ADDR_SAME (&rinfo->from, &from->sin6_addr) && + IPV6_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. Keep "rinfo" null and the new route + * is added in the ECMP list in below. */ + } + } + if (rinfo) { /* Redistributed route check. */ if (rinfo->type != ZEBRA_ROUTE_RIPNG && rinfo->metric != RIPNG_METRIC_INFINITY) - return; + { + route_unlock_node (rp); + return; + } /* Local static route. */ if (rinfo->type == ZEBRA_ROUTE_RIPNG && ((rinfo->sub_type == RIPNG_ROUTE_STATIC) || (rinfo->sub_type == RIPNG_ROUTE_DEFAULT)) && rinfo->metric != RIPNG_METRIC_INFINITY) - return; + { + route_unlock_node (rp); + return; + } } - if (rp->info == NULL) + if (!rinfo) { /* Now, check to see whether there is already an explicit route for the destination prefix. If there is no such route, add @@ -756,53 +861,10 @@ ripng_route_process (struct rte *rte, struct sockaddr_in6 *from, infinity (there is no point in adding a route which unusable). */ if (rte->metric != RIPNG_METRIC_INFINITY) - { - rinfo = ripng_info_new (); - - /* - Setting the destination prefix and length to those in - the RTE. */ - rp->info = rinfo; - 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. */ - IPV6_ADDR_COPY (&rinfo->nexthop, nexthop); - IPV6_ADDR_COPY (&rinfo->from, &from->sin6_addr); - rinfo->ifindex = ifp->ifindex; - - /* - Initialize the timeout for the route. If the - garbage-collection timer is running for this route, stop it. */ - ripng_timeout_update (rinfo); - - /* - Set the route change flag. */ - rinfo->flags |= RIPNG_RTF_CHANGED; - - /* - Signal the output process to trigger an update (see section - 2.5). */ - ripng_event (RIPNG_TRIGGERED_UPDATE, 0); - - /* Finally, route goes into the kernel. */ - rinfo->type = ZEBRA_ROUTE_RIPNG; - rinfo->sub_type = RIPNG_ROUTE_RTE; - - ripng_zebra_ipv6_add (&p, &rinfo->nexthop, rinfo->ifindex, - rinfo->metric); - rinfo->flags |= RIPNG_RTF_FIB; - - /* Aggregate check. */ - ripng_aggregate_increment (rp, rinfo); - } + ripng_ecmp_add (&newinfo); } else { - 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. If this datagram is from the same router as the existing @@ -810,9 +872,6 @@ ripng_route_process (struct rte *rte, struct sockaddr_in6 *from, same = (IN6_ARE_ADDR_EQUAL (&rinfo->from, &from->sin6_addr) && (rinfo->ifindex == ifp->ifindex)); - if (same) - ripng_timeout_update (rinfo); - /* Next, compare the metrics. If the datagram is from the same router as the existing route, and the new metric is different than the old one; or, if the new metric is lower than the old @@ -820,96 +879,24 @@ ripng_route_process (struct rte *rte, struct sockaddr_in6 *from, if ((same && rinfo->metric != rte->metric) || rte->metric < rinfo->metric) { - /* - 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); - IPV6_ADDR_COPY (&rinfo->from, &from->sin6_addr); - rinfo->ifindex = ifp->ifindex; - - /* 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 == RIPNG_METRIC_INFINITY && - rinfo->metric < RIPNG_METRIC_INFINITY) - { - rinfo->type = ZEBRA_ROUTE_RIPNG; - rinfo->sub_type = RIPNG_ROUTE_RTE; - - RIPNG_TIMER_OFF (rinfo->t_garbage_collect); - - if (! IPV6_ADDR_SAME (&rinfo->nexthop, nexthop)) - IPV6_ADDR_COPY (&rinfo->nexthop, nexthop); - - ripng_zebra_ipv6_add (&p, nexthop, ifp->ifindex, rinfo->metric); - rinfo->flags |= RIPNG_RTF_FIB; - - /* The aggregation counter needs to be updated because - the prefixes, which are into the gc, have been - removed from the aggregator (see ripng_timout). */ - ripng_aggregate_increment (rp, rinfo); - } - - /* Update nexthop and/or metric value. */ - if (oldmetric != RIPNG_METRIC_INFINITY) - { - ripng_zebra_ipv6_delete (&p, &rinfo->nexthop, rinfo->ifindex); - ripng_zebra_ipv6_add (&p, nexthop, ifp->ifindex, rinfo->metric); - rinfo->flags |= RIPNG_RTF_FIB; - - if (! IPV6_ADDR_SAME (&rinfo->nexthop, nexthop)) - IPV6_ADDR_COPY (&rinfo->nexthop, nexthop); - } - - /* - Set the route change flag and signal the output process - to trigger an update. */ - rinfo->flags |= RIPNG_RTF_CHANGED; - ripng_event (RIPNG_TRIGGERED_UPDATE, 0); - - /* - If the new metric is infinity, start the deletion - process (described above); */ - if (rinfo->metric == RIPNG_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 != RIPNG_METRIC_INFINITY) - { - /* - The garbage-collection timer is set for 120 seconds. */ - RIPNG_TIMER_ON (rinfo->t_garbage_collect, - ripng_garbage_collect, ripng->garbage_time); - RIPNG_TIMER_OFF (rinfo->t_timeout); - - /* - The metric for the route is set to 16 - (infinity). This causes the route to be removed - from service.*/ - ripng_zebra_ipv6_delete (&p, &rinfo->nexthop, rinfo->ifindex); - rinfo->flags &= ~RIPNG_RTF_FIB; - - /* Aggregate count decrement. */ - ripng_aggregate_decrement (rp, rinfo); - - /* - 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. */ - } - } - else - { - /* otherwise, re-initialize the timeout. */ - ripng_timeout_update (rinfo); - } + if (listcount (list) == 1) + { + if (newinfo.metric != RIPNG_METRIC_INFINITY) + ripng_ecmp_replace (&newinfo); + else + ripng_ecmp_delete (rinfo); + } + else + { + if (newinfo.metric < rinfo->metric) + ripng_ecmp_replace (&newinfo); + else /* newinfo.metric > rinfo->metric */ + ripng_ecmp_delete (rinfo); + } } + else /* same & no change */ + ripng_timeout_update (rinfo); + /* Unlock tempolary lock of the route. */ route_unlock_node (rp); } @@ -918,31 +905,36 @@ ripng_route_process (struct rte *rte, struct sockaddr_in6 *from, /* Add redistributed route to RIPng table. */ void ripng_redistribute_add (int type, int sub_type, struct prefix_ipv6 *p, - unsigned int ifindex, struct in6_addr *nexthop) + ifindex_t ifindex, struct in6_addr *nexthop, + route_tag_t tag) { struct route_node *rp; - struct ripng_info *rinfo; + struct ripng_info *rinfo = NULL, newinfo; + struct list *list = NULL; /* Redistribute route */ if (IN6_IS_ADDR_LINKLOCAL (&p->prefix)) 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; - if (rinfo) + memset (&newinfo, 0, sizeof (struct ripng_info)); + newinfo.type = type; + newinfo.sub_type = sub_type; + newinfo.ifindex = ifindex; + newinfo.metric = 1; + if (tag <= UINT16_MAX) /* RIPng only supports 16 bit tags */ + newinfo.tag = tag; + newinfo.rp = rp; + if (nexthop && IN6_IS_ADDR_LINKLOCAL(nexthop)) + newinfo.nexthop = *nexthop; + + if ((list = rp->info) != NULL && listcount (list) != 0) { + rinfo = listgetdata (listhead (list)); + if (rinfo->type == ZEBRA_ROUTE_CONNECT && rinfo->sub_type == RIPNG_ROUTE_INTERFACE && rinfo->metric != RIPNG_METRIC_INFINITY) { @@ -962,42 +954,12 @@ ripng_redistribute_add (int type, int sub_type, struct prefix_ipv6 *p, return; } } - - RIPNG_TIMER_OFF (rinfo->t_timeout); - RIPNG_TIMER_OFF (rinfo->t_garbage_collect); - - /* Tells the other daemons about the deletion of - * this RIPng route - **/ - if (ripng_route_rte (rinfo)) - ripng_zebra_ipv6_delete ((struct prefix_ipv6 *)&rp->p, &rinfo->nexthop, - rinfo->metric); - - rp->info = NULL; - ripng_info_free (rinfo); + rinfo = ripng_ecmp_replace (&newinfo); route_unlock_node (rp); - } - - rinfo = ripng_info_new (); - - rinfo->type = type; - rinfo->sub_type = sub_type; - rinfo->ifindex = ifindex; - rinfo->metric = 1; - rinfo->rp = rp; - - if (nexthop && IN6_IS_ADDR_LINKLOCAL(nexthop)) - rinfo->nexthop = *nexthop; - - rinfo->flags |= RIPNG_RTF_FIB; - rp->info = rinfo; - - /* Aggregate check. */ - ripng_aggregate_increment (rp, rinfo); - - rinfo->flags |= RIPNG_RTF_CHANGED; + else + rinfo = ripng_ecmp_add (&newinfo); if (IS_RIPNG_DEBUG_EVENT) { if (!nexthop) @@ -1016,7 +978,7 @@ ripng_redistribute_add (int type, int sub_type, struct prefix_ipv6 *p, /* Delete redistributed route to RIPng table. */ void ripng_redistribute_delete (int type, int sub_type, struct prefix_ipv6 *p, - unsigned int ifindex) + ifindex_t ifindex) { struct route_node *rp; struct ripng_info *rinfo; @@ -1025,45 +987,42 @@ 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); if (rp) { - rinfo = rp->info; - - if (rinfo != NULL - && rinfo->type == type - && rinfo->sub_type == sub_type - && rinfo->ifindex == ifindex) - { - /* Perform poisoned reverse. */ - rinfo->metric = RIPNG_METRIC_INFINITY; - RIPNG_TIMER_ON (rinfo->t_garbage_collect, - ripng_garbage_collect, ripng->garbage_time); - RIPNG_TIMER_OFF (rinfo->t_timeout); - - /* Aggregate count decrement. */ - ripng_aggregate_decrement (rp, rinfo); - - rinfo->flags |= RIPNG_RTF_CHANGED; - - if (IS_RIPNG_DEBUG_EVENT) - zlog_debug ("Poisone %s/%d on the interface %s with an infinity metric [delete]", - inet6_ntoa(p->prefix), p->prefixlen, - ifindex2ifname(ifindex)); - - ripng_event (RIPNG_TRIGGERED_UPDATE, 0); - } + struct list *list = rp->info; + + 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 = RIPNG_METRIC_INFINITY; + RIPNG_TIMER_ON (rinfo->t_garbage_collect, + ripng_garbage_collect, ripng->garbage_time); + RIPNG_TIMER_OFF (rinfo->t_timeout); + + /* Aggregate count decrement. */ + ripng_aggregate_decrement (rp, rinfo); + + rinfo->flags |= RIPNG_RTF_CHANGED; + + if (IS_RIPNG_DEBUG_EVENT) + zlog_debug ("Poisone %s/%d on the interface %s with an " + "infinity metric [delete]", + inet6_ntoa (p->prefix), p->prefixlen, + ifindex2ifname (ifindex)); + + ripng_event (RIPNG_TRIGGERED_UPDATE, 0); + } + } + route_unlock_node (rp); } } @@ -1072,14 +1031,16 @@ void ripng_redistribute_withdraw (int type) { struct route_node *rp; - struct ripng_info *rinfo; + struct ripng_info *rinfo = NULL; + struct list *list = NULL; if (!ripng) return; for (rp = route_top (ripng->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 != RIPNG_ROUTE_INTERFACE)) { @@ -1314,7 +1275,7 @@ ripng_request_process (struct ripng_packet *packet,int size, if (rp) { - rinfo = rp->info; + rinfo = listgetdata (listhead ((struct list *)rp->info)); rte->metric = rinfo->metric; route_unlock_node (rp); } @@ -1335,7 +1296,7 @@ ripng_read (struct thread *thread) int sock; struct sockaddr_in6 from; struct ripng_packet *packet; - unsigned int ifindex; + ifindex_t ifindex = 0; struct interface *ifp; int hoplimit = -1; @@ -1422,12 +1383,18 @@ static void ripng_clear_changed_flag (void) { struct route_node *rp; - struct ripng_info *rinfo; + struct ripng_info *rinfo = NULL; + struct list *list = NULL; + struct listnode *listnode = NULL; for (rp = route_top (ripng->table); rp; rp = route_next (rp)) - if ((rinfo = rp->info) != NULL) - if (rinfo->flags & RIPNG_RTF_CHANGED) - rinfo->flags &= ~RIPNG_RTF_CHANGED; + if ((list = rp->info) != NULL) + for (ALL_LIST_ELEMENTS_RO (list, listnode, rinfo)) + { + UNSET_FLAG (rinfo->flags, RIPNG_RTF_CHANGED); + /* This flag can be set only on the first entry. */ + break; + } } /* Regular update of RIPng route. Send all routing formation to RIPng @@ -1605,6 +1572,8 @@ ripng_output_process (struct interface *ifp, struct sockaddr_in6 *to, struct ripng_aggregate *aggregate; struct prefix_ipv6 *p; struct list * ripng_rte_list; + struct list *list = NULL; + struct listnode *listnode = NULL; if (IS_RIPNG_DEBUG_EVENT) { if (to) @@ -1621,7 +1590,9 @@ ripng_output_process (struct interface *ifp, struct sockaddr_in6 *to, for (rp = route_top (ripng->table); rp; rp = route_next (rp)) { - if ((rinfo = rp->info) != NULL && rinfo->suppress == 0) + if ((list = rp->info) != NULL && + (rinfo = listgetdata (listhead (list))) != NULL && + rinfo->suppress == 0) { /* If no route-map are applied, the RTE will be these following * informations. @@ -1640,7 +1611,7 @@ ripng_output_process (struct interface *ifp, struct sockaddr_in6 *to, rinfo->nexthop_out = rinfo->nexthop; /* Apply output filters. */ - ret = ripng_outgoing_filter (p, ri); + ret = ripng_filter (RIPNG_FILTER_OUT, p, ri); if (ret < 0) continue; @@ -1653,8 +1624,17 @@ ripng_output_process (struct interface *ifp, struct sockaddr_in6 *to, if (ri->split_horizon == RIPNG_SPLIT_HORIZON) { /* We perform split horizon for RIPng routes. */ - if ((rinfo->type == ZEBRA_ROUTE_RIPNG) && - rinfo->ifindex == ifp->ifindex) + int suppress = 0; + struct ripng_info *tmp_rinfo = NULL; + + for (ALL_LIST_ELEMENTS_RO (list, listnode, tmp_rinfo)) + if (tmp_rinfo->type == ZEBRA_ROUTE_RIPNG && + tmp_rinfo->ifindex == ifp->ifindex) + { + suppress = 1; + break; + } + if (suppress) continue; } @@ -1733,9 +1713,12 @@ ripng_output_process (struct interface *ifp, struct sockaddr_in6 *to, * for RIPng routes. **/ if (ri->split_horizon == RIPNG_SPLIT_HORIZON_POISONED_REVERSE) { - if ((rinfo->type == ZEBRA_ROUTE_RIPNG) && - rinfo->ifindex == ifp->ifindex) - rinfo->metric_out = RIPNG_METRIC_INFINITY; + struct ripng_info *tmp_rinfo = NULL; + + for (ALL_LIST_ELEMENTS_RO (list, listnode, tmp_rinfo)) + if ((tmp_rinfo->type == ZEBRA_ROUTE_RIPNG) && + tmp_rinfo->ifindex == ifp->ifindex) + rinfo->metric_out = RIPNG_METRIC_INFINITY; } /* Add RTE to the list */ @@ -1757,7 +1740,7 @@ ripng_output_process (struct interface *ifp, struct sockaddr_in6 *to, memset(&aggregate->nexthop_out, 0, sizeof(aggregate->nexthop_out)); /* Apply output filters.*/ - ret = ripng_outgoing_filter (p, ri); + ret = ripng_filter (RIPNG_FILTER_OUT, p, ri); if (ret < 0) continue; @@ -1886,11 +1869,11 @@ ripng_request (struct interface *ifp) NULL, ifp); } - + static int ripng_update_jitter (int time) { - return ((rand () % (time + 1)) - (time / 2)); + return ((random () % (time + 1)) - (time / 2)); } void @@ -1928,7 +1911,7 @@ ripng_event (enum ripng_event event, int sock) break; } } - + /* Print out routes update time. */ static void @@ -2001,6 +1984,8 @@ DEFUN (show_ipv6_ripng, struct ripng_info *rinfo; struct ripng_aggregate *aggregate; struct prefix_ipv6 *p; + struct list *list = NULL; + struct listnode *listnode = NULL; int len; if (! ripng) @@ -2038,7 +2023,8 @@ DEFUN (show_ipv6_ripng, VTY_NEWLINE); } - if ((rinfo = rp->info) != NULL) + if ((list = rp->info) != NULL) + for (ALL_LIST_ELEMENTS_RO (list, listnode, rinfo)) { p = (struct prefix_ipv6 *) &rp->p; @@ -2231,7 +2217,7 @@ DEFUN (ripng_route, } rp->info = (void *)1; - ripng_redistribute_add (ZEBRA_ROUTE_RIPNG, RIPNG_ROUTE_STATIC, &p, 0, NULL); + ripng_redistribute_add (ZEBRA_ROUTE_RIPNG, RIPNG_ROUTE_STATIC, &p, 0, NULL, 0); return CMD_SUCCESS; } @@ -2568,7 +2554,7 @@ DEFUN (ripng_default_information_originate, ripng->default_information = 1; str2prefix_ipv6 ("::/0", &p); - ripng_redistribute_add (ZEBRA_ROUTE_RIPNG, RIPNG_ROUTE_DEFAULT, &p, 0, NULL); + ripng_redistribute_add (ZEBRA_ROUTE_RIPNG, RIPNG_ROUTE_DEFAULT, &p, 0, NULL, 0); } return CMD_SUCCESS; @@ -2593,6 +2579,80 @@ DEFUN (no_ripng_default_information_originate, return CMD_SUCCESS; } +/* Update ECMP routes to zebra when ECMP is disabled. */ +static void +ripng_ecmp_disable (void) +{ + struct route_node *rp; + struct ripng_info *rinfo, *tmp_rinfo; + struct list *list; + struct listnode *node, *nextnode; + + if (!ripng) + return; + + for (rp = route_top (ripng->table); rp; rp = route_next (rp)) + if ((list = rp->info) != NULL && listcount (list) > 1) + { + rinfo = listgetdata (listhead (list)); + if (!ripng_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) + { + RIPNG_TIMER_OFF (tmp_rinfo->t_timeout); + RIPNG_TIMER_OFF (tmp_rinfo->t_garbage_collect); + list_delete_node (list, node); + ripng_info_free (tmp_rinfo); + } + + /* Update zebra. */ + ripng_zebra_ipv6_add (rp); + + /* Set the route change flag. */ + SET_FLAG (rinfo->flags, RIPNG_RTF_CHANGED); + + /* Signal the output process to trigger an update. */ + ripng_event (RIPNG_TRIGGERED_UPDATE, 0); + } +} + +DEFUN (ripng_allow_ecmp, + ripng_allow_ecmp_cmd, + "allow-ecmp", + "Allow Equal Cost MultiPath\n") +{ + if (ripng->ecmp) + { + vty_out (vty, "ECMP is already enabled.%s", VTY_NEWLINE); + return CMD_WARNING; + } + + ripng->ecmp = 1; + zlog_info ("ECMP is enabled."); + return CMD_SUCCESS; +} + +DEFUN (no_ripng_allow_ecmp, + no_ripng_allow_ecmp_cmd, + "no allow-ecmp", + NO_STR + "Allow Equal Cost MultiPath\n") +{ + if (!ripng->ecmp) + { + vty_out (vty, "ECMP is already disabled.%s", VTY_NEWLINE); + return CMD_WARNING; + } + + ripng->ecmp = 0; + zlog_info ("ECMP is disabled."); + ripng_ecmp_disable (); + return CMD_SUCCESS; +} + /* RIPng configuration write function. */ static int ripng_config_write (struct vty *vty) @@ -2632,6 +2692,10 @@ ripng_config_write (struct vty *vty) VTY_NEWLINE); + /* ECMP configuration. */ + if (ripng->ecmp) + vty_out (vty, " allow-ecmp%s", VTY_NEWLINE); + /* RIPng static routes. */ for (rp = route_top (ripng->route); rp; rp = route_next (rp)) if (rp->info != NULL) @@ -2696,9 +2760,9 @@ ripng_distribute_update (struct distribute *dist) ri = ifp->info; - if (dist->list[DISTRIBUTE_IN]) + if (dist->list[DISTRIBUTE_V6_IN]) { - alist = access_list_lookup (AFI_IP6, dist->list[DISTRIBUTE_IN]); + alist = access_list_lookup (AFI_IP6, dist->list[DISTRIBUTE_V6_IN]); if (alist) ri->list[RIPNG_FILTER_IN] = alist; else @@ -2707,9 +2771,9 @@ ripng_distribute_update (struct distribute *dist) else ri->list[RIPNG_FILTER_IN] = NULL; - if (dist->list[DISTRIBUTE_OUT]) + if (dist->list[DISTRIBUTE_V6_OUT]) { - alist = access_list_lookup (AFI_IP6, dist->list[DISTRIBUTE_OUT]); + alist = access_list_lookup (AFI_IP6, dist->list[DISTRIBUTE_V6_OUT]); if (alist) ri->list[RIPNG_FILTER_OUT] = alist; else @@ -2718,9 +2782,9 @@ ripng_distribute_update (struct distribute *dist) else ri->list[RIPNG_FILTER_OUT] = NULL; - if (dist->prefix[DISTRIBUTE_IN]) + if (dist->prefix[DISTRIBUTE_V6_IN]) { - plist = prefix_list_lookup (AFI_IP6, dist->prefix[DISTRIBUTE_IN]); + plist = prefix_list_lookup (AFI_IP6, dist->prefix[DISTRIBUTE_V6_IN]); if (plist) ri->prefix[RIPNG_FILTER_IN] = plist; else @@ -2729,9 +2793,9 @@ ripng_distribute_update (struct distribute *dist) else ri->prefix[RIPNG_FILTER_IN] = NULL; - if (dist->prefix[DISTRIBUTE_OUT]) + if (dist->prefix[DISTRIBUTE_V6_OUT]) { - plist = prefix_list_lookup (AFI_IP6, dist->prefix[DISTRIBUTE_OUT]); + plist = prefix_list_lookup (AFI_IP6, dist->prefix[DISTRIBUTE_V6_OUT]); if (plist) ri->prefix[RIPNG_FILTER_OUT] = plist; else @@ -2767,7 +2831,7 @@ ripng_distribute_update_all_wrapper (struct access_list *notused) { ripng_distribute_update_all(NULL); } - + /* delete all the added ripng routes. */ void ripng_clean() @@ -2775,24 +2839,37 @@ ripng_clean() int i; struct route_node *rp; struct ripng_info *rinfo; + struct ripng_aggregate *aggregate; + struct list *list = NULL; + struct listnode *listnode = NULL; if (ripng) { /* Clear RIPng routes */ - for (rp = route_top (ripng->table); rp; rp = route_next (rp)) { - if ((rinfo = rp->info) != NULL) { - if ((rinfo->type == ZEBRA_ROUTE_RIPNG) && - (rinfo->sub_type == RIPNG_ROUTE_RTE)) - ripng_zebra_ipv6_delete ((struct prefix_ipv6 *)&rp->p, - &rinfo->nexthop, rinfo->metric); - - RIPNG_TIMER_OFF (rinfo->t_timeout); - RIPNG_TIMER_OFF (rinfo->t_garbage_collect); - - rp->info = NULL; - route_unlock_node (rp); - - ripng_info_free(rinfo); - } + for (rp = route_top (ripng->table); rp; rp = route_next (rp)) + { + if ((list = rp->info) != NULL) + { + rinfo = listgetdata (listhead (list)); + if (ripng_route_rte (rinfo)) + ripng_zebra_ipv6_delete (rp); + + for (ALL_LIST_ELEMENTS_RO (list, listnode, rinfo)) + { + RIPNG_TIMER_OFF (rinfo->t_timeout); + RIPNG_TIMER_OFF (rinfo->t_garbage_collect); + ripng_info_free (rinfo); + } + list_delete (list); + rp->info = NULL; + route_unlock_node (rp); + } + + if ((aggregate = rp->aggregate) != NULL) + { + ripng_aggregate_free (aggregate); + rp->aggregate = NULL; + route_unlock_node (rp); + } } /* Cancel the RIPng timers */ @@ -2944,7 +3021,7 @@ void ripng_init () { /* Randomize. */ - srand (time (NULL)); + srandom (time (NULL)); /* Install RIPNG_NODE. */ install_node (&cmd_ripng_node, ripng_config_write); @@ -2953,9 +3030,6 @@ ripng_init () install_element (VIEW_NODE, &show_ipv6_ripng_cmd); install_element (VIEW_NODE, &show_ipv6_ripng_status_cmd); - install_element (ENABLE_NODE, &show_ipv6_ripng_cmd); - install_element (ENABLE_NODE, &show_ipv6_ripng_status_cmd); - install_element (CONFIG_NODE, &router_ripng_cmd); install_element (CONFIG_NODE, &no_router_ripng_cmd); @@ -2984,6 +3058,9 @@ ripng_init () install_element (RIPNG_NODE, &ripng_default_information_originate_cmd); install_element (RIPNG_NODE, &no_ripng_default_information_originate_cmd); + install_element (RIPNG_NODE, &ripng_allow_ecmp_cmd); + install_element (RIPNG_NODE, &no_ripng_allow_ecmp_cmd); + ripng_if_init (); ripng_debug_init (); diff --git a/ripngd/ripngd.h b/ripngd/ripngd.h index ab06d81bf..a0c6a4e58 100644 --- a/ripngd/ripngd.h +++ b/ripngd/ripngd.h @@ -129,6 +129,9 @@ struct ripng struct thread *t_triggered_update; struct thread *t_triggered_interval; + /* RIPng ECMP flag */ + unsigned int ecmp; + /* For redistribute route map. */ struct { @@ -143,7 +146,7 @@ struct ripng struct rte { struct in6_addr addr; /* RIPng destination prefix */ - u_short tag; /* RIPng tag */ + u_int16_t tag; /* RIPng tag */ u_char prefixlen; /* Length of the RIPng prefix */ u_char metric; /* Metric of the RIPng route */ /* The nexthop is stored by the structure @@ -173,7 +176,7 @@ struct ripng_info struct in6_addr from; /* Which interface does this route come from. */ - unsigned int ifindex; + ifindex_t ifindex; /* Metric of this route. */ u_char metric; @@ -197,7 +200,7 @@ struct ripng_info struct in6_addr nexthop_out; u_char metric_set; u_char metric_out; - u_short tag_out; + u_int16_t tag_out; struct route_node *rp; }; @@ -334,9 +337,6 @@ do { \ } \ } while (0) -/* Count prefix size from mask length */ -#define PSIZE(a) (((a) + 7) / (8)) - /* Extern variables. */ extern struct ripng *ripng; @@ -355,7 +355,7 @@ extern void ripng_route_map_init (void); extern void ripng_route_map_reset (void); extern void ripng_terminate (void); /* zclient_init() is done by ripng_zebra.c:zebra_init() */ -extern void zebra_init (void); +extern void zebra_init (struct thread_master *); extern void ripng_zclient_start (void); extern void ripng_zclient_reset (void); extern void ripng_offset_init (void); @@ -381,20 +381,16 @@ extern void ripng_info_free (struct ripng_info *rinfo); extern void ripng_event (enum ripng_event, int); extern int ripng_request (struct interface *ifp); extern void ripng_redistribute_add (int, int, struct prefix_ipv6 *, - unsigned int, struct in6_addr *); + ifindex_t, struct in6_addr *, route_tag_t); extern void ripng_redistribute_delete (int, int, struct prefix_ipv6 *, - unsigned int); + ifindex_t); extern void ripng_redistribute_withdraw (int type); extern void ripng_distribute_update_interface (struct interface *); extern void ripng_if_rmap_update_interface (struct interface *); -extern void ripng_zebra_ipv6_add (struct prefix_ipv6 *p, - struct in6_addr *nexthop, - unsigned int ifindex, u_char metric); -extern void ripng_zebra_ipv6_delete (struct prefix_ipv6 *p, - struct in6_addr *nexthop, - unsigned int ifindex); +extern void ripng_zebra_ipv6_add (struct route_node *); +extern void ripng_zebra_ipv6_delete (struct route_node *); extern void ripng_redistribute_clean (void); extern int ripng_redistribute_check (int); @@ -409,13 +405,23 @@ extern int ripng_send_packet (caddr_t buf, int bufsize, extern void ripng_packet_dump (struct ripng_packet *packet, int size, const char *sndrcv); -extern int ripng_interface_up (int command, struct zclient *, zebra_size_t); -extern int ripng_interface_down (int command, struct zclient *, zebra_size_t); -extern int ripng_interface_add (int command, struct zclient *, zebra_size_t); -extern int ripng_interface_delete (int command, struct zclient *, zebra_size_t); -extern int ripng_interface_address_add (int command, struct zclient *, zebra_size_t); -extern int ripng_interface_address_delete (int command, struct zclient *, zebra_size_t); +extern int ripng_interface_up (int command, struct zclient *, zebra_size_t, + vrf_id_t); +extern int ripng_interface_down (int command, struct zclient *, zebra_size_t, + vrf_id_t); +extern int ripng_interface_add (int command, struct zclient *, zebra_size_t, + vrf_id_t); +extern int ripng_interface_delete (int command, struct zclient *, zebra_size_t, + vrf_id_t); +extern int ripng_interface_address_add (int command, struct zclient *, zebra_size_t, + vrf_id_t); +extern int ripng_interface_address_delete (int command, struct zclient *, zebra_size_t, + vrf_id_t); extern int ripng_network_write (struct vty *, int); +extern struct ripng_info *ripng_ecmp_add (struct ripng_info *); +extern struct ripng_info *ripng_ecmp_replace (struct ripng_info *); +extern struct ripng_info *ripng_ecmp_delete (struct ripng_info *); + #endif /* _ZEBRA_RIPNG_RIPNGD_H */ diff --git a/solaris/quagga.init.in b/solaris/quagga.init.in index 00426241b..ed3350eea 100755 --- a/solaris/quagga.init.in +++ b/solaris/quagga.init.in @@ -120,7 +120,7 @@ routeadm_daemon_args () { # user and group we need for config file upgrade.. SMF_USER=`get_routeadm_property $SMF_FMRI user` - SMF_GROUP=`get_routeadm_property()$SMF_FMRI group` + SMF_GROUP=`get_routeadm_property() $SMF_FMRI group` if [ "${SMF_USER}" ] ; then USER="${SMF_USER}" args="${args} -u ${SMF_USER}" @@ -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 @@ -222,7 +197,7 @@ fi DAEMON="$1" # daemon path must be given -if [ -z "$DAEMON_PATH/$DAEMON" ]; then +if [ "$DAEMON_PATH/$DAEMON" = "/" ]; then usage exit $SMF_EXIT_ERR_FATAL fi @@ -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 @@ -257,9 +231,13 @@ fi upgrade_config "$DAEMON" -if [ ! -f "@sysconfdir@/${DAEMON}.conf" ] ; then - echo "Could not find config file, @sysconfdir@/${DAEMON}.conf" - exit $SMF_EXIT_ERR_CONFIG +CONF_FILE=`get_routeadm_property $SMF_FMRI config_file` +if [ -z "$CONF_FILE" ] ; then + CONF_FILE="@sysconfdir@/${DAEMON}.conf" +fi +if [ ! -f "$CONF_FILE" ] ; then + echo "Could not find config file, $CONF_FILE" + exit $SMF_EXIT_ERR_CONFIG fi # we need @quagga_statedir@ to exist, it probably is on tmpfs. diff --git a/solaris/quagga.xml.in b/solaris/quagga.xml.in index 50c52c226..60427b06e 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 ca0859438..3002b2714 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -5,15 +5,37 @@ tags TAGS .deps .nfs* +*~ +*.loT *.lo *.la *.libs -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 +testcli 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 4ab507bbe..b13e903dc 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -1,14 +1,55 @@ -INCLUDES = @INCLUDES@ -I.. -I$(top_srcdir) -I$(top_srcdir)/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 \ + testcli.in \ + testcli.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) +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 \ + testcli \ + $(TESTS_BGPD) + +../vtysh/vtysh_cmd.c: + $(MAKE) -C ../vtysh vtysh_cmd.c + +test-commands-defun.c: ../vtysh/vtysh_cmd.c + sed \ + -e 's/"vtysh\.h"/"tests.h"/' \ + -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 +CLEANFILES = test-commands-defun.c bgpd libzebra -noinst_PROGRAMS = testsig testbuffer testmemory heavy heavywq heavythread \ - aspathtest testprivs teststream testbgpcap ecommtest \ - testbgpmpattr testchecksum +noinst_HEADERS = prng.h tests.h common-cli.h +testcli_SOURCES = test-cli.c common-cli.c 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 @@ -21,8 +62,16 @@ testbgpcap_SOURCES = bgp_capability_test.c ecommtest_SOURCES = ecommunity_test.c 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 +testcli_LDADD = ../lib/libzebra.la @LIBCAP@ 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@ @@ -30,8 +79,14 @@ teststream_LDADD = ../lib/libzebra.la @LIBCAP@ heavy_LDADD = ../lib/libzebra.la @LIBCAP@ -lm heavywq_LDADD = ../lib/libzebra.la @LIBCAP@ -lm heavythread_LDADD = ../lib/libzebra.la @LIBCAP@ -lm -aspathtest_LDADD = ../lib/libzebra.la @LIBCAP@ -lm ../bgpd/libbgp.a -testbgpcap_LDADD = ../lib/libzebra.la @LIBCAP@ -lm ../bgpd/libbgp.a -ecommtest_LDADD = ../lib/libzebra.la @LIBCAP@ -lm ../bgpd/libbgp.a -testbgpmpattr_LDADD = ../lib/libzebra.la @LIBCAP@ -lm ../bgpd/libbgp.a +aspathtest_LDADD = ../bgpd/libbgp.a ../lib/libzebra.la @LIBCAP@ -lm +testbgpcap_LDADD = ../bgpd/libbgp.a ../lib/libzebra.la @LIBCAP@ -lm +ecommtest_LDADD = ../bgpd/libbgp.a ../lib/libzebra.la @LIBCAP@ -lm +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 4a2ce9aa0..5a0899ecb 100644 --- a/tests/aspath_test.c +++ b/tests/aspath_test.c @@ -1,8 +1,30 @@ +/* + * 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" #include "stream.h" #include "privs.h" +#include "filter.h" #include "bgpd/bgpd.h" #include "bgpd/bgp_aspath.h" @@ -432,6 +454,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 +475,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 +485,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 +499,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 +513,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 +527,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 +541,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 +555,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 +569,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 +583,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 +597,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 +611,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 +625,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 }, }; @@ -983,8 +1043,8 @@ validate (struct aspath *as, const struct test_spec *sp) printf ("private check: %d %d\n", sp->private_as, aspath_private_as_check (as)); } - aspath_unintern (asinout); - aspath_unintern (as4); + aspath_unintern (&asinout); + aspath_unintern (&as4); aspath_free (asconfeddel); aspath_free (asstr); @@ -1030,7 +1090,7 @@ parse_test (struct test_segment *t) printf ("\n"); if (asp) - aspath_unintern (asp); + aspath_unintern (&asp); } /* prepend testing */ @@ -1046,7 +1106,7 @@ prepend_test (struct tests *t) asp2 = make_aspath (t->test2->asdata, t->test2->len, 0); ascratch = aspath_dup (asp2); - aspath_unintern (asp2); + aspath_unintern (&asp2); asp2 = aspath_prepend (asp1, ascratch); @@ -1058,7 +1118,7 @@ prepend_test (struct tests *t) printf ("%s!\n", FAILED); printf ("\n"); - aspath_unintern (asp1); + aspath_unintern (&asp1); aspath_free (asp2); } @@ -1074,7 +1134,7 @@ empty_prepend_test (struct test_segment *t) asp2 = aspath_empty (); ascratch = aspath_dup (asp2); - aspath_unintern (asp2); + aspath_unintern (&asp2); asp2 = aspath_prepend (asp1, ascratch); @@ -1087,7 +1147,7 @@ empty_prepend_test (struct test_segment *t) printf ("\n"); if (asp1) - aspath_unintern (asp1); + aspath_unintern (&asp1); aspath_free (asp2); } @@ -1111,8 +1171,8 @@ as4_reconcile_test (struct tests *t) printf (FAILED "!\n"); printf ("\n"); - aspath_unintern (asp1); - aspath_unintern (asp2); + aspath_unintern (&asp1); + aspath_unintern (&asp2); aspath_free (ascratch); } @@ -1137,8 +1197,8 @@ aggregate_test (struct tests *t) printf (FAILED "!\n"); printf ("\n"); - aspath_unintern (asp1); - aspath_unintern (asp2); + aspath_unintern (&asp1); + aspath_unintern (&asp2); aspath_free (ascratch); /* aspath_unintern (ascratch);*/ } @@ -1185,8 +1245,8 @@ cmp_test () printf (OK "\n"); printf ("\n"); - aspath_unintern (asp1); - aspath_unintern (asp2); + aspath_unintern (&asp1); + aspath_unintern (&asp2); } } @@ -1212,24 +1272,32 @@ 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); if (ret != t->result) { printf ("bgp_attr_parse returned %d, expected %d\n", ret, t->result); - printf ("datalen %d\n", datalen); + printf ("datalen %zd\n", datalen); failed++; } 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,12 +1305,17 @@ 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) - aspath_unintern (attr.aspath); + aspath_unintern (&attr.aspath); if (asp) - aspath_unintern (asp); + aspath_unintern (&asp); return failed - initfail; } @@ -1259,6 +1332,7 @@ main (void) int i = 0; bgp_master_init (); master = bm->master; + bgp_option_set (BGP_OPT_NO_LISTEN); bgp_attr_init (); while (test_segments[i].name) diff --git a/tests/bgp_capability_test.c b/tests/bgp_capability_test.c index 9b43159c7..a3813518b 100644 --- a/tests/bgp_capability_test.c +++ b/tests/bgp_capability_test.c @@ -1,13 +1,36 @@ +/* + * 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" #include "stream.h" #include "privs.h" #include "memory.h" +#include "filter.h" #include "bgpd/bgpd.h" #include "bgpd/bgp_open.h" #include "bgpd/bgp_debug.h" +#include "bgpd/bgp_packet.h" #define VT100_RESET "\x1b[0m" #define VT100_RED "\x1b[31m" @@ -35,7 +58,7 @@ static struct test_segment { #define SHOULD_PARSE 0 #define SHOULD_ERR -1 int parses; /* whether it should parse or not */ - int peek_for; /* what peek_for_as4_capability should say */ + as_t peek_for; /* what peek_for_as4_capability should say */ /* AFI/SAFI validation */ int validate_afi; @@ -289,7 +312,16 @@ static struct test_segment misc_segments[] = { 0x41, 0x4, 0xab, 0xcd, 0xef, 0x12 }, /* AS: 2882400018 */ 6, SHOULD_PARSE, 2882400018, }, - /* 20 */ + { "AS4", + "AS4 capability: short", + { 0x41, 0x4, 0xab, 0xcd, 0xef }, /* AS: 2882400018 */ + 5, SHOULD_ERR, + }, + { "AS4", + "AS4 capability: long", + { 0x41, 0x4, 0xab, 0xcd, 0xef, 0x12, 0x12 }, + 7, SHOULD_ERR, 2882400018, + }, { "GR", "GR capability", { /* hdr */ CAPABILITY_CODE_RESTART, 0xe, @@ -306,7 +338,6 @@ static struct test_segment misc_segments[] = }, 16, SHOULD_PARSE, }, - /* 21 */ { "GR-short", "GR capability, but header length too short", { /* hdr */ 0x40, 0xa, @@ -321,9 +352,8 @@ static struct test_segment misc_segments[] = /* safi */ 0x2, /* flags */ 0x1, }, - 16, SHOULD_PARSE, + 15 /* array is 16 though */, SHOULD_ERR, }, - /* 22 */ { "GR-long", "GR capability, but header length too long", { /* hdr */ 0x40, 0xf, @@ -336,6 +366,7 @@ static struct test_segment misc_segments[] = /* flags */ 0x0, /* afi */ 0x0, 0x2, /* safi */ 0x2, + /* flags */ 0x01, }, 16, SHOULD_ERR, }, @@ -617,6 +648,7 @@ main (void) master = thread_master_create (); bgp_master_init (); + bgp_option_set (BGP_OPT_NO_LISTEN); if (fileno (stdout) >= 0) tty = isatty (fileno (stdout)); @@ -625,7 +657,7 @@ main (void) return -1; peer = peer_create_accept (bgp); - peer->host = "foo"; + peer->host = (char *) "foo"; for (i = AFI_IP; i < AFI_MAX; i++) for (j = SAFI_UNICAST; j < SAFI_MAX; j++) diff --git a/tests/bgp_mp_attr_test.c b/tests/bgp_mp_attr_test.c index 9cbe9f224..3b1bf1452 100644 --- a/tests/bgp_mp_attr_test.c +++ b/tests/bgp_mp_attr_test.c @@ -1,14 +1,40 @@ +/* + * 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" #include "stream.h" #include "privs.h" #include "memory.h" +#include "filter.h" #include "bgpd/bgpd.h" #include "bgpd/bgp_attr.h" #include "bgpd/bgp_open.h" #include "bgpd/bgp_debug.h" +#include "bgpd/bgp_route.h" +#include "bgpd/bgp_packet.h" +#include "bgpd/bgp_mplsvpn.h" +#include "bgpd/bgp_nexthop.h" #define VT100_RESET "\x1b[0m" #define VT100_RED "\x1b[31m" @@ -286,8 +312,34 @@ static struct test_segment { SHOULD_ERR, AFI_IP, SAFI_UNICAST, VALID_AFI, }, - { "IPv4-MLVPN", - "IPv4/MPLS-labeled VPN MP Reach, RD, Nexthop, 3 NLRIs", + { "IPv4-VPNv4", + "IPv4/VPNv4 MP Reach, RD, Nexthop, 2 NLRIs", + { + /* AFI / SAFI */ 0x0, AFI_IP, SAFI_MPLS_LABELED_VPN, + /* nexthop bytes */ 12, + /* RD */ 0, 0, 0, 0, /* RD defined to be 0 */ + 0, 0, 0, 0, + /* Nexthop */ 192, 168, 0, 1, + /* SNPA (defunct, MBZ) */ 0x0, + /* NLRI tuples */ 88 + 16, + 0, 1, 2, /* tag */ + /* rd, 8 octets */ + 0, 0, /* RD_TYPE_AS */ + 0, 2, 0, 0xff, 3, 4, /* AS(2):val(4) */ + 10, 1, /* 10.1/16 */ + 88 + 17, + 0xff, 0, 0, /* tag */ + /* rd, 8 octets */ + 0, 0, /* RD_TYPE_IP */ + 192, 168, 0, 1, /* IPv4 */ + 10, 2, 3, /* 10.2.3/17 */ + }, + (4 + 12 + 1 + (1+3+8+2) + (1+3+8+3)), + SHOULD_PARSE, + AFI_IP, SAFI_MPLS_LABELED_VPN, VALID_AFI, + }, + { "IPv4-VPNv4-bogus-plen", + "IPv4/MPLS-labeled VPN MP Reach, RD, Nexthop, NLRI / bogus p'len", { /* AFI / SAFI */ 0x0, AFI_IP, SAFI_MPLS_LABELED_VPN, /* nexthop bytes */ 12, @@ -299,10 +351,169 @@ static struct test_segment { 17, 10, 2, 3, /* 10.2.3/17 */ 0, /* 0/0 */ }, - (4 + 12 + 1 + 3 + 4 + 1), + (3 + 1 + 3*4 + 1 + 3 + 4 + 1), + SHOULD_ERR, + AFI_IP, SAFI_MPLS_LABELED_VPN, VALID_AFI, + }, + { "IPv4-VPNv4-plen1-short", + "IPv4/VPNv4 MP Reach, RD, Nexthop, 2 NLRIs, 1st plen short", + { + /* AFI / SAFI */ 0x0, AFI_IP, SAFI_MPLS_LABELED_VPN, + /* nexthop bytes */ 12, + /* RD */ 0, 0, 0, 0, /* RD defined to be 0 */ + 0, 0, 0, 0, + /* Nexthop */ 192, 168, 0, 1, + /* SNPA (defunct, MBZ) */ 0x0, + /* NLRI tuples */ 88 + 1, + 0, 1, 2, /* tag */ + /* rd, 8 octets */ + 0, 0, /* RD_TYPE_AS */ + 0, 2, 0, 0xff, 3, 4, /* AS(2):val(4) */ + 10, 1, /* 10.1/16 */ + 88 + 17, + 0xff, 0, 0, /* tag */ + /* rd, 8 octets */ + 0, 0, /* RD_TYPE_IP */ + 192, 168, 0, 1, /* IPv4 */ + 10, 2, 3, /* 10.2.3/17 */ + }, + (4 + 12 + 1 + (1+3+8+2) + (1+3+8+3)), + SHOULD_ERR, + AFI_IP, SAFI_MPLS_LABELED_VPN, VALID_AFI, + }, + { "IPv4-VPNv4-plen1-long", + "IPv4/VPNv4 MP Reach, RD, Nexthop, 2 NLRIs, 1st plen long", + { + /* AFI / SAFI */ 0x0, AFI_IP, SAFI_MPLS_LABELED_VPN, + /* nexthop bytes */ 12, + /* RD */ 0, 0, 0, 0, /* RD defined to be 0 */ + 0, 0, 0, 0, + /* Nexthop */ 192, 168, 0, 1, + /* SNPA (defunct, MBZ) */ 0x0, + /* NLRI tuples */ 88 + 32, + 0, 1, 2, /* tag */ + /* rd, 8 octets */ + 0, 0, /* RD_TYPE_AS */ + 0, 2, 0, 0xff, 3, 4, /* AS(2):val(4) */ + 10, 1, /* 10.1/16 */ + 88 + 17, + 0xff, 0, 0, /* tag */ + /* rd, 8 octets */ + 0, 0, /* RD_TYPE_IP */ + 192, 168, 0, 1, /* IPv4 */ + 10, 2, 3, /* 10.2.3/17 */ + }, + (4 + 12 + 1 + (1+3+8+2) + (1+3+8+3)), + SHOULD_ERR, + AFI_IP, SAFI_MPLS_LABELED_VPN, VALID_AFI, + }, + { "IPv4-VPNv4-plenn-long", + "IPv4/VPNv4 MP Reach, RD, Nexthop, 3 NLRIs, last plen long", + { + /* AFI / SAFI */ 0x0, AFI_IP, SAFI_MPLS_LABELED_VPN, + /* nexthop bytes */ 12, + /* RD */ 0, 0, 0, 0, /* RD defined to be 0 */ + 0, 0, 0, 0, + /* Nexthop */ 192, 168, 0, 1, + /* SNPA (defunct, MBZ) */ 0x0, + /* NLRI tuples */ 88 + 16, + 0, 1, 2, /* tag */ + /* rd, 8 octets */ + 0, 0, /* RD_TYPE_AS */ + 0, 2, 0, 0xff, 3, 4, /* AS(2):val(4) */ + 10, 1, /* 10.1/16 */ + 88 + 17, + 0xff, 0, 0, /* tag */ + /* rd, 8 octets */ + 0, 0, /* RD_TYPE_IP */ + 192, 168, 0, 1, /* IPv4 */ + 10, 2, 3, /* 10.2.3/17 */ + 88 + 1, /* bogus */ + }, + (4 + 12 + 1 + (1+3+8+2) + (1+3+8+3) + 1), + SHOULD_ERR, + AFI_IP, SAFI_MPLS_LABELED_VPN, VALID_AFI, + }, + { "IPv4-VPNv4-plenn-short", + "IPv4/VPNv4 MP Reach, RD, Nexthop, 2 NLRIs, last plen short", + { + /* AFI / SAFI */ 0x0, AFI_IP, SAFI_MPLS_LABELED_VPN, + /* nexthop bytes */ 12, + /* RD */ 0, 0, 0, 0, /* RD defined to be 0 */ + 0, 0, 0, 0, + /* Nexthop */ 192, 168, 0, 1, + /* SNPA (defunct, MBZ) */ 0x0, + /* NLRI tuples */ 88 + 16, + 0, 1, 2, /* tag */ + /* rd, 8 octets */ + 0, 0, /* RD_TYPE_AS */ + 0, 2, 0, 0xff, 3, 4, /* AS(2):val(4) */ + 10, 1, /* 10.1/16 */ + 88 + 2, + 0xff, 0, 0, /* tag */ + /* rd, 8 octets */ + 0, 0, /* RD_TYPE_IP */ + 192, 168, 0, 1, /* IPv4 */ + 10, 2, 3, /* 10.2.3/17 */ + }, + (4 + 12 + 1 + (1+3+8+2) + (1+3+8+3)), + SHOULD_ERR, + AFI_IP, SAFI_MPLS_LABELED_VPN, VALID_AFI, + }, + { "IPv4-VPNv4-bogus-rd-type", + "IPv4/VPNv4 MP Reach, RD, NH, 2 NLRI, unknown RD in 1st (log, but parse)", + { + /* AFI / SAFI */ 0x0, AFI_IP, SAFI_MPLS_LABELED_VPN, + /* nexthop bytes */ 12, + /* RD */ 0, 0, 0, 0, /* RD defined to be 0 */ + 0, 0, 0, 0, + /* Nexthop */ 192, 168, 0, 1, + /* SNPA (defunct, MBZ) */ 0x0, + /* NLRI tuples */ 88 + 16, + 0, 1, 2, /* tag */ + /* rd, 8 octets */ + 0xff, 0, /* Bogus RD */ + 0, 2, 0, 0xff, 3, 4, /* AS(2):val(4) */ + 10, 1, /* 10.1/16 */ + 88 + 17, + 0xff, 0, 0, /* tag */ + /* rd, 8 octets */ + 0, 0, /* RD_TYPE_IP */ + 192, 168, 0, 1, /* IPv4 */ + 10, 2, 3, /* 10.2.3/17 */ + }, + (4 + 12 + 1 + (1+3+8+2) + (1+3+8+3)), SHOULD_PARSE, - AFI_IP, SAFI_UNICAST, VALID_AFI, + AFI_IP, SAFI_MPLS_LABELED_VPN, VALID_AFI, }, + { "IPv4-VPNv4-0-nlri", + "IPv4/VPNv4 MP Reach, RD, Nexthop, 3 NLRI, 3rd 0 bogus", + { + /* AFI / SAFI */ 0x0, AFI_IP, SAFI_MPLS_LABELED_VPN, + /* nexthop bytes */ 12, + /* RD */ 0, 0, 0, 0, /* RD defined to be 0 */ + 0, 0, 0, 0, + /* Nexthop */ 192, 168, 0, 1, + /* SNPA (defunct, MBZ) */ 0x0, + /* NLRI tuples */ 88 + 16, + 0, 1, 2, /* tag */ + /* rd, 8 octets */ + 0, 0, /* RD_TYPE_AS */ + 0, 2, 0, 0xff, 3, 4, /* AS(2):val(4) */ + 10, 1, /* 10.1/16 */ + 88 + 17, + 0xff, 0, 0, /* tag */ + /* rd, 8 octets */ + 0, 0, /* RD_TYPE_IP */ + 192, 168, 0, 1, /* IPv4 */ + 10, 2, 3, /* 10.2.3/17 */ + 0 /* 0/0, bogus for vpnv4 ?? */ + }, + (4 + 12 + 1 + (1+3+8+2) + (1+3+8+3) + 1), + SHOULD_ERR, + AFI_IP, SAFI_MPLS_LABELED_VPN, VALID_AFI, + }, + /* From bug #385 */ { "IPv6-bug", "IPv6, global nexthop, 1 default NLRI", @@ -409,51 +620,41 @@ static struct test_segment mp_unreach_segments [] = SHOULD_ERR, AFI_IP, SAFI_UNICAST, VALID_AFI, }, - { "IPv4-unreach-MLVPN", + { "IPv4-unreach-VPNv4", "IPv4/MPLS-labeled VPN MP Unreach, RD, 3 NLRIs", { /* AFI / SAFI */ 0x0, AFI_IP, SAFI_MPLS_LABELED_VPN, - /* nexthop bytes */ 12, - /* RD */ 0, 0, 1, 2, - 0, 0xff, 3, 4, - /* Nexthop */ 192, 168, 0, 1, - /* SNPA (defunct, MBZ) */ 0x0, - /* NLRI tuples */ 16, 10, 1, /* 10.1/16 */ - 17, 10, 2, 3, /* 10.2.3/17 */ - 0, /* 0/0 */ + /* NLRI tuples */ 88 + 16, + 0, 1, 2, /* tag */ + /* rd, 8 octets */ + 0, 0, /* RD_TYPE_AS */ + 0, 2, 0, 0xff, 3, 4, /* AS(2):val(4) */ + 10, 1, /* 10.1/16 */ + 88 + 17, + 0xff, 0, 0, /* tag */ + /* rd, 8 octets */ + 0, 0, /* RD_TYPE_IP */ + 192, 168, 0, 1, /* IPv4 */ + 10, 2, 3, /* 10.2.3/17 */ }, - (3 + 3 + 4 + 1), + (3 + (1+3+8+2) + (1+3+8+3)), SHOULD_PARSE, - AFI_IP, SAFI_UNICAST, VALID_AFI, + AFI_IP, SAFI_MPLS_LABELED_VPN, VALID_AFI, }, { NULL, NULL, {0}, 0, 0} }; - -/* basic parsing test */ +/* nlri_parse indicates 0 on successful parse, and -1 otherwise. + * attr_parse indicates BGP_ATTR_PARSE_PROCEED/0 on success, + * and BGP_ATTR_PARSE_ERROR/-1 or lower negative ret on err. + */ static void -parse_test (struct peer *peer, struct test_segment *t, int type) +handle_result (struct peer *peer, struct test_segment *t, + int parse_ret, int nlri_ret) { - int ret; int oldfailed = failed; - struct attr attr; - struct bgp_nlri nlri; -#define RANDOM_FUZZ 35 - stream_reset (peer->ibuf); - stream_put (peer->ibuf, NULL, RANDOM_FUZZ); - stream_set_getp (peer->ibuf, RANDOM_FUZZ); - - stream_write (peer->ibuf, t->data, t->len); - - printf ("%s: %s\n", t->name, t->desc); - - if (type == BGP_ATTR_MP_REACH_NLRI) - ret = bgp_mp_reach_parse (peer, t->len, &attr, BGP_ATTR_FLAG_OPTIONAL, BGP_INPUT_PNT (peer), &nlri); - else - ret = bgp_mp_unreach_parse (peer, t->len, BGP_ATTR_FLAG_OPTIONAL, BGP_INPUT_PNT (peer), &nlri); - - if (!ret) + if (!parse_ret) { safi_t safi = t->safi; @@ -466,11 +667,15 @@ parse_test (struct peer *peer, struct test_segment *t, int type) peer->afc_nego[t->afi][safi]); } - printf ("parsed?: %s\n", ret ? "no" : "yes"); + printf ("mp attr parsed?: %s\n", parse_ret ? "no" : "yes"); + if (!parse_ret) + printf ("nrli parsed?: %s\n", nlri_ret ? "no" : "yes"); + printf ("should parse?: %s\n", t->parses ? "no" : "yes"); - if (ret != t->parses) + if ((parse_ret != 0 || nlri_ret != 0) != (t->parses != 0)) failed++; + if (tty) printf ("%s", (failed > oldfailed) ? VT100_RED "failed!" VT100_RESET : VT100_GREEN "OK" VT100_RESET); @@ -483,6 +688,51 @@ parse_test (struct peer *peer, struct test_segment *t, int type) printf ("\n\n"); } +/* basic parsing test */ +static void +parse_test (struct peer *peer, struct test_segment *t, int type) +{ + int parse_ret = 0, nlri_ret = 0; + struct attr attr = { }; + struct bgp_nlri nlri = { }; + struct bgp_attr_parser_args attr_args = { + .peer = peer, + .length = t->len, + .total = 1, + .attr = &attr, + .type = type, + .flags = BGP_ATTR_FLAG_OPTIONAL, + .startp = BGP_INPUT_PNT (peer), + }; +#define RANDOM_FUZZ 35 + + stream_reset (peer->ibuf); + stream_put (peer->ibuf, NULL, RANDOM_FUZZ); + stream_set_getp (peer->ibuf, RANDOM_FUZZ); + + stream_write (peer->ibuf, t->data, t->len); + + printf ("%s: %s\n", t->name, t->desc); + + if (type == BGP_ATTR_MP_REACH_NLRI) + parse_ret = bgp_mp_reach_parse (&attr_args, &nlri); + else + parse_ret = bgp_mp_unreach_parse (&attr_args, &nlri); + + if (parse_ret == 0 && t->afi_valid == VALID_AFI) + assert (nlri.afi == t->afi && nlri.safi == t->safi); + + if (!parse_ret) + { + if (type == BGP_ATTR_MP_REACH_NLRI) + nlri_ret = bgp_nlri_parse (peer, &attr, &nlri); + else + nlri_ret = bgp_nlri_parse (peer, NULL, &nlri); + } + + handle_result (peer, t, parse_ret, nlri_ret); +} + static struct bgp *bgp; static as_t asn = 100; @@ -505,6 +755,10 @@ main (void) master = thread_master_create (); bgp_master_init (); + bgp_option_set (BGP_OPT_NO_LISTEN); + bgp_attr_init (); + bgp_address_init (); + bgp_scan_init (); if (fileno (stdout) >= 0) tty = isatty (fileno (stdout)); @@ -513,7 +767,8 @@ main (void) return -1; peer = peer_create_accept (bgp); - peer->host = "foo"; + peer->host = (char *)"foo"; + peer->status = Established; for (i = AFI_IP; i < AFI_MAX; i++) for (j = SAFI_UNICAST; j < SAFI_MAX; j++) diff --git a/tests/bgp_mpath_test.c b/tests/bgp_mpath_test.c new file mode 100644 index 000000000..174d29987 --- /dev/null +++ b/tests/bgp_mpath_test.c @@ -0,0 +1,488 @@ +/* $QuaggaId: Format:%an, %ai, %h$ $ + * + * BGP Multipath Unit Test + * Copyright (C) 2010 Google 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" +#include "stream.h" +#include "privs.h" +#include "linklist.h" +#include "memory.h" +#include "zclient.h" +#include "filter.h" + +#include "bgpd/bgpd.h" +#include "bgpd/bgp_table.h" +#include "bgpd/bgp_route.h" +#include "bgpd/bgp_attr.h" +#include "bgpd/bgp_mpath.h" + +#define VT100_RESET "\x1b[0m" +#define VT100_RED "\x1b[31m" +#define VT100_GREEN "\x1b[32m" +#define VT100_YELLOW "\x1b[33m" +#define OK VT100_GREEN "OK" VT100_RESET +#define FAILED VT100_RED "failed" VT100_RESET + +#define TEST_PASSED 0 +#define TEST_FAILED -1 + +#define EXPECT_TRUE(expr, res) \ + if (!(expr)) \ + { \ + printf ("Test failure in %s line %u: %s\n", \ + __FUNCTION__, __LINE__, #expr); \ + (res) = TEST_FAILED; \ + } + +typedef struct testcase_t__ testcase_t; + +typedef int (*test_setup_func)(testcase_t *); +typedef int (*test_run_func)(testcase_t *); +typedef int (*test_cleanup_func)(testcase_t *); + +struct testcase_t__ { + const char *desc; + void *test_data; + void *verify_data; + void *tmp_data; + test_setup_func setup; + test_run_func run; + test_cleanup_func cleanup; +}; + +/* need these to link in libbgp */ +struct thread_master *master = NULL; +struct zclient *zclient; +struct zebra_privs_t bgpd_privs = +{ + .user = NULL, + .group = NULL, + .vty_group = NULL, +}; + +static int tty = 0; + +/* Create fake bgp instance */ +static struct bgp * +bgp_create_fake (as_t *as, const char *name) +{ + struct bgp *bgp; + afi_t afi; + safi_t safi; + + if ( (bgp = XCALLOC (MTYPE_BGP, sizeof (struct bgp))) == NULL) + return NULL; + + bgp_lock (bgp); + //bgp->peer_self = peer_new (bgp); + //bgp->peer_self->host = XSTRDUP (MTYPE_BGP_PEER_HOST, "Static announcement"); + + bgp->peer = list_new (); + //bgp->peer->cmp = (int (*)(void *, void *)) peer_cmp; + + bgp->group = list_new (); + //bgp->group->cmp = (int (*)(void *, void *)) peer_group_cmp; + + bgp->rsclient = list_new (); + //bgp->rsclient->cmp = (int (*)(void*, void*)) peer_cmp; + + for (afi = AFI_IP; afi < AFI_MAX; afi++) + for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) + { + bgp->route[afi][safi] = bgp_table_init (afi, safi); + bgp->aggregate[afi][safi] = bgp_table_init (afi, safi); + bgp->rib[afi][safi] = bgp_table_init (afi, safi); + bgp->maxpaths[afi][safi].maxpaths_ebgp = BGP_DEFAULT_MAXPATHS; + bgp->maxpaths[afi][safi].maxpaths_ibgp = BGP_DEFAULT_MAXPATHS; + } + + bgp->default_local_pref = BGP_DEFAULT_LOCAL_PREF; + bgp->default_holdtime = BGP_DEFAULT_HOLDTIME; + bgp->default_keepalive = BGP_DEFAULT_KEEPALIVE; + bgp->restart_time = BGP_DEFAULT_RESTART_TIME; + bgp->stalepath_time = BGP_DEFAULT_STALEPATH_TIME; + + bgp->as = *as; + + if (name) + bgp->name = strdup (name); + + return bgp; +} + +/*========================================================= + * Testcase for maximum-paths configuration + */ +static int +setup_bgp_cfg_maximum_paths (testcase_t *t) +{ + as_t asn = 1; + t->tmp_data = bgp_create_fake (&asn, NULL); + if (!t->tmp_data) + return -1; + return 0; +} + +static int +run_bgp_cfg_maximum_paths (testcase_t *t) +{ + afi_t afi; + safi_t safi; + struct bgp *bgp; + int api_result; + int test_result = TEST_PASSED; + + bgp = t->tmp_data; + for (afi = AFI_IP; afi < AFI_MAX; afi++) + for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) + { + /* test bgp_maximum_paths_set */ + api_result = bgp_maximum_paths_set (bgp, afi, safi, BGP_PEER_EBGP, 10); + EXPECT_TRUE (api_result == 0, test_result); + api_result = bgp_maximum_paths_set (bgp, afi, safi, BGP_PEER_IBGP, 10); + EXPECT_TRUE (api_result == 0, test_result); + EXPECT_TRUE (bgp->maxpaths[afi][safi].maxpaths_ebgp == 10, test_result); + EXPECT_TRUE (bgp->maxpaths[afi][safi].maxpaths_ibgp == 10, test_result); + + /* test bgp_maximum_paths_unset */ + api_result = bgp_maximum_paths_unset (bgp, afi, safi, BGP_PEER_EBGP); + EXPECT_TRUE (api_result == 0, test_result); + api_result = bgp_maximum_paths_unset (bgp, afi, safi, BGP_PEER_IBGP); + EXPECT_TRUE (api_result == 0, test_result); + EXPECT_TRUE ((bgp->maxpaths[afi][safi].maxpaths_ebgp == + BGP_DEFAULT_MAXPATHS), test_result); + EXPECT_TRUE ((bgp->maxpaths[afi][safi].maxpaths_ibgp == + BGP_DEFAULT_MAXPATHS), test_result); + } + + return test_result; +} + +static int +cleanup_bgp_cfg_maximum_paths (testcase_t *t) +{ + return bgp_delete ((struct bgp *)t->tmp_data); +} + +testcase_t test_bgp_cfg_maximum_paths = { + .desc = "Test bgp maximum-paths config", + .setup = setup_bgp_cfg_maximum_paths, + .run = run_bgp_cfg_maximum_paths, + .cleanup = cleanup_bgp_cfg_maximum_paths, +}; + +/*========================================================= + * Testcase for bgp_mp_list + */ + +struct bgp test_mp_bgp; + +struct peer test_mp_list_peer[] = { + { .local_as = 1, .as = 2, .bgp = &test_mp_bgp }, + { .local_as = 1, .as = 2, .bgp = &test_mp_bgp }, + { .local_as = 1, .as = 2, .bgp = &test_mp_bgp }, + { .local_as = 1, .as = 2, .bgp = &test_mp_bgp }, + { .local_as = 1, .as = 2, .bgp = &test_mp_bgp }, +}; +int test_mp_list_peer_count = sizeof (test_mp_list_peer)/ sizeof (struct peer); +struct attr test_mp_list_attr[4]; +struct bgp_info test_mp_list_info[] = { + { .peer = &test_mp_list_peer[0], .attr = &test_mp_list_attr[0] }, + { .peer = &test_mp_list_peer[1], .attr = &test_mp_list_attr[1] }, + { .peer = &test_mp_list_peer[2], .attr = &test_mp_list_attr[1] }, + { .peer = &test_mp_list_peer[3], .attr = &test_mp_list_attr[2] }, + { .peer = &test_mp_list_peer[4], .attr = &test_mp_list_attr[3] }, +}; +int test_mp_list_info_count = + sizeof (test_mp_list_info)/sizeof (struct bgp_info); + +static int +setup_bgp_mp_list (testcase_t *t) +{ + test_mp_list_attr[0].nexthop.s_addr = 0x01010101; + test_mp_list_attr[1].nexthop.s_addr = 0x02020202; + test_mp_list_attr[2].nexthop.s_addr = 0x03030303; + test_mp_list_attr[3].nexthop.s_addr = 0x04040404; + + if ((test_mp_list_peer[0].su_remote = sockunion_str2su ("1.1.1.1")) == NULL) + return -1; + if ((test_mp_list_peer[1].su_remote = sockunion_str2su ("2.2.2.2")) == NULL) + return -1; + if ((test_mp_list_peer[2].su_remote = sockunion_str2su ("3.3.3.3")) == NULL) + return -1; + if ((test_mp_list_peer[3].su_remote = sockunion_str2su ("4.4.4.4")) == NULL) + return -1; + if ((test_mp_list_peer[4].su_remote = sockunion_str2su ("5.5.5.5")) == NULL) + return -1; + + return 0; +} + +static int +run_bgp_mp_list (testcase_t *t) +{ + struct list mp_list; + struct listnode *mp_node; + struct bgp_info *info; + int i; + int test_result = TEST_PASSED; + bgp_mp_list_init (&mp_list); + EXPECT_TRUE (listcount(&mp_list) == 0, test_result); + + bgp_mp_list_add (&mp_list, &test_mp_list_info[1]); + bgp_mp_list_add (&mp_list, &test_mp_list_info[4]); + bgp_mp_list_add (&mp_list, &test_mp_list_info[2]); + bgp_mp_list_add (&mp_list, &test_mp_list_info[3]); + bgp_mp_list_add (&mp_list, &test_mp_list_info[0]); + + for (i = 0, mp_node = mp_list.head; i < test_mp_list_info_count; + i++, mp_node = listnextnode(mp_node)) + { + info = listgetdata(mp_node); + EXPECT_TRUE (info == &test_mp_list_info[i], test_result); + } + + bgp_mp_list_clear (&mp_list); + EXPECT_TRUE (listcount(&mp_list) == 0, test_result); + + return test_result; +} + +static int +cleanup_bgp_mp_list (testcase_t *t) +{ + int i; + + for (i = 0; i < test_mp_list_peer_count; i++) + sockunion_free (test_mp_list_peer[i].su_remote); + + return 0; +} + +testcase_t test_bgp_mp_list = { + .desc = "Test bgp_mp_list", + .setup = setup_bgp_mp_list, + .run = run_bgp_mp_list, + .cleanup = cleanup_bgp_mp_list, +}; + +/*========================================================= + * Testcase for bgp_info_mpath_update + */ + +struct bgp_node test_rn; + +static int +setup_bgp_info_mpath_update (testcase_t *t) +{ + int i; + str2prefix ("42.1.1.0/24", &test_rn.p); + setup_bgp_mp_list (t); + for (i = 0; i < test_mp_list_info_count; i++) + bgp_info_add (&test_rn, &test_mp_list_info[i]); + return 0; +} + +static int +run_bgp_info_mpath_update (testcase_t *t) +{ + struct bgp_info *new_best, *old_best, *mpath; + struct list mp_list; + + test_mp_bgp.maxpaths[AFI_IP][SAFI_UNICAST].maxpaths_ebgp = 3; + test_mp_bgp.maxpaths[AFI_IP][SAFI_UNICAST].maxpaths_ibgp = 3; + + int test_result = TEST_PASSED; + bgp_mp_list_init (&mp_list); + bgp_mp_list_add (&mp_list, &test_mp_list_info[4]); + bgp_mp_list_add (&mp_list, &test_mp_list_info[3]); + bgp_mp_list_add (&mp_list, &test_mp_list_info[0]); + bgp_mp_list_add (&mp_list, &test_mp_list_info[1]); + new_best = &test_mp_list_info[3]; + old_best = NULL; + bgp_info_mpath_update (&test_rn, new_best, old_best, &mp_list, AFI_IP, SAFI_UNICAST); + bgp_mp_list_clear (&mp_list); + EXPECT_TRUE (bgp_info_mpath_count (new_best) == 2, test_result); + mpath = bgp_info_mpath_first (new_best); + EXPECT_TRUE (mpath == &test_mp_list_info[0], test_result); + EXPECT_TRUE (CHECK_FLAG (mpath->flags, BGP_INFO_MULTIPATH), test_result); + mpath = bgp_info_mpath_next (mpath); + EXPECT_TRUE (mpath == &test_mp_list_info[1], test_result); + EXPECT_TRUE (CHECK_FLAG (mpath->flags, BGP_INFO_MULTIPATH), test_result); + + bgp_mp_list_add (&mp_list, &test_mp_list_info[0]); + bgp_mp_list_add (&mp_list, &test_mp_list_info[1]); + new_best = &test_mp_list_info[0]; + old_best = &test_mp_list_info[3]; + bgp_info_mpath_update (&test_rn, new_best, old_best, &mp_list, AFI_IP, SAFI_UNICAST); + bgp_mp_list_clear (&mp_list); + EXPECT_TRUE (bgp_info_mpath_count (new_best) == 1, test_result); + mpath = bgp_info_mpath_first (new_best); + EXPECT_TRUE (mpath == &test_mp_list_info[1], test_result); + EXPECT_TRUE (CHECK_FLAG (mpath->flags, BGP_INFO_MULTIPATH), test_result); + EXPECT_TRUE (!CHECK_FLAG (test_mp_list_info[0].flags, BGP_INFO_MULTIPATH), + test_result); + + return test_result; +} + +static int +cleanup_bgp_info_mpath_update (testcase_t *t) +{ + int i; + + for (i = 0; i < test_mp_list_peer_count; i++) + sockunion_free (test_mp_list_peer[i].su_remote); + + return 0; +} + +testcase_t test_bgp_info_mpath_update = { + .desc = "Test bgp_info_mpath_update", + .setup = setup_bgp_info_mpath_update, + .run = run_bgp_info_mpath_update, + .cleanup = cleanup_bgp_info_mpath_update, +}; + +/*========================================================= + * Set up testcase vector + */ +testcase_t *all_tests[] = { + &test_bgp_cfg_maximum_paths, + &test_bgp_mp_list, + &test_bgp_info_mpath_update, +}; + +int all_tests_count = (sizeof(all_tests)/sizeof(testcase_t *)); + +/*========================================================= + * Test Driver Functions + */ +static int +global_test_init (void) +{ + master = thread_master_create (); + zclient = zclient_new (master); + bgp_master_init (); + bgp_option_set (BGP_OPT_NO_LISTEN); + + if (fileno (stdout) >= 0) + tty = isatty (fileno (stdout)); + return 0; +} + +static int +global_test_cleanup (void) +{ + if (zclient != NULL) + zclient_free (zclient); + thread_master_free (master); + return 0; +} + +static void +display_result (testcase_t *test, int result) +{ + if (tty) + printf ("%s: %s\n", test->desc, result == TEST_PASSED ? OK : FAILED); + else + printf ("%s: %s\n", test->desc, result == TEST_PASSED ? "OK" : "FAILED"); +} + +static int +setup_test (testcase_t *t) +{ + int res = 0; + if (t->setup) + res = t->setup (t); + return res; +} + +static int +cleanup_test (testcase_t *t) +{ + int res = 0; + if (t->cleanup) + res = t->cleanup (t); + return res; +} + +static void +run_tests (testcase_t *tests[], int num_tests, int *pass_count, int *fail_count) +{ + int test_index, result; + testcase_t *cur_test; + + *pass_count = *fail_count = 0; + + for (test_index = 0; test_index < num_tests; test_index++) + { + cur_test = tests[test_index]; + if (!cur_test->desc) + { + printf ("error: test %d has no description!\n", test_index); + continue; + } + if (!cur_test->run) + { + printf ("error: test %s has no run function!\n", cur_test->desc); + continue; + } + if (setup_test (cur_test) != 0) + { + printf ("error: setup failed for test %s\n", cur_test->desc); + continue; + } + result = cur_test->run (cur_test); + if (result == TEST_PASSED) + *pass_count += 1; + else + *fail_count += 1; + display_result (cur_test, result); + if (cleanup_test (cur_test) != 0) + { + printf ("error: cleanup failed for test %s\n", cur_test->desc); + continue; + } + } +} + +int +main (void) +{ + int pass_count, fail_count; + time_t cur_time; + + time (&cur_time); + printf("BGP Multipath Tests Run at %s", ctime(&cur_time)); + if (global_test_init () != 0) + { + printf("Global init failed. Terminating.\n"); + exit(1); + } + run_tests (all_tests, all_tests_count, &pass_count, &fail_count); + global_test_cleanup (); + printf("Total pass/fail: %d/%d\n", pass_count, fail_count); + return fail_count; +} diff --git a/tests/bgpd.tests/Makefile.am b/tests/bgpd.tests/Makefile.am new file mode 100644 index 000000000..5900186cf --- /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 000000000..dfecec780 --- /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 000000000..074952fab --- /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 000000000..2572623f0 --- /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 000000000..96a51e390 --- /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 000000000..e6d7305a6 --- /dev/null +++ b/tests/bgpd.tests/testbgpmpattr.exp @@ -0,0 +1,38 @@ +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-VPNv4: IPv4/VPNv4 MP Reach, RD, Nexthop, 2 NLRIs" +simpletest "IPv4-VPNv4-bogus-plen: IPv4/MPLS-labeled VPN MP Reach, RD, Nexthop, NLRI / bogus p'len" +simpletest "IPv4-VPNv4-plen1-short: IPv4/VPNv4 MP Reach, RD, Nexthop, 2 NLRIs, 1st plen short" +simpletest "IPv4-VPNv4-plen1-long: IPv4/VPNv4 MP Reach, RD, Nexthop, 2 NLRIs, 1st plen long" +simpletest "IPv4-VPNv4-plenn-long: IPv4/VPNv4 MP Reach, RD, Nexthop, 3 NLRIs, last plen long" +simpletest "IPv4-VPNv4-plenn-short: IPv4/VPNv4 MP Reach, RD, Nexthop, 2 NLRIs, last plen short" +simpletest "IPv4-VPNv4-bogus-rd-type: IPv4/VPNv4 MP Reach, RD, NH, 2 NLRI, unknown RD in 1st (log, but parse)" +simpletest "IPv4-VPNv4-0-nlri: IPv4/VPNv4 MP Reach, RD, Nexthop, 3 NLRI, 3rd 0 bogus" +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-VPNv4: IPv4/MPLS-labeled VPN MP Unreach, RD, 3 NLRIs" + diff --git a/tests/common-cli.c b/tests/common-cli.c new file mode 100644 index 000000000..7135856e9 --- /dev/null +++ b/tests/common-cli.c @@ -0,0 +1,90 @@ +/* + * generic CLI test helper functions + * + * Copyright (C) 2015 by David Lamparter, + * for Open Source Routing / NetDEF, Inc. + * + * 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 "thread.h" +#include "vty.h" +#include "command.h" +#include "memory.h" +#include "log.h" + +#include "common-cli.h" + +struct thread_master *master; + +int dump_args(struct vty *vty, const char *descr, + int argc, const char **argv) +{ + int i; + vty_out (vty, "%s with %d args.%s", descr, argc, VTY_NEWLINE); + for (i = 0; i < argc; i++) + { + vty_out (vty, "[%02d]: %s%s", i, argv[i], VTY_NEWLINE); + } + + return CMD_SUCCESS; +} + +static void vty_do_exit(void) +{ + printf ("\nend.\n"); + exit (0); +} + +/* main routine. */ +int +main (int argc, char **argv) +{ + struct thread thread; + + /* Set umask before anything for security */ + umask (0027); + + /* master init. */ + master = thread_master_create (); + + zlog_default = openzlog ("common-cli", 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, ZLOG_DISABLED); + zlog_set_level (NULL, ZLOG_DEST_MONITOR, LOG_DEBUG); + + /* Library inits. */ + cmd_init (1); + host.name = strdup ("test"); + + vty_init (master); + memory_init (); + + test_init (); + + vty_stdio (vty_do_exit); + + /* Fetch next active thread. */ + while (thread_fetch (master, &thread)) + thread_call (&thread); + + /* Not reached. */ + exit (0); +} + diff --git a/tests/common-cli.h b/tests/common-cli.h new file mode 100644 index 000000000..8f6751512 --- /dev/null +++ b/tests/common-cli.h @@ -0,0 +1,49 @@ +/* + * generic CLI test helper functions + * + * Copyright (C) 2015 by David Lamparter, + * for Open Source Routing / NetDEF, Inc. + * + * 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 _COMMON_CLI_H +#define _COMMON_CLI_H + +#include "zebra.h" +#include "vty.h" +#include "command.h" + +/* function to be implemented by test */ +extern void test_init (void); + +/* functions provided by common cli + * (includes main()) + */ +extern struct thread_master *master; + +extern int dump_args(struct vty *vty, const char *descr, + int argc, const char **argv); + +#define DUMMY_HELPSTR \ + "00\n01\n02\n03\n04\n05\n06\n07\n08\n09\n" \ + "10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n" \ + "20\n21\n22\n23\n24\n25\n26\n27\n28\n29\n" +#define DUMMY_DEFUN(name, cmdstr) \ + DEFUN (name, name ## _cmd, cmdstr, DUMMY_HELPSTR) \ + { return dump_args(vty, #name, argc, argv); } + +#endif /* _COMMON_CLI_H */ diff --git a/tests/config/unix.exp b/tests/config/unix.exp new file mode 100644 index 000000000..2f6bceadb --- /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 418f659f7..23ee40583 100644 --- a/tests/ecommunity_test.c +++ b/tests/ecommunity_test.c @@ -1,9 +1,30 @@ +/* + * 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" #include "stream.h" #include "privs.h" #include "memory.h" +#include "filter.h" #include "bgpd/bgpd.h" #include "bgpd/bgp_ecommunity.h" @@ -25,7 +46,7 @@ struct test_spec static struct test_segment { const char *name; const char *desc; - const u_char data[1024]; + const u_int8_t data[1024]; int len; struct test_spec sp; } test_segments [] = @@ -97,7 +118,7 @@ validate (struct ecommunity *ecom, const struct test_spec *sp) str1, (etmp && str2) ? str2 : "NULL"); } - ecommunity_free (etmp); + ecommunity_free (&etmp); XFREE (MTYPE_ECOMMUNITY_STR, str1); XFREE (MTYPE_ECOMMUNITY_STR, str2); @@ -112,7 +133,7 @@ parse_test (struct test_segment *t) printf ("%s: %s\n", t->name, t->desc); - ecom = ecommunity_parse (t->data, t->len); + ecom = ecommunity_parse ((u_int8_t *)t->data, t->len); printf ("ecom: %s\nvalidating...:\n", ecommunity_str (ecom)); @@ -122,7 +143,7 @@ parse_test (struct test_segment *t) printf ("failed\n"); printf ("\n"); - ecommunity_unintern (ecom); + ecommunity_unintern (&ecom); } diff --git a/tests/global-conf.exp b/tests/global-conf.exp new file mode 100644 index 000000000..e69de29bb diff --git a/tests/heavy-thread.c b/tests/heavy-thread.c index cd3a3b9d0..c2e71c17d 100644 --- a/tests/heavy-thread.c +++ b/tests/heavy-thread.c @@ -37,6 +37,8 @@ #include "memory.h" #include "log.h" +#include "tests.h" + extern struct thread_master *master; enum diff --git a/tests/heavy-wq.c b/tests/heavy-wq.c index e5f688c21..2f133cc5d 100644 --- a/tests/heavy-wq.c +++ b/tests/heavy-wq.c @@ -36,6 +36,8 @@ #include "workqueue.h" #include +#include "tests.h" + extern struct thread_master *master; static struct work_queue *heavy_wq; diff --git a/tests/heavy.c b/tests/heavy.c index 577a4816f..9af46c88f 100644 --- a/tests/heavy.c +++ b/tests/heavy.c @@ -36,6 +36,8 @@ #include "memory.h" #include +#include "tests.h" + enum { ITERS_FIRST = 0, diff --git a/tests/lib/bgpd.exp b/tests/lib/bgpd.exp new file mode 100644 index 000000000..e69de29bb diff --git a/tests/lib/libzebra.exp b/tests/lib/libzebra.exp new file mode 100644 index 000000000..e69de29bb diff --git a/tests/libzebra.tests/Makefile.am b/tests/libzebra.tests/Makefile.am new file mode 100644 index 000000000..4b74e2d3f --- /dev/null +++ b/tests/libzebra.tests/Makefile.am @@ -0,0 +1,6 @@ +EXTRA_DIST = \ + tabletest.exp \ + test-timer-correctness.exp \ + testcommands.exp \ + testcli.exp \ + testnexthopiter.exp diff --git a/tests/libzebra.tests/tabletest.exp b/tests/libzebra.tests/tabletest.exp new file mode 100644 index 000000000..5838d4fc7 --- /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 000000000..83531c7df --- /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/testcli.exp b/tests/libzebra.tests/testcli.exp new file mode 100644 index 000000000..778bd0caa --- /dev/null +++ b/tests/libzebra.tests/testcli.exp @@ -0,0 +1,23 @@ +set timeout 30 +set test_name "testcli" + +spawn sh -c "./testcli < $env(srcdir)/testcli.in | diff -au $env(srcdir)/testcli.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/testcommands.exp b/tests/libzebra.tests/testcommands.exp new file mode 100644 index 000000000..d4bfc8231 --- /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 000000000..be35a0a2b --- /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 000000000..ca602e305 --- /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 e0fbb4d51..5396c7d50 100644 --- a/tests/main.c +++ b/tests/main.c @@ -52,7 +52,7 @@ DEFUN (daemon_exit, } static int timer_count; -int +static int test_timer (struct thread *thread) { int *count = THREAD_ARG(thread); @@ -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 000000000..bdcfb07af --- /dev/null +++ b/tests/prng.c @@ -0,0 +1,132 @@ +/* + * 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 + +#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 000000000..cf0bacc5f --- /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/table_test.c b/tests/table_test.c new file mode 100644 index 000000000..fc9cc3dd9 --- /dev/null +++ b/tests/table_test.c @@ -0,0 +1,555 @@ +/* $QuaggaId: Format:%an, %ai, %h$ $ + * + * Routing table test + * Copyright (C) 2012 OSR. + * + * 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 "table.h" + +/* + * test_node_t + * + * Information that is kept for each node in the radix tree. + */ +typedef struct test_node_t_ +{ + + /* + * Human readable representation of the string. Allocated using + * malloc()/dup(). + */ + char *prefix_str; +} test_node_t; + +struct thread_master *master; + +/* + * add_node + * + * Add the given prefix (passed in as a string) to the given table. + */ +static void +add_node (struct route_table *table, const char *prefix_str) +{ + struct prefix_ipv4 p; + test_node_t *node; + struct route_node *rn; + + assert (prefix_str); + + if (str2prefix_ipv4 (prefix_str, &p) <= 0) + { + assert (0); + } + + rn = route_node_get (table, (struct prefix *) &p); + if (rn->info) + { + assert (0); + return; + } + + node = malloc (sizeof (test_node_t)); + assert (node); + node->prefix_str = strdup (prefix_str); + assert (node->prefix_str); + rn->info = node; +} + +/* + * add_nodes + * + * Convenience function to add a bunch of nodes together. + * + * The arguments must be prefixes in string format, with a NULL as the + * last argument. + */ +static void +add_nodes (struct route_table *table, ...) +{ + va_list arglist; + char *prefix; + + va_start (arglist, table); + + prefix = va_arg (arglist, char *); + while (prefix) + { + add_node (table, prefix); + prefix = va_arg (arglist, char *); + } + + va_end (arglist); +} + +/* + * print_subtree + * + * Recursive function to print a route node and its children. + * + * @see print_table + */ +static void +print_subtree (struct route_node *rn, const char *legend, int indent_level) +{ + char buf[INET_ADDRSTRLEN + 4]; + int i; + + /* + * Print this node first. + */ + for (i = 0; i < indent_level; i++) + { + printf (" "); + } + + prefix2str (&rn->p, buf, sizeof (buf)); + printf ("%s: %s", legend, buf); + if (!rn->info) + { + printf (" (internal)"); + } + printf ("\n"); + if (rn->l_left) + { + print_subtree (rn->l_left, "Left", indent_level + 1); + } + if (rn->l_right) + { + print_subtree (rn->l_right, "Right", indent_level + 1); + } +} + +/* + * print_table + * + * Function that prints out the internal structure of a route table. + */ +static void +print_table (struct route_table *table) +{ + struct route_node *rn; + + rn = table->top; + + if (!rn) + { + printf ("\n"); + return; + } + + print_subtree (rn, "Top", 0); +} + +/* + * clear_table + * + * Remove all nodes from the given table. + */ +static void +clear_table (struct route_table *table) +{ + route_table_iter_t iter; + struct route_node *rn; + test_node_t *node; + + route_table_iter_init (&iter, table); + + while ((rn = route_table_iter_next (&iter))) + { + node = rn->info; + if (!node) + { + continue; + } + rn->info = NULL; + route_unlock_node (rn); + free (node->prefix_str); + free (node); + } + + route_table_iter_cleanup (&iter); + + assert (table->top == NULL); +} + +/* + * verify_next_by_iterating + * + * Iterate over the tree to make sure that the first prefix after + * target_pfx is the expected one. Note that target_pfx may not be + * present in the tree. + */ +static void +verify_next_by_iterating (struct route_table *table, + struct prefix *target_pfx, struct prefix *next_pfx) +{ + route_table_iter_t iter; + struct route_node *rn; + + route_table_iter_init (&iter, table); + while ((rn = route_table_iter_next (&iter))) + { + if (route_table_prefix_iter_cmp (&rn->p, target_pfx) > 0) + { + assert (!prefix_cmp (&rn->p, next_pfx)); + break; + } + } + + if (!rn) + { + assert (!next_pfx); + } + + route_table_iter_cleanup (&iter); +} + +/* + * verify_next + * + * Verifies that route_table_get_next() returns the expected result + * (result) for the prefix string 'target'. + */ +static void +verify_next (struct route_table *table, const char *target, const char *next) +{ + struct prefix_ipv4 target_pfx, next_pfx; + struct route_node *rn; + char result_buf[INET_ADDRSTRLEN + 4]; + + if (str2prefix_ipv4 (target, &target_pfx) <= 0) + { + assert (0); + } + + rn = route_table_get_next (table, (struct prefix *) &target_pfx); + if (rn) + { + prefix2str (&rn->p, result_buf, sizeof (result_buf)); + } + else + { + snprintf (result_buf, sizeof (result_buf), "(Null)"); + } + + printf ("\n"); + print_table (table); + printf ("Verifying successor of %s. Expected: %s, Result: %s\n", target, + next ? next : "(Null)", result_buf); + + if (!rn) + { + assert (!next); + verify_next_by_iterating (table, (struct prefix *) &target_pfx, NULL); + return; + } + + assert (next); + + if (str2prefix_ipv4 (next, &next_pfx) <= 0) + { + assert (0); + } + + if (prefix_cmp (&rn->p, (struct prefix *) &next_pfx)) + { + assert (0); + } + route_unlock_node (rn); + + verify_next_by_iterating (table, (struct prefix *) &target_pfx, + (struct prefix *) &next_pfx); +} + +/* + * test_get_next + */ +static void +test_get_next (void) +{ + struct route_table *table; + + printf ("\n\nTesting route_table_get_next()\n"); + table = route_table_init (); + + /* + * Target exists in tree, but has no successor. + */ + add_nodes (table, "1.0.1.0/24", NULL); + verify_next (table, "1.0.1.0/24", NULL); + clear_table (table); + + /* + * Target exists in tree, and there is a node in its left subtree. + */ + add_nodes (table, "1.0.1.0/24", "1.0.1.0/25", NULL); + verify_next (table, "1.0.1.0/24", "1.0.1.0/25"); + clear_table (table); + + /* + * Target exists in tree, and there is a node in its right subtree. + */ + add_nodes (table, "1.0.1.0/24", "1.0.1.128/25", NULL); + verify_next (table, "1.0.1.0/24", "1.0.1.128/25"); + clear_table (table); + + /* + * Target exists in the tree, next node is outside subtree. + */ + add_nodes (table, "1.0.1.0/24", "1.1.0.0/16", NULL); + verify_next (table, "1.0.1.0/24", "1.1.0.0/16"); + clear_table (table); + + /* + * The target node does not exist in the tree for all the test cases + * below this point. + */ + + /* + * There is no successor in the tree. + */ + add_nodes (table, "1.0.0.0/16", NULL); + verify_next (table, "1.0.1.0/24", NULL); + clear_table (table); + + /* + * There exists a node that would be in the target's left subtree. + */ + add_nodes (table, "1.0.0.0/16", "1.0.1.0/25", NULL); + verify_next (table, "1.0.1.0/24", "1.0.1.0/25"); + clear_table (table); + + /* + * There exists a node would be in the target's right subtree. + */ + add_nodes (table, "1.0.0.0/16", "1.0.1.128/25", NULL); + verify_next (table, "1.0.1.0/24", "1.0.1.128/25"); + clear_table (table); + + /* + * A search for the target reaches a node where there are no child + * nodes in the direction of the target (left), but the node has a + * right child. + */ + add_nodes (table, "1.0.0.0/16", "1.0.128.0/17", NULL); + verify_next (table, "1.0.0.0/17", "1.0.128.0/17"); + clear_table (table); + + /* + * A search for the target reaches a node with no children. We have + * to go upwards in the tree to find a successor. + */ + add_nodes (table, "1.0.0.0/16", "1.0.0.0/24", "1.0.1.0/24", + "1.0.128.0/17", NULL); + verify_next (table, "1.0.1.0/25", "1.0.128.0/17"); + clear_table (table); + + /* + * A search for the target reaches a node where neither the node nor + * the target prefix contain each other. + * + * In first case below the node succeeds the target. + * + * In the second case, the node comes before the target, so we have + * to go up the tree looking for a successor. + */ + add_nodes (table, "1.0.0.0/16", "1.0.1.0/24", NULL); + verify_next (table, "1.0.0.0/24", "1.0.1.0/24"); + clear_table (table); + + add_nodes (table, "1.0.0.0/16", "1.0.0.0/24", "1.0.1.0/25", + "1.0.128.0/17", NULL); + verify_next (table, "1.0.1.128/25", "1.0.128.0/17"); + clear_table (table); + + route_table_finish (table); +} + +/* + * verify_prefix_iter_cmp + */ +static void +verify_prefix_iter_cmp (const char *p1, const char *p2, int exp_result) +{ + struct prefix_ipv4 p1_pfx, p2_pfx; + int result; + + if (str2prefix_ipv4 (p1, &p1_pfx) <= 0) + { + assert (0); + } + + if (str2prefix_ipv4 (p2, &p2_pfx) <= 0) + { + assert (0); + } + + result = route_table_prefix_iter_cmp ((struct prefix *) &p1_pfx, + (struct prefix *) &p2_pfx); + + printf ("Verifying cmp(%s, %s) returns %d\n", p1, p2, exp_result); + + assert (exp_result == result); + + /* + * Also check the reverse comparision. + */ + result = route_table_prefix_iter_cmp ((struct prefix *) &p2_pfx, + (struct prefix *) &p1_pfx); + + if (exp_result) + { + exp_result = -exp_result; + } + + printf ("Verifying cmp(%s, %s) returns %d\n", p1, p2, exp_result); + assert (result == exp_result); +} + +/* + * test_prefix_iter_cmp + * + * Tests comparision of prefixes according to order of iteration. + */ +static void +test_prefix_iter_cmp () +{ + printf ("\n\nTesting route_table_prefix_iter_cmp()\n"); + + verify_prefix_iter_cmp ("1.0.0.0/8", "1.0.0.0/8", 0); + + verify_prefix_iter_cmp ("1.0.0.0/8", "1.0.0.0/16", -1); + + verify_prefix_iter_cmp ("1.0.0.0/16", "1.128.0.0/16", -1); +} + +/* + * verify_iter_with_pause + * + * Iterates over a tree using two methods: 'normal' iteration, and an + * iterator that pauses at each node. Verifies that the two methods + * yield the same results. + */ +static void +verify_iter_with_pause (struct route_table *table) +{ + unsigned long num_nodes; + struct route_node *rn, *iter_rn; + route_table_iter_t iter_space; + route_table_iter_t *iter = &iter_space; + + route_table_iter_init (iter, table); + num_nodes = 0; + + for (rn = route_top (table); rn; rn = route_next (rn)) + { + num_nodes++; + route_table_iter_pause (iter); + + assert (iter->current == NULL); + if (route_table_iter_started (iter)) + { + assert (iter->state == RT_ITER_STATE_PAUSED); + } + else + { + assert (rn == table->top); + assert (iter->state == RT_ITER_STATE_INIT); + } + + iter_rn = route_table_iter_next (iter); + + /* + * Make sure both iterations return the same node. + */ + assert (rn == iter_rn); + } + + assert (num_nodes == route_table_count (table)); + + route_table_iter_pause (iter); + iter_rn = route_table_iter_next (iter); + + assert (iter_rn == NULL); + assert (iter->state == RT_ITER_STATE_DONE); + + assert (route_table_iter_next (iter) == NULL); + assert (iter->state == RT_ITER_STATE_DONE); + + route_table_iter_cleanup (iter); + + print_table (table); + printf ("Verified pausing iteration on tree with %lu nodes\n", num_nodes); +} + +/* + * test_iter_pause + */ +static void +test_iter_pause (void) +{ + struct route_table *table; + int i, num_prefixes; + const char *prefixes[] = { + "1.0.1.0/24", + "1.0.1.0/25", + "1.0.1.128/25", + "1.0.2.0/24", + "2.0.0.0/8" + }; + + num_prefixes = sizeof (prefixes) / sizeof (prefixes[0]); + + printf ("\n\nTesting that route_table_iter_pause() works as expected\n"); + table = route_table_init (); + for (i = 0; i < num_prefixes; i++) + { + add_nodes (table, prefixes[i], NULL); + } + + verify_iter_with_pause (table); + + clear_table (table); + route_table_finish (table); +} + +/* + * run_tests + */ +static void +run_tests (void) +{ + test_prefix_iter_cmp (); + test_get_next (); + test_iter_pause (); +} + +/* + * main + */ +int +main (void) +{ + run_tests (); +} diff --git a/tests/test-buffer.c b/tests/test-buffer.c index b310776f8..e95d6fb82 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 bd156baa1..b6741f305 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,42 +45,6 @@ typedef uint16_t testoff_t; /* Fletcher Checksum -- Refer to RFC1008. */ #define MODX 4102 - -/* Accumulator phase of checksum */ -static -struct acc_vals -accumulate (u_char *buffer, testsz_t len, testoff_t off) -{ - u_int8_t *p; - u_int16_t *csum; - int i, init_len, partial_len; - struct acc_vals ret; - - csum = (u_int16_t *) (buffer + off); - *(csum) = 0; - - p = buffer; - ret.c0 = 0; - ret.c1 = 0; - init_len = len; - - while (len != 0) - { - partial_len = MIN(len, MODX); - - for (i = 0; i < partial_len; i++) - { - ret.c0 = ret.c0 + *(p++); - ret.c1 += ret.c0; - } - - ret.c0 = ret.c0 % 255; - ret.c1 = ret.c1 % 255; - - len -= partial_len; - } - return ret; -} /* The final reduction phase. * This one should be the original ospfd version @@ -264,7 +249,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) @@ -397,11 +382,9 @@ verify (u_char * buffer, testsz_t len) u_int8_t *p; u_int32_t c0; u_int32_t c1; - u_int16_t checksum; int i, partial_len; p = buffer; - checksum = 0; c0 = 0; c1 = 0; @@ -427,7 +410,7 @@ verify (u_char * buffer, testsz_t len) return 1; } -int /* return checksum in low-order 16 bits */ +static int /* return checksum in low-order 16 bits */ in_cksum_optimized(void *parg, int nbytes) { u_short *ptr = parg; @@ -459,7 +442,7 @@ in_cksum_optimized(void *parg, int nbytes) } -int /* return checksum in low-order 16 bits */ +static int /* return checksum in low-order 16 bits */ in_cksum_rfc(void *parg, int count) /* from RFC 1071 */ { diff --git a/tests/test-cli.c b/tests/test-cli.c new file mode 100644 index 000000000..6fab6d52c --- /dev/null +++ b/tests/test-cli.c @@ -0,0 +1,58 @@ +/* + * CLI/command dummy handling tester + * + * Copyright (C) 2015 by David Lamparter, + * for Open Source Routing / NetDEF, Inc. + * + * 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 "common-cli.h" + +DUMMY_DEFUN(cmd0, "arg ipv4 A.B.C.D"); +DUMMY_DEFUN(cmd1, "arg ipv4m A.B.C.D/M"); +DUMMY_DEFUN(cmd2, "arg ipv6 X:X::X:X"); +DUMMY_DEFUN(cmd3, "arg ipv6m X:X::X:X/M"); +DUMMY_DEFUN(cmd4, "arg range <5-15>"); +DUMMY_DEFUN(cmd5, "pat a ( a|b)"); +DUMMY_DEFUN(cmd6, "pat b (a|)"); +DUMMY_DEFUN(cmd7, "pat c (a | b|c) A.B.C.D"); +DUMMY_DEFUN(cmd8, "pat d { foo A.B.C.D|bar X:X::X:X| baz }"); +DUMMY_DEFUN(cmd9, "pat e [ WORD ]"); +DUMMY_DEFUN(cmd10, "pat f [key]"); +DUMMY_DEFUN(cmd11, "alt a WORD"); +DUMMY_DEFUN(cmd12, "alt a A.B.C.D"); +DUMMY_DEFUN(cmd13, "alt a X:X::X:X"); + +void test_init(void) +{ + install_element (ENABLE_NODE, &cmd0_cmd); + install_element (ENABLE_NODE, &cmd1_cmd); + install_element (ENABLE_NODE, &cmd2_cmd); + install_element (ENABLE_NODE, &cmd3_cmd); + install_element (ENABLE_NODE, &cmd4_cmd); + install_element (ENABLE_NODE, &cmd5_cmd); + install_element (ENABLE_NODE, &cmd6_cmd); + install_element (ENABLE_NODE, &cmd7_cmd); + install_element (ENABLE_NODE, &cmd8_cmd); + install_element (ENABLE_NODE, &cmd9_cmd); + install_element (ENABLE_NODE, &cmd10_cmd); + install_element (ENABLE_NODE, &cmd11_cmd); + install_element (ENABLE_NODE, &cmd12_cmd); + install_element (ENABLE_NODE, &cmd13_cmd); +} diff --git a/tests/test-commands.c b/tests/test-commands.c new file mode 100644 index 000000000..18b3b50d7 --- /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 2971160b7..807249ea6 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 000000000..250379329 --- /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 a888ea0fc..beae81f69 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 000000000..1b851fc36 --- /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; + +static 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 63aab6f0b..f24da965c 100644 --- a/tests/test-sig.c +++ b/tests/test-sig.c @@ -1,20 +1,40 @@ +/* + * 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" -void +static void sighup (void) { printf ("processed hup\n"); } -void +static void sigusr1 (void) { printf ("processed usr1\n"); } -void +static void sigusr2 (void) { printf ("processed usr2\n"); @@ -43,7 +63,7 @@ int main (void) { master = thread_master_create (); - signal_init (master, Q_SIGC(sigs), sigs); + signal_init (master, array_size(sigs), sigs); zlog_default = openzlog("testsig", ZLOG_NONE, LOG_CONS|LOG_NDELAY|LOG_PID, LOG_DAEMON); diff --git a/tests/test-stream.c b/tests/test-stream.c index 785ce5882..7ef637475 100644 --- a/tests/test-stream.c +++ b/tests/test-stream.c @@ -1,8 +1,30 @@ +/* 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 -static long int ham = 0xdeadbeefdeadbeef; +static unsigned long long ham = 0xdeadbeefdeadbeef; struct thread_master *master; static void @@ -10,7 +32,7 @@ print_stream (struct stream *s) { size_t getp = stream_get_getp (s); - printf ("endp: %ld, readable: %ld, writeable: %ld\n", + printf ("endp: %zu, readable: %zu, writeable: %zu\n", stream_get_endp (s), STREAM_READABLE (s), STREAM_WRITEABLE (s)); @@ -48,7 +70,7 @@ main (void) printf ("c: 0x%hhx\n", stream_getc (s)); printf ("w: 0x%hx\n", stream_getw (s)); printf ("l: 0x%x\n", stream_getl (s)); - printf ("q: 0x%lx\n", stream_getq (s)); + printf ("q: 0x%" PRIu64 "\n", stream_getq (s)); return 0; } diff --git a/tests/test-timer-correctness.c b/tests/test-timer-correctness.c new file mode 100644 index 000000000..e523929be --- /dev/null +++ b/tests/test-timer-correctness.c @@ -0,0 +1,197 @@ +/* + * 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, "%lld.%06lld", + (long long)timers[i]->u.sands.tv_sec, + (long long)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, + "%lld.%06lld\n", + (long long)alarms[i]->tv_sec, + (long long)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 000000000..ee45ede6a --- /dev/null +++ b/tests/test-timer-performance.c @@ -0,0 +1,105 @@ +/* + * 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/testcli.in b/tests/testcli.in new file mode 100644 index 000000000..f4212b975 --- /dev/null +++ b/tests/testcli.in @@ -0,0 +1,93 @@ +echo this is a test message +echo foo bla ? baz +echo + +arg ipv4 1.2.3.4 +arg ipv4 1.2.?3.4 +arg ipv4 1.2.3 +arg ipv4 1.2.3.4.5 +arg ipv4 1.a.3.4 +arg ipv4 blah + +arg ipv4m 1.2.3.0/24 +arg ipv4m 1.2.?3.0/24 +arg ipv4m 1.2.3/9 +arg ipv4m 1.2.3.4.5/6 +arg ipv4m 1.a.3.4 +arg ipv4m blah +arg ipv4m 1.2.3.0/999 +arg ipv4m 1.2.3.0/a9 +arg ipv4m 1.2.3.0/9a + +arg ipv6 de4d:b33f::cafe +arg ipv6 de4d:b3?3f::caf?e +arg ipv6 de4d:b3 3f::caf?e +arg ipv6 de4d:b33f:z::cafe +arg ipv6 de4d:b33f:cafe: +arg ipv6 :: +arg ipv6 ::/ +arg ipv6 1:2:3:4:5:6:7:8:9:0:1:2:3:4:5:6:7:8:9:0:1:2:3:4:5:6:7:8:9:0 +arg ipv6 12::34::56 +arg ipv6m dead:beef:cafe::/64 +arg ipv6m dead:be?ef:cafe:?:/64 + +arg range 4 +arg range 5 +arg range 9? +arg range 15 +arg range 16 +arg range -1 +arg range 99999999999999999999999999999999999999999 + +arg ? + +pa +pat + +pat a +pat a a +pat a ?b +pat a c? +pat a a x + +pat b +pat b ?a +pat b x +pat b x y + +pat c a +pat c a 1.2.3.4 +pat c b 2.3.4 +pat c c ?x + +pat d +pat d +pat d foo 1.2.3.4 +pat d foo +pat d noooo +pat d bar 1::2 +pat d bar 1::2 foo 3.4.5.6 +pat d ba?z +pat d foo 3.4.5.6 baz + +pat e +pat e f +pat e f g +pat e 1.2.3.4 + +pat f +pat f foo +pat f key + +alt a a?b +alt a 1 .2?.3.4 +alt a 1 :2? ::?3 + +conf t +do pat d baz +exit + +show run +conf t +hostname foohost +do show run diff --git a/tests/testcli.refout b/tests/testcli.refout new file mode 100644 index 000000000..1515ea2c6 --- /dev/null +++ b/tests/testcli.refout @@ -0,0 +1,290 @@ +test# echo this is a test message +this is a test message +test# echo foo bla + MESSAGE The message to echo + +test# echo foo bla baz +foo bla baz +test# echo +% Command incomplete. +test# +test# arg ipv4 1.2.3.4 +cmd0 with 1 args. +[00]: 1.2.3.4 +test# arg ipv4 1.2. + A.B.C.D 02 +test# arg ipv4 1.2.3.4 +cmd0 with 1 args. +[00]: 1.2.3.4 +test# arg ipv4 1.2.3 +cmd0 with 1 args. +[00]: 1.2.3 +test# arg ipv4 1.2.3.4.5 +% [NONE] Unknown command: arg ipv4 1.2.3.4.5 +test# arg ipv4 1.a.3.4 +% [NONE] Unknown command: arg ipv4 1.a.3.4 +test# arg ipv4 blah +% [NONE] Unknown command: arg ipv4 blah +test# +test# arg ipv4m 1.2.3.0/24 +cmd1 with 1 args. +[00]: 1.2.3.0/24 +test# arg ipv4m 1.2. + A.B.C.D/M 02 +test# arg ipv4m 1.2.3.0/24 +cmd1 with 1 args. +[00]: 1.2.3.0/24 +test# arg ipv4m 1.2.3/9 +% [NONE] Unknown command: arg ipv4m 1.2.3/9 +test# arg ipv4m 1.2.3.4.5/6 +% [NONE] Unknown command: arg ipv4m 1.2.3.4.5/6 +test# arg ipv4m 1.a.3.4 +% [NONE] Unknown command: arg ipv4m 1.a.3.4 +test# arg ipv4m blah +% [NONE] Unknown command: arg ipv4m blah +test# arg ipv4m 1.2.3.0/999 +% [NONE] Unknown command: arg ipv4m 1.2.3.0/999 +test# arg ipv4m 1.2.3.0/a9 +% [NONE] Unknown command: arg ipv4m 1.2.3.0/a9 +test# arg ipv4m 1.2.3.0/9a +% [NONE] Unknown command: arg ipv4m 1.2.3.0/9a +test# +test# arg ipv6 de4d:b33f::cafe +cmd2 with 1 args. +[00]: de4d:b33f::cafe +test# arg ipv6 de4d:b3 +% There is no matched command. +test# arg ipv6 de4d:b33f::caf + X:X::X:X 02 +test# arg ipv6 de4d:b33f::cafe +cmd2 with 1 args. +[00]: de4d:b33f::cafe +test# arg ipv6 de4d:b3 +test# arg ipv6 de4d:b33f::caf + X:X::X:X 02 +test# arg ipv6 de4d:b33f::cafe +cmd2 with 1 args. +[00]: de4d:b33f::cafe +test# arg ipv6 de4d:b33f:z::cafe +% [NONE] Unknown command: arg ipv6 de4d:b33f:z::cafe +test# arg ipv6 de4d:b33f:cafe: +% [NONE] Unknown command: arg ipv6 de4d:b33f:cafe: +test# arg ipv6 :: +cmd2 with 1 args. +[00]: :: +test# arg ipv6 ::/ +% [NONE] Unknown command: arg ipv6 ::/ +test# arg ipv6 1:2:3:4:5:6:7:8:9:0:1:2:3:4:5:6:7:8:9:0:1:2:3:4:5:6:7:8:9:0 +% [NONE] Unknown command: arg ipv6 1:2:3:4:5:6:7:8:9:0:1:2:3:4:5:6:7:8:9:0:1:2:3:4:5:6:7:8:9:0 +test# arg ipv6 12::34::56 +% [NONE] Unknown command: arg ipv6 12::34::56 +test# arg ipv6m dead:beef:cafe::/64 +cmd3 with 1 args. +[00]: dead:beef:cafe::/64 +test# arg ipv6m dead:be + X:X::X:X/M 02 +test# arg ipv6m dead:beef:cafe: + X:X::X:X/M 02 +test# arg ipv6m dead:beef:cafe::/64 +cmd3 with 1 args. +[00]: dead:beef:cafe::/64 +test# +test# arg range 4 +% [NONE] Unknown command: arg range 4 +test# arg range 5 +cmd4 with 1 args. +[00]: 5 +test# arg range 9 + <5-15> 02 +test# arg range 9 +cmd4 with 1 args. +[00]: 9 +test# arg range 15 +cmd4 with 1 args. +[00]: 15 +test# arg range 16 +% [NONE] Unknown command: arg range 16 +test# arg range -1 +% [NONE] Unknown command: arg range -1 +test# arg range 99999999999999999999999999999999999999999 +% [NONE] Unknown command: arg range 99999999999999999999999999999999999999999 +test# +test# arg + ipv4 01 + ipv4m 01 + ipv6 01 + ipv6m 01 + range 01 +test# arg +% Command incomplete. +test# +test# pa +test# papat +% Command incomplete. +test# pat +a b c d e f +test# pat +% Command incomplete. +test# +test# pat a +% Command incomplete. +test# pat a a +cmd5 with 1 args. +[00]: a +test# pat a + a 02 + b 03 +test# pat a b +cmd5 with 1 args. +[00]: b +test# pat a c +% There is no matched command. +test# pat a c +% [NONE] Unknown command: pat a c +test# pat a a x +% [NONE] Unknown command: pat a a x +test# +test# pat b +% Command incomplete. +test# pat b + a 02 +test# pat b a +cmd6 with 1 args. +[00]: a +test# pat b x +% [NONE] Unknown command: pat b x +test# pat b x y +% [NONE] Unknown command: pat b x y +test# +test# pat c a +% Command incomplete. +test# pat c a 1.2.3.4 +cmd7 with 2 args. +[00]: a +[01]: 1.2.3.4 +test# pat c b 2.3.4 +cmd7 with 2 args. +[00]: b +[01]: 2.3.4 +test# pat c c + A.B.C.D 05 +test# pat c c x +% [NONE] Unknown command: pat c c x +test# +test# pat d +cmd8 with 3 args. +[00]: (null) +[01]: (null) +[02]: (null) +test# pat d +bar baz foo +test# pat d +cmd8 with 3 args. +[00]: (null) +[01]: (null) +[02]: (null) +test# pat d foo 1.2.3.4 +cmd8 with 3 args. +[00]: 1.2.3.4 +[01]: (null) +[02]: (null) +test# pat d foo +% Command incomplete. +test# pat d noooo +% [NONE] Unknown command: pat d noooo +test# pat d bar 1::2 +cmd8 with 3 args. +[00]: (null) +[01]: 1::2 +[02]: (null) +test# pat d bar 1::2 foo 3.4.5.6 +cmd8 with 3 args. +[00]: 3.4.5.6 +[01]: 1::2 +[02]: (null) +test# pat d ba + bar 04 + baz 06 +test# pat d baz +cmd8 with 3 args. +[00]: (null) +[01]: (null) +[02]: baz +test# pat d foo 3.4.5.6 baz +cmd8 with 3 args. +[00]: 3.4.5.6 +[01]: (null) +[02]: baz +test# +test# pat e +% Command incomplete. +test# pat e f +% Command incomplete. +test# pat e f g +% Command incomplete. +test# pat e 1.2.3.4 +% Command incomplete. +test# +test# pat f +cmd10 with 0 args. +test# pat f foo +cmd10 with 1 args. +[00]: foo +test# pat f key +cmd10 with 1 args. +[00]: key +test# +test# alt a +test# alt a a + WORD 02 +test# alt a ab +cmd11 with 1 args. +[00]: ab +test# alt a 1 +test# alt a 1.2 + A.B.C.D 02 + WORD 02 +test# alt a 1.2.3.4 +cmd12 with 1 args. +[00]: 1.2.3.4 +test# alt a 1 +test# alt a 1:2 + WORD 02 +test# alt a 1:2 +test# alt a 1:2:: + WORD 02 + X:X::X:X 02 +test# alt a 1:2::3 +cmd13 with 1 args. +[00]: 1:2::3 +test# +test# conf t +test(config)# do pat d baz +cmd8 with 3 args. +[00]: (null) +[01]: (null) +[02]: baz +test(config)# exit +test# +test# show run + +Current configuration: +! +hostname test +! +line vty +! +end +test# conf t +test(config)# hostname foohost +foohost(config)# do show run + +Current configuration: +! +hostname foohost +! +line vty +! +end +foohost(config)# +end. diff --git a/tests/testcommands.in b/tests/testcommands.in new file mode 100644 index 000000000..7fe62799f --- /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 000000000..11483b84b --- /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/tests/tests.h b/tests/tests.h new file mode 100644 index 000000000..a528e55f0 --- /dev/null +++ b/tests/tests.h @@ -0,0 +1,31 @@ +/* + * Test wrappers common header file + * + * Copyright (C) 2015 by David Lamparter, + * for Open Source Routing./ NetDEF, 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. + */ + +#ifndef _QUAGGA_TESTS_H +#define _QUAGGA_TESTS_H + +extern void test_init (void); +extern void test_init_cmd (void); + +#endif /* _QUAGGA_TESTS_H */ diff --git a/tools/mrlg.cgi b/tools/mrlg.cgi deleted file mode 100755 index ac468eef1..000000000 --- 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 000000000..0ebf7ee68 --- /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/tools/multiple-bgpd.sh b/tools/multiple-bgpd.sh index 20a92a916..c5668e19b 100644 --- a/tools/multiple-bgpd.sh +++ b/tools/multiple-bgpd.sh @@ -2,83 +2,457 @@ # Public domain, not copyrighted.. -NUM=5 +set -u + +# number of bgpd instances, not more than 255 at this point. At least 3 are +# needed to connect in a ring. +NUM=7 + +# The NUM peers can be connected in a ring topology. +# +# This sets the proportion of other peers that each peer should be +# configured to connect to E.g., 20 means each BGP instance will peer with +# 20% of the other peers before and after it in the ring. So 10% of the +# peers prior to this instance in the ring, and 10% of the following peers. +# 100 should lead to a full-mesh, for an odd total number of peers. +# +# A value of 1 will result in each instance having at least 2 peers in the ring. +# +# A value of 0 will disable creating a ring, in which case the only peers +# configured will be those in the EXPEERS list. +PEERPROP=100 + +# number of routes each BGP instance should advertise +ADV=10 +# First octet to use for the IPv4 advertisements. The advertisements +# will be /32s under this /8. E.g. ADVPREF=10 will mean +# 10.x.y.z/32's are advertised. +ADVPREF=10 + +# Base VTY port to allocate Quagga telnet vtys from. VTYBASE+ID will be +# the port. VTYBASE=2610 -ASBASE=64560 -BGPD=/path/to/bgpd +# Base ASN to allocate ASNs to instances. +ASBASE=64500 PREFIX=192.168.145. #PREFIX=3ffe:123:456:: ADDRPLEN=32 CONFBASE=/tmp PIDBASE=/var/run/quagga -CHOWNSTR=quagga:quagga - -for H in `seq 1 ${NUM}` ; do - CONF="${CONFBASE}"/bgpd${H}.conf - ADDR=${PREFIX}${H} - - if [ ! -e "$CONF" ] ; then - # This sets up a ring of bgpd peerings - NEXT=$(( ($H % ${NUM}) + 1 )) - PREV=$(( (($H + $NUM - 2) % ${NUM}) + 1 )) - NEXTADDR="${PREFIX}${NEXT}" - NEXTAS=$((${ASBASE} + $NEXT)) - PREVADDR="${PREFIX}${PREV}" - PREVAS=$((${ASBASE} + $PREV)) - ASN=$((64560+${H})) +USER=quagga +GROUP=quagga + +# MRAI to specify, where an implementation supports it. +MRAI=1 +# Connect retry timer +CONNECTRETRY=1 + +# The binary locations for BGP instances. +declare -A BGP_BINS=( + [quagga]=/usr/sbin/bgpd + [bird]=/usr/sbin/bird + [birdgit]=/home/paul/code/bird/bird + [quaggagit]=/home/paul/code/quagga/bgpd/bgpd + [exabgp]=/home/paul/code/exabgp/sbin/exabgp +) + +# Configuration generation functions for the BGP instances. +declare -A BGP_CONFIGGEN=( + [quagga]=quagga_config + [quaggagit]=quagga_config + [bird]=bird_config + [birdgit]=bird_config + [exabgp]=exabgp_config +) + +# Launch functions for the BGP instances. +declare -A BGP_LAUNCH=( + [quagga]=quagga_launch + [quaggagit]=quagga_launch + [bird]=bird_launch + [birdgit]=bird_launch + [quaggagit]=quagga_launch + [exabgp]=exabgp_launch +) + +# the instances to run, in the order they should appear in the ring +# (repeated over until there are $NUM instances). The value must exist as a +# key into the above two arrays. +declare -a BGP_INSTANCES=( + quagga + bird + quaggagit + exabgp +) + +# Peers to configure, that are external to this script. One list of IPs, with +# corresponding list of their ASes. +# +# e.g.: +#EXPEERS=(192.168.147.{1..10}) +#EXPEERASES=($(seq $((ASBASE+11)) $(($ASBASE+20)))) + +EXPEERS=() +EXPEERASES=() + +############################################################################ +# Can override any of the above from a supplied file with declarations +CONFWRITE=Y +if [ $# -gt 0 ] ; then + echo "multiple-bgpd.sh: sourcing config from $1" + [ -f "$1" ] && . "$1" + + # keep config, if exists + [ $# -gt 1 ] && [ "$2" = "k" ] && CONFWRITE=N +fi + +############################################################################ +# Internal variables. + +# Number of peers for each instance to peer with +PEERNUM=$(( ($NUM-1) * $PEERPROP / 100 )) +[ "$PEERNUM" -gt $(($NUM-1)) ] && PEERNUM=$(($NUM-1)) + +# the 'range', i.e. how many of the previous and next peers in the ring to +# connect to +PEERRANGE=$(( $PEERNUM/2 )) +[ "$PEERPROP" -gt 0 -a "$NUM" -ge 3 -a "$PEERRANGE" -le 0 ] && PEERRANGE=1 + +# and a convenience expansion +PEEREXP="" +if [ "$PEERRANGE" -gt 0 ]; then + PEEREXP=($(seq -${PEERRANGE} ${PEERRANGE})) + # dont need 0 + unset PEEREXP[PEERRANGE] +fi + +#echo ${PEEREXP[@]} + +############################################################################ +## helpers + +# translate instance ID to its address. +id2addr () { + local ID=$1 + echo ${PREFIX}${ID} +} + +# return the ID of a peer, in terms of an offset on the given instance's ID. +# +# E.g., given an ID of 1 and an offset of -1, if there are 10 instances overall, +# this will return 10. +peeridoff () { + local ID=$1 + local OFF=$2 + echo $(( (($ID + $OFF - 1 + $NUM) % $NUM) + 1 )) +} + +# return IPv4 address to advertise, for given instance ID and number. +advipaddr () { + local ID=$1 + local N=$2 + echo "$ADVPREF.$(( ($N >> 16) %256 )).$(( ($N >> 8) % 256 )).$(( $N % 256 ))" +} + +############################################################################ +# launch functions +# +# do not daemonise, so that all launched instances can be killed by killing +# the script. +# + +quagga_launch () { + local ID=$1 + local ASN=$2 + local ADDR=$3 + local BIN=$4 + local CONF=$5 + ${BIN} -i "${PIDBASE}"/bgpd${ID}.pid \ + -l ${ADDR} \ + -f "${CONF}" \ + -u $USER -g $GROUP \ + -P $((${VTYBASE}+${ID})) +} + +exabgp_launch () { + local ID=$1 + local ASN=$2 + local ADDR=$3 + local BIN=$4 + local CONF=$5 + + env exabgp.api.file="${PIDBASE}"/exabgp${ID}.ctl \ + exabgp.daemon.pid="${PIDBASE}"/bgpd${ID}.pid \ + exabgp.daemon.daemonize=false \ + exabgp.tcp.bind=${ADDR} \ + exabgp.log.enable=false \ + exabgp.daemon.user=quagga \ + ${BIN} ${CONF} +} + +bird_launch () { + local ID=$1 + local ASN=$2 + local ADDR=$3 + local BIN=$4 + local CONF=$5 + ${BIN} -P "${PIDBASE}"/bird${ID}.pid \ + -c "${CONF}" \ + -s "${PIDBASE}"/bird${ID}.ctl \ + -f +} + +####################################################################### +# +# functions to write the configuration for instances +# + +exabgp_config () { + local ID=$1 + local ASN=$2 + local ADDR=$3 + + local N + local P + + cat <<- EOF + group default { + local-address $ADDR; + local-as $ASN; + router-id $ADDR; + + capability { + asn4 enable; + } + EOF + + for N in $(seq 1 $ADV) ; do + echo " static {" + echo " route `advipaddr $ID $N`/32 {" + echo " next-hop $ADDR;" + echo " }" + echo " }" + done + + for P in ${PEEREXP[@]}; do + [ "$P" -eq 0 ] && continue; + + #local PID=$(( (($ID + $P - 1 + $NUM) % $NUM) + 1 )) + local PID=`peeridoff $ID $P` + #local PADDR="${PREFIX}${PID}" + local PADDR=`id2addr $PID` + local PAS=$((${ASBASE} + $PID)) - # Edit config to suit. - cat > "$CONF" <<- EOF - password whatever - service advanced-vty - ! - router bgp ${ASN} - bgp router-id ${ADDR} - network 10.${H}.1.0/24 pathlimit 1 - network 10.${H}.2.0/24 pathlimit 2 - network 10.${H}.3.0/24 pathlimit 3 - neighbor default peer-group - neighbor default update-source ${ADDR} - neighbor default capability orf prefix-list both - neighbor default soft-reconfiguration inbound - neighbor default route-map test out - neighbor ${NEXTADDR} remote-as ${NEXTAS} - neighbor ${NEXTADDR} peer-group default - neighbor ${PREVADDR} remote-as ${PREVAS} - neighbor ${PREVADDR} peer-group default - ! - address-family ipv6 - network 3ffe:${H}::/48 - network 3ffe:${H}:1::/48 pathlimit 1 - network 3ffe:${H}:2::/48 pathlimit 3 - network 3ffe:${H}:3::/48 pathlimit 3 - neighbor default activate - neighbor default capability orf prefix-list both - neighbor default default-originate - neighbor default route-map test out - neighbor ${NEXTADDR} peer-group default - neighbor ${PREVADDR} peer-group default - exit-address-family - ! - ! bgpd still has problems with extcommunity rt/soo - route-map test permit 10 - set extcommunity rt ${ASN}:1 - set extcommunity soo ${ASN}:2 - set community ${ASN}:1 - line vty - ! - end - EOF - chown ${CHOWNSTR} "$CONF" + echo " neighbor $PADDR {" + #echo " local-address $ADDR;" + #echo " local-as $ASN;" + #echo " graceful-restart;" + #echo " router-id $ADDR;" + echo " peer-as $PAS;" + echo " }" + done + + for P in ${!EXPEERS[@]}; do + echo " neighbor ${EXPEERS[$P]} {" + echo " peer-as ${EXPEERASES[$P]};" + echo " }" + done + + cat <<- EOF + } + EOF +} + +quagga_config () { + local ID=$1 + local ASN=$2 + local ADDR=$3 + + local N + local P + + # Edit config to suit. + cat <<- EOF + password foo + service advanced-vty + ! + router bgp ${ASN} + bgp router-id ${ADDR} + !maximum-paths 32 + !bgp bestpath as-path multipath-relax + EOF + + for N in $(seq 1 $ADV) ; do + echo " network `advipaddr $ID $N`/32" + done + + cat <<- EOF + neighbor default peer-group + neighbor default update-source ${ADDR} + neighbor default capability orf prefix-list both + !neighbor default soft-reconfiguration inbound + neighbor default advertisement-interval $MRAI + neighbor default timers connect $CONNECTRETRY + neighbor default route-map test out + EOF + + for P in ${PEEREXP[@]}; do + [ "$P" -eq 0 ] && continue; + + local PID=`peeridoff $ID $P` + local PADDR=`id2addr $PID` + local PAS=$((${ASBASE} + $PID)) + echo " neighbor ${PADDR} remote-as ${PAS}" + echo " neighbor ${PADDR} peer-group default" + done + + for P in ${!EXPEERS[@]}; do + echo " neighbor ${EXPEERS[$P]} remote-as ${EXPEERASES[$P]}" + echo " neighbor ${EXPEERS[$P]} peer-group default" + done + + cat <<- EOF + ! + address-family ipv6 + network 3ffe:${ID}::/48 + network 3ffe:${ID}:1::/48 pathlimit 1 + network 3ffe:${ID}:2::/48 pathlimit 3 + network 3ffe:${ID}:3::/48 pathlimit 3 + neighbor default activate + neighbor default capability orf prefix-list both + neighbor default default-originate + neighbor default route-map test out + EOF + + for P in ${PEEREXP[@]}; do + [ "$P" -eq 0 ] && continue; + + local PID=`peeridoff $ID $P` + local PADDR=`id2addr $PID` + local PAS=$((${ASBASE} + $PID)) + echo " neighbor ${PADDR} peer-group default" + done + + cat <<- EOF + exit-address-family + ! + ! bgpd still has problems with extcommunity rt/soo + route-map test permit 10 + set extcommunity rt ${ASN}:1 + set extcommunity soo ${ASN}:2 + set community ${ASN}:1 + ! + line vty + exec-timeout 0 0 + ! + end + EOF +} + +bird_config () { + local ID=$1 + local ASN=$2 + local ADDR=$3 + + cat <<- EOF + #log "/var/log/bird.log" all; + #debug protocols all; + + # Override router ID + router id ${ADDR}; + listen bgp address ${ADDR}; + + protocol kernel { device routes; import all; } + protocol device { import all; } + + function avoid_martians() + prefix set martians; + { + martians = [ + 224.0.0.0/4+, 240.0.0.0/4+ + ]; + + # Avoid RFC1918 and similar networks + if net ~ martians then return false; + return true; + } + + filter import_filter + { + if ! (avoid_martians()) then reject; + accept; + } + + filter set_comm + { + bgp_community.add ((${ASN}, 1)); + accept; + } + + template bgp peer_conf { + local as ${ASN}; + source address ${ADDR}; + import filter import_filter; + export filter set_comm; + multihop; + } + EOF + + local P; + + for P in ${PEEREXP[@]}; do + [ "$P" -eq 0 ] && continue; + + local PID=`peeridoff $ID $P` + local PADDR=`id2addr $PID` + local PAS=$((${ASBASE} + $PID)) + echo "protocol bgp from peer_conf {" + echo " neighbor ${PADDR} as ${PAS};" + echo "}" + done + + for P in ${!EXPEERS[@]}; do + echo "protocol bgp from peer_conf {" + echo " neighbor ${EXPEERS[$P]} as ${EXPEERASES[$P]};" + echo "}" + done + + + for N in $(seq 1 $ADV) ; do + echo " network `advipaddr $ID $N`/32" + done +} + +####################################################################### + +for ID in $(seq 1 $NUM); do + BGP_INST=${BGP_INSTANCES[${ID} % ${#BGP_INSTANCES[@]}]} + BGPBIN=${BGP_BINS[$BGP_INST]} + CONF="${CONFBASE}"/${BGP_INST}_bgpd${ID}.conf + ASN=$(($ASBASE + ${ID})) + ADDR=`id2addr $ID` + + #if [ ! -e "$CONF" ] ; then + if [ ! -e "$CONF" -o "$CONFWRITE" = "Y" ] ; then + ${BGP_CONFIGGEN[$BGP_INST]} $ID $ASN $ADDR > "$CONF" + chown $USER:$GROUP "$CONF" fi # You may want to automatically add configure a local address # on a loop interface. # # Solaris: ifconfig vni${H} plumb ${ADDR}/${ADDRPLEN} up - # Linux: ip address add ${ADDR}/${ADDRPLEN} dev lo 2> /dev/null - ${BGPD} -i "${PIDBASE}"/bgpd${H}.pid \ - -l ${ADDR} \ - -f "${CONF}" \ - -P $((${VTYBASE}+${H})) \ - -d + # Linux: + #ip address add ${ADDR}/${ADDRPLEN} dev lo 2> /dev/null + + ip link add dummy${ID} type dummy 2> /dev/null + ip link set dev dummy${ID} up + ip address add ${ADDR}/${ADDRPLEN} dev dummy${ID} 2> /dev/null + + ${BGP_LAUNCH[$BGP_INST]} $ID $ASN $ADDR $BGPBIN $CONF & + + sleep 0.1 done + +echo "multiple-bgpd.sh: waiting..." + +wait diff --git a/tools/rrcheck.pl b/tools/rrcheck.pl deleted file mode 100644 index 5e5a983c2..000000000 --- a/tools/rrcheck.pl +++ /dev/null @@ -1,135 +0,0 @@ -#! /bin/perl -## -## Read BGPd logfile and lookup RR's whois database. -## -## Copyright (c) 1997 Kunihiro Ishiguro -## -use Socket; - -## Configuration variables -$whois_host = "whois.jpix.ad.jp"; - -#$logfile = "/usr/local/sbin/logfile" -$logfile = shift || die "Please specify filename"; - -## mail routine -{ - local ($prefix, $origin); - - open (LOG, $logfile) || die "can't open $logfile"; - - $index = ''; - while ($index) { - $index = ; - if ($index =~ /[bgpd]/) { - break; - } - } - - while () { - if (/([\d\.\/]+)\s+([\d\.]+)\s+(\d+)\s+(\d+)\s+([\d ]+)\s+[ie\?]/) { - $prefix = $1; - $nexthop = $2; - $med = $3; - $dummy = $4; - $aspath = $5; - ($origin) = ($aspath =~ /([\d]+)$/); - - print "$nexthop [$origin] $prefix $aspath "; - - $ret = &whois_check ($prefix, $origin); - if ($ret == 0) { - print "Check OK\n"; - } elsif ($ret == 1){ - print "AS orgin mismatch\n"; - } else { - print "prefix doesn't exist \n"; - } - } - } -} - -sub whois_check -{ - local ($prefix, $origin) = @_; - local ($rr_prefix, $rr_origin) = (); - local (@result); - - $origin = "AS" . $origin; - - @result = &whois ($prefix); - - $prefix_match = 0; - foreach (@result) { - if (/^route:.*\s([\d\.\/]+)$/) { - $rr_prefix = $1; - } - if (/^origin:.*\s(AS[\d]+)$/) { - $rr_origin = $1; - - if ($prefix eq $rr_prefix and $origin eq $rr_origin) { - return 0; - } elsif ($prefix eq $rr_prefix) { - $prefix_match = 1; - } - } - } -# alarm_mail ($prefix, $origin, @result); - if ($prefix_match) { - return 1; - } else { - return 2; - } -} - -## get port of whois -sub get_whois_port -{ - local ($name, $aliases, $port, $proto) = getservbyname ("whois", "tcp"); - return ($port, $proto); -} - -## whois lookup -sub whois -{ - local ($query) = @_; - local ($port, $proto) = &get_whois_port; - local (@result); - - if ($whois_host=~ /^\s*\d+\.\d+\.\d+\.\d+\s*$/) { - $address = pack ("C4",split(/\./,$host)); - } else { - $address = (gethostbyname ($whois_host))[4]; - } - - socket (SOCKET, PF_INET, SOCK_STREAM, $proto); - - if (connect (SOCKET, sockaddr_in ($port, $address))) { - local ($oldhandle) = select (SOCKET); - $| = 1; - select($oldhandle); - - print SOCKET "$query\r\n"; - - @result = ; - return @result; - } -} - -## -sub alarm_mail -{ - local ($prefix, $origin, @result) = @_; - - open (MAIL, "|$mailer -t $mail_address") || die "can't open $mailer"; - - print MAIL "From: root\@rr1.jpix.ad.jp\n"; - print MAIL "Subject: RR $origin $prefix\n"; - print MAIL "MIME-Version: 1.0\n"; - print MAIL "Content-Type: text/plain; charset=us-ascii \n\n"; - print MAIL "RR Lookup Error Report\n"; - print MAIL "======================\n"; - print MAIL "Announced route : $prefix from $origin\n\n"; - print MAIL "@result"; - close MAIL; -} diff --git a/tools/rrlookup.pl b/tools/rrlookup.pl deleted file mode 100644 index 2c14e73ea..000000000 --- a/tools/rrlookup.pl +++ /dev/null @@ -1,123 +0,0 @@ -#! /usr/local/bin/perl -## -## Read BGPd logfile and lookup RR's whois database. -## -## Copyright (c) 1997 Kunihiro Ishiguro -## -use Socket; - -## Configuration variables -$whois_host = "whois.jpix.ad.jp"; - -#$mail_address = "toshio\@iri.co.jp"; -$mail_address = "kunihiro\@zebra.org"; -$mailer = "/usr/sbin/sendmail -oi"; - -#$logfile = "/usr/local/sbin/logfile" -$logfile = "logfile"; -$lookuplog = "lookuplog"; - -## mail routine -{ - local ($prefix, $origin); - - open (LOG, $logfile) || die "can't open $logfile"; - open (LOOKUP, ">$lookuplog") || die "can't open $lookuplog"; - - for (;;) { - while () { - if (/Update\S+ ([\d\.\/]+) .* (\d+) [ie\?]/) { - $prefix = $1; - $origin = $2; - $ret = &whois_check ($prefix, $origin); - if ($ret) { - print LOOKUP "$prefix AS$origin : Check OK\n"; - } else { - print LOOKUP "$prefix AS$origin : Error\n"; - } -# fflush (LOOKUP); - } - } - sleep (3); - } -} - -sub whois_check -{ - local ($prefix, $origin) = @_; - local ($rr_prefix, $rr_origin) = (); - local (@result); - - $origin = "AS" . $origin; - -# print "$prefix $origin\n"; - - @result = &whois ($prefix); - - foreach (@result) { - if (/^route:.*\s([\d\.\/]+)$/) { - $rr_prefix = $1; - } - if (/^origin:.*\s(AS[\d]+)$/) { - $rr_origin = $1; - - if ($prefix eq $rr_prefix and $origin eq $rr_origin) { - return 1; - } - } - } - alarm_mail ($prefix, $origin, @result); - return 0; -} - -## get port of whois -sub get_whois_port -{ - local ($name, $aliases, $port, $proto) = getservbyname ("whois", "tcp"); - return ($port, $proto); -} - -## whois lookup -sub whois -{ - local ($query) = @_; - local ($port, $proto) = &get_whois_port; - local (@result); - - if ($whois_host=~ /^\s*\d+\.\d+\.\d+\.\d+\s*$/) { - $address = pack ("C4",split(/\./,$host)); - } else { - $address = (gethostbyname ($whois_host))[4]; - } - - socket (SOCKET, PF_INET, SOCK_STREAM, $proto); - - if (connect (SOCKET, sockaddr_in ($port, $address))) { - local ($oldhandle) = select (SOCKET); - $| = 1; - select($oldhandle); - - print SOCKET "$query\r\n"; - - @result = ; - return @result; - } -} - -## -sub alarm_mail -{ - local ($prefix, $origin, @result) = @_; - - open (MAIL, "|$mailer -t $mail_address") || die "can't open $mailer"; - - print MAIL "From: root\@rr1.jpix.ad.jp\n"; - print MAIL "Subject: RR $origin $prefix\n"; - print MAIL "MIME-Version: 1.0\n"; - print MAIL "Content-Type: text/plain; charset=us-ascii \n\n"; - print MAIL "RR Lookup Error Report\n"; - print MAIL "======================\n"; - print MAIL "Announced route : $prefix from $origin\n\n"; - print MAIL "@result"; - close MAIL; -} diff --git a/tools/zc.pl b/tools/zc.pl deleted file mode 100755 index 026e8fe5b..000000000 --- a/tools/zc.pl +++ /dev/null @@ -1,111 +0,0 @@ -#! /usr/bin/perl -## -## Zebra interactive console -## Copyright (C) 2000 Vladimir B. Grebenschikov -## -## 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. - -use Net::Telnet (); -use Getopt::Std; - -#use strict; - -my $host = `hostname -s`; $host =~ s/\s//g; -my $port = 'zebra'; -my $server = 'localhost'; - -# Check arguments -&getopts ('l:e:czborh'); - -&usage () if $opt_h; - -# main -{ - my $login_pass = $opt_l || $ENV{ZEBRA_PASSWORD} || 'zebra'; - my $enable_pass = $opt_e || $ENV{ZEBRA_ENABLE} || ''; - - my $port = ($opt_z ? 'zebra' : 0) || - ($opt_b ? 'bgpd' : 0) || - ($opt_o ? 'ospfd' : 0) || - ($opt_r ? 'ripd' : 0) || 'zebra'; - - my $cmd = join (' ', @ARGV); - - my $t = new Net::Telnet (Timeout => 10, - Prompt => '/[\>\#] $/', - Port => $port); - - $t->open ($server); - - $t->cmd ($login_pass); - if ($enable_pass) { - $t->cmd (String => 'en', - Prompt => '/Password: /'); - $t->cmd ($enable_pass); - } - $t->cmd ('conf t') if "$opt_c"; - - if ($cmd) - { - docmd ($t, $cmd); - exit (0); - } - - my $prompt = sprintf ("%s%s# ", $host, - ($port eq 'zebra') ? '' : "/$port"); - - print "\nZEBRA interactive console ($port)\n\n" if -t STDIN; - - while (1) - { - $| = 1; - print $prompt if -t STDIN; - chomp ($cmd = <>); - if (!defined ($cmd)) - { - print "\n" if -t STDIN; - exit(0); - } - exit (0) if ($cmd eq 'q' || $cmd eq 'quit'); - - docmd ($t, $cmd) if $cmd !~ /^\s*$/; - } - - exit(0); -} - -sub docmd -{ - my ($t, $cmd) = @_; - my @lines = $t->cmd ($cmd); - print join ('', grep (!/[\>\#] $/, @lines)), "\n"; -} - -sub usage -{ - print "USAGE: $0 [-l LOGIN_PASSWORD] [-e ENABLE_PASSWORD] [-z|-b|-o|-r|-h] []\n", - "\t-l - specify login password\n", - "\t-e - specify enable password\n", - "\t-c - execute command in configure mode\n", - "\t-z - connect to zebra daemon\n", - "\t-b - connect to bgpd daemon\n", - "\t-o - connect to ospfd daemon\n", - "\t-r - connect to ripd daemon\n", - "\t-h - help\n"; - exit (1); -} diff --git a/vtysh/Makefile.am b/vtysh/Makefile.am index 0fd2f1487..983103fff 100644 --- a/vtysh/Makefile.am +++ b/vtysh/Makefile.am @@ -1,12 +1,11 @@ ## Process this file with Automake to create Makefile.in -INCLUDES = @INCLUDES@ -I$(top_srcdir) -I$(top_srcdir)/lib +AM_CPPFLAGS = -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib DEFS = @DEFS@ -DSYSCONFDIR=\"$(sysconfdir)/\" LIBS = @LIBS@ @CURSES@ @LIBPAM@ -AM_CFLAGS = $(PICFLAGS) -AM_LDFLAGS = $(PILDFLAGS) +AM_CFLAGS = $(WERROR) bin_PROGRAMS = vtysh @@ -24,17 +23,19 @@ EXTRA_DIST = extract.pl 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)/nhrpd/nhrp_vty.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 \ + $(top_srcdir)/lib/vrf.c \ $(top_srcdir)/lib/vty.c $(top_srcdir)/zebra/debug.c \ $(top_srcdir)/zebra/interface.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 - -vtysh_cmd.c: $(vtysh_cmd_FILES) - ./$(EXTRA_DIST) $(vtysh_cmd_FILES) > vtysh_cmd.c + $(top_srcdir)/zebra/zebra_routemap.c \ + $(top_srcdir)/zebra/zebra_fpm.c +vtysh_cmd.c: $(vtysh_cmd_FILES) extract.pl + ./extract.pl $(vtysh_cmd_FILES) > vtysh_cmd.c diff --git a/vtysh/extract.pl.in b/vtysh/extract.pl.in index 61b2d2a21..6e24b7756 100755 --- a/vtysh/extract.pl.in +++ b/vtysh/extract.pl.in @@ -31,13 +31,14 @@ print <"'} = "ignore"; +$ignore{'"link-params"'} = "ignore"; $ignore{'"ip vrf NAME"'} = "ignore"; $ignore{'"router rip"'} = "ignore"; $ignore{'"router ripng"'} = "ignore"; $ignore{'"router ospf"'} = "ignore"; $ignore{'"router ospf <0-65535>"'} = "ignore"; $ignore{'"router ospf6"'} = "ignore"; -$ignore{'"router babel"'} = "ignore"; $ignore{'"router bgp " "<1-4294967295>"'} = "ignore"; $ignore{'"router bgp " "<1-4294967295>" " view WORD"'} = "ignore"; $ignore{'"router isis WORD"'} = "ignore"; @@ -45,11 +46,20 @@ $ignore{'"router zebra"'} = "ignore"; $ignore{'"address-family ipv4"'} = "ignore"; $ignore{'"address-family ipv4 (unicast|multicast)"'} = "ignore"; $ignore{'"address-family ipv6"'} = "ignore"; -$ignore{'"address-family ipv6 unicast"'} = "ignore"; +$ignore{'"address-family ipv6 (unicast|multicast)"'} = "ignore"; $ignore{'"address-family vpnv4"'} = "ignore"; $ignore{'"address-family vpnv4 unicast"'} = "ignore"; +$ignore{'"address-family vpnv6"'} = "ignore"; +$ignore{'"address-family vpnv6 unicast"'} = "ignore"; $ignore{'"address-family ipv4 vrf NAME"'} = "ignore"; +$ignore{'"address-family encap"'} = "ignore"; +$ignore{'"address-family encapv4"'} = "ignore"; +$ignore{'"address-family encapv6"'} = "ignore"; $ignore{'"exit-address-family"'} = "ignore"; +$ignore{'"exit-link-params"'} = "ignore"; +$ignore{'"vnc defaults"'} = "ignore"; +$ignore{'"vnc nve-group NAME"'} = "ignore"; +$ignore{'"exit-vnc"'} = "ignore"; $ignore{'"key chain WORD"'} = "ignore"; $ignore{'"key <0-2147483647>"'} = "ignore"; $ignore{'"route-map WORD (deny|permit) <1-65535>"'} = "ignore"; @@ -60,10 +70,12 @@ $ignore{'"terminal monitor"'} = "ignore"; $ignore{'"terminal no monitor"'} = "ignore"; $ignore{'"show history"'} = "ignore"; +my $cli_stomp = 0; + foreach (@ARGV) { $file = $_; - open (FH, "cpp -DHAVE_CONFIG_H -DVTYSH_EXTRACT_PL -DHAVE_IPV6 -I@top_builddir@ -I@srcdir@/ -I@srcdir@/.. -I@top_srcdir@/lib -I@top_srcdir@/isisd/topology @SNMP_INCLUDES@ @CPPFLAGS@ $file |"); + open (FH, "@CPP@ -DHAVE_CONFIG_H -DVTYSH_EXTRACT_PL -DHAVE_IPV6 -I@top_builddir@ -I@srcdir@/ -I@srcdir@/.. -I@top_srcdir@/lib -I@top_builddir@/lib -I@top_srcdir@/isisd/topology @CPPFLAGS@ $file |"); local $/; undef $/; $line = ; close (FH); @@ -90,41 +102,43 @@ 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\/vrf\.c$/) { + $protocol = "VTYSH_ZEBRA"; + } + 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; } @@ -134,6 +148,12 @@ foreach (@ARGV) { $defun_body = join (", ", @defun_array); # $cmd -> $str hash for lookup + if (exists($cmd2str{$cmd})) { + warn "Duplicate CLI Function: $cmd\n"; + warn "\tFrom cli: $cmd2str{$cmd} to New cli: $str\n"; + warn "\tOriginal Protocol: $cmd2proto{$cmd} to New Protocol: $protocol\n"; + $cli_stomp++; + } $cmd2str{$cmd} = $str; $cmd2defun{$cmd} = $defun_body; $cmd2proto{$cmd} = $protocol; @@ -167,6 +187,27 @@ foreach (@ARGV) { } } +my $bad_cli_stomps = 102; +# Currently we have $bad_cli_stomps. This was determined by +# running this script and counting up the collisions from what +# was returned. +# +# When we have cli commands that map to the same function name, we +# can introduce subtle bugs due to code not being called when +# we think it is. +# +# If extract.pl fails with a error message and you've been +# modifying the cli, then go back and fix your code to +# not have cli command function collisions. +# +# If you've removed a cli overwrite, you can safely subtract +# one from $bad_cli_stomps. If you've added to the problem +# please fix your code before submittal +if ($cli_stomp != $bad_cli_stomps) { + warn "Expected $bad_cli_stomps command line stomps, but got $cli_stomp instead\n"; + exit $cli_stomp; +} + # Check finaly alive $cmd; foreach (keys %odefun) { my ($node, $str) = (split (/,/)); @@ -186,7 +227,7 @@ foreach (keys %live) { # Output install_element print < lenstr) + return 0; + return strncmp(str, prefix, lenprefix) == 0; +} + /* Following filled with debug code to trace a problematic condition * under load - it SHOULD handle it. */ -#define ERR_WHERE_STRING "vtysh(): vtysh_client_config(): " +#define ERR_WHERE_STRING "vtysh(): vtysh_client_execute(): " static int -vtysh_client_config (struct vtysh_client *vclient, char *line) +vtysh_client_execute (struct vtysh_client *vclient, const char *line, FILE *fp) { int ret; char *buf; @@ -100,6 +114,7 @@ vtysh_client_config (struct vtysh_client *vclient, char *line) int nbytes; int i; int readln; + int numnulls = 0; if (vclient->fd < 0) return CMD_SUCCESS; @@ -123,6 +138,7 @@ vtysh_client_config (struct vtysh_client *vclient, char *line) { fprintf (stderr, ERR_WHERE_STRING \ "warning - pbuf beyond buffer end.\n"); + XFREE(MTYPE_TMP, buf); return CMD_WARNING; } @@ -145,119 +161,87 @@ vtysh_client_config (struct vtysh_client *vclient, char *line) XFREE(MTYPE_TMP, buf); return CMD_SUCCESS; } + /* If we have already seen 3 nulls, then current byte is ret code */ + if ((numnulls == 3) && (nbytes == 1)) + { + ret = pbuf[0]; + break; + } pbuf[nbytes] = '\0'; - if (nbytes >= 4) - { - i = nbytes - 4; - if (pbuf[i] == '\0' && pbuf[i + 1] == '\0' && pbuf[i + 2] == '\0') - { - ret = pbuf[i + 3]; - break; - } - } - pbuf += nbytes; - - /* See if a line exists in buffer, if so parse and consume it, and - * reset read position. */ - if ((eoln = strrchr(buf, '\n')) == NULL) - continue; - - if (eoln >= ((buf + bufsz) - 1)) - { - fprintf (stderr, ERR_WHERE_STRING \ - "warning - eoln beyond buffer end.\n"); - } - vtysh_config_parse(buf); - - eoln++; - left = (size_t)(buf + bufsz - eoln); - memmove(buf, eoln, left); - buf[bufsz-1] = '\0'; - pbuf = buf + strlen(buf); + /* If the config needs to be written in file or stdout */ + if (fp) + { + fputs(pbuf, fp); + fflush (fp); + } + + /* At max look last four bytes */ + if (nbytes >= 4) + { + i = nbytes - 4; + numnulls = 0; + } + else + i = 0; + + /* Count the numnulls */ + while (i < nbytes && numnulls <3) + { + if (pbuf[i++] == '\0') + numnulls++; + else + numnulls = 0; + } + /* We might have seen 3 consecutive nulls so store the ret code before updating pbuf*/ + ret = pbuf[nbytes-1]; + pbuf += nbytes; + + /* See if a line exists in buffer, if so parse and consume it, and + * reset read position. If 3 nulls has been encountered consume the buffer before + * next read. + */ + if (((eoln = strrchr(buf, '\n')) == NULL) && (numnulls<3)) + continue; + + if (eoln >= ((buf + bufsz) - 1)) + { + fprintf (stderr, ERR_WHERE_STRING \ + "warning - eoln beyond buffer end.\n"); + } + + /* If the config needs parsing, consume it */ + if(!fp) + vtysh_config_parse(buf); + + eoln++; + left = (size_t)(buf + bufsz - eoln); + /* + * This check is required since when a config line split between two consecutive reads, + * then buf will have first half of config line and current read will bring rest of the + * line. So in this case eoln will be 1 here, hence calculation of left will be wrong. + * In this case we don't need to do memmove, because we have already seen 3 nulls. + */ + if(left < bufsz) + memmove(buf, eoln, left); + + buf[bufsz-1] = '\0'; + pbuf = buf + strlen(buf); + /* got 3 or more trailing NULs? */ + if ((numnulls >=3) && (i < nbytes)) + { + break; + } } - /* Parse anything left in the buffer. */ - - vtysh_config_parse (buf); + if(!fp) + vtysh_config_parse (buf); XFREE(MTYPE_TMP, buf); return ret; } - -static int -vtysh_client_execute (struct vtysh_client *vclient, const char *line, FILE *fp) -{ - int ret; - char buf[1001]; - int nbytes; - int i; - int numnulls = 0; - - if (vclient->fd < 0) - return CMD_SUCCESS; - - ret = write (vclient->fd, line, strlen (line) + 1); - if (ret <= 0) - { - vclient_close (vclient); - return CMD_SUCCESS; - } - - while (1) - { - nbytes = read (vclient->fd, buf, sizeof(buf)-1); - - if (nbytes <= 0 && errno != EINTR) - { - vclient_close (vclient); - return CMD_SUCCESS; - } - - if (nbytes > 0) - { - if ((numnulls == 3) && (nbytes == 1)) - return buf[0]; - - buf[nbytes] = '\0'; - fputs (buf, fp); - fflush (fp); - - /* check for trailling \0\0\0, - * even if split across reads - * (see lib/vty.c::vtysh_read) - */ - if (nbytes >= 4) - { - i = nbytes-4; - numnulls = 0; - } - else - i = 0; - - while (i < nbytes && numnulls < 3) - { - if (buf[i++] == '\0') - numnulls++; - else - numnulls = 0; - } - - /* got 3 or more trailing NULs? */ - if ((numnulls >= 3) && (i < nbytes)) - return (buf[nbytes-1]); - } - } -} - -void -vtysh_exit_ripd_only (void) -{ - if (ripd_client) - vtysh_client_execute (ripd_client, "exit", stdout); -} - + void vtysh_pager_init (void) @@ -311,7 +295,9 @@ vtysh_execute_func (const char *line, int pager) * to move into node in the vtysh where it succeeded. */ if (ret == CMD_SUCCESS || ret == CMD_SUCCESS_DAEMON || ret == CMD_WARNING) { - if ((saved_node == BGP_VPNV4_NODE || saved_node == BGP_IPV4_NODE + if ((saved_node == BGP_VPNV4_NODE || saved_node == BGP_VPNV6_NODE + || saved_node == BGP_ENCAP_NODE || saved_node == BGP_ENCAPV6_NODE + || saved_node == BGP_IPV4_NODE || saved_node == BGP_IPV6_NODE || saved_node == BGP_IPV4M_NODE || saved_node == BGP_IPV6M_NODE) && (tried == 1)) @@ -374,7 +360,7 @@ vtysh_execute_func (const char *line, int pager) if (! strcmp(cmd->string,"configure terminal")) { - for (i = 0; i < VTYSH_INDEX_MAX; i++) + for (i = 0; i < array_size(vtysh_client); i++) { cmd_stat = vtysh_client_execute(&vtysh_client[i], line, fp); if (cmd_stat == CMD_WARNING) @@ -413,7 +399,7 @@ vtysh_execute_func (const char *line, int pager) } cmd_stat = CMD_SUCCESS; - for (i = 0; i < VTYSH_INDEX_MAX; i++) + for (i = 0; i < array_size(vtysh_client); i++) { if (cmd->daemon & vtysh_client[i].flag) { @@ -457,53 +443,11 @@ int vtysh_config_from_file (struct vty *vty, FILE *fp) { int ret; - vector vline; struct cmd_element *cmd; - while (fgets (vty->buf, VTY_BUFSIZ, fp)) + while (fgets (vty->buf, vty->max, fp)) { - if (vty->buf[0] == '!' || vty->buf[1] == '#') - continue; - - vline = cmd_make_strvec (vty->buf); - - /* In case of comment line. */ - if (vline == NULL) - continue; - - /* Execute configuration command : this is strict match. */ - ret = cmd_execute_command_strict (vline, vty, &cmd); - - /* Try again with setting node to CONFIG_NODE. */ - if (ret != CMD_SUCCESS - && ret != CMD_SUCCESS_DAEMON - && ret != CMD_WARNING) - { - if (vty->node == KEYCHAIN_KEY_NODE) - { - vty->node = KEYCHAIN_NODE; - vtysh_exit_ripd_only (); - ret = cmd_execute_command_strict (vline, vty, &cmd); - - if (ret != CMD_SUCCESS - && ret != CMD_SUCCESS_DAEMON - && ret != CMD_WARNING) - { - vtysh_exit_ripd_only (); - vty->node = CONFIG_NODE; - ret = cmd_execute_command_strict (vline, vty, &cmd); - } - } - else - { - vtysh_execute ("end"); - vtysh_execute ("configure terminal"); - vty->node = CONFIG_NODE; - ret = cmd_execute_command_strict (vline, vty, &cmd); - } - } - - cmd_free_strvec (vline); + ret = command_config_read_one_line (vty, &cmd, 1); switch (ret) { @@ -525,7 +469,7 @@ vtysh_config_from_file (struct vty *vty, FILE *fp) u_int i; int cmd_stat = CMD_SUCCESS; - for (i = 0; i < VTYSH_INDEX_MAX; i++) + for (i = 0; i < array_size(vtysh_client); i++) { if (cmd->daemon & vtysh_client[i].flag) { @@ -547,7 +491,7 @@ vtysh_config_from_file (struct vty *vty, FILE *fp) } /* We don't care about the point of the cursor when '?' is typed. */ -int +static int vtysh_rl_describe (void) { int ret; @@ -555,7 +499,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); @@ -563,11 +507,11 @@ vtysh_rl_describe (void) if (vline == NULL) { vline = vector_init (1); - vector_set (vline, '\0'); + vector_set (vline, NULL); } else if (rl_end && isspace ((int) rl_line_buffer[rl_end - 1])) - vector_set (vline, '\0'); + vector_set (vline, NULL); describe = cmd_describe_command (vline, vty, &ret); @@ -593,15 +537,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) @@ -609,19 +553,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); @@ -657,7 +601,7 @@ command_generator (const char *text, int state) return NULL; if (rl_end && isspace ((int) rl_line_buffer[rl_end - 1])) - vector_set (vline, '\0'); + vector_set (vline, NULL); matched = cmd_complete_command (vline, vty, &complete_status); } @@ -678,8 +622,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; @@ -756,6 +701,24 @@ static struct cmd_node bgp_vpnv4_node = "%s(config-router-af)# " }; +static struct cmd_node bgp_vpnv6_node = +{ + BGP_VPNV6_NODE, + "%s(config-router-af)# " +}; + +static struct cmd_node bgp_encap_node = +{ + BGP_ENCAP_NODE, + "%s(config-router-af)# " +}; + +static struct cmd_node bgp_encapv6_node = +{ + BGP_ENCAPV6_NODE, + "%s(config-router-af)# " +}; + static struct cmd_node bgp_ipv4_node = { BGP_IPV4_NODE, @@ -816,11 +779,17 @@ static struct cmd_node keychain_key_node = "%s(config-keychain-key)# " }; +struct cmd_node link_params_node = +{ + LINK_PARAMS_NODE, + "%s(config-link-params)# ", +}; + /* Defined in lib/vty.c */ extern struct cmd_node vty_node; /* When '^Z' is received from vty, move down to the enable mode. */ -int +static int vtysh_end (void) { switch (vty->node) @@ -890,6 +859,62 @@ DEFUNSH (VTYSH_BGPD, return CMD_SUCCESS; } +DEFUNSH (VTYSH_BGPD, + address_family_vpnv6, + address_family_vpnv6_cmd, + "address-family vpnv6", + "Enter Address Family command mode\n" + "Address family\n") +{ + vty->node = BGP_VPNV6_NODE; + return CMD_SUCCESS; +} + +DEFUNSH (VTYSH_BGPD, + address_family_vpnv6_unicast, + address_family_vpnv6_unicast_cmd, + "address-family vpnv6 unicast", + "Enter Address Family command mode\n" + "Address family\n" + "Address Family Modifier\n") +{ + vty->node = BGP_VPNV6_NODE; + return CMD_SUCCESS; +} + +DEFUNSH (VTYSH_BGPD, + address_family_encap, + address_family_encap_cmd, + "address-family encap", + "Enter Address Family command mode\n" + "Address family\n") +{ + vty->node = BGP_ENCAP_NODE; + return CMD_SUCCESS; +} + +DEFUNSH (VTYSH_BGPD, + address_family_encapv4, + address_family_encapv4_cmd, + "address-family encapv4", + "Enter Address Family command mode\n" + "Address family\n") +{ + vty->node = BGP_ENCAP_NODE; + return CMD_SUCCESS; +} + +DEFUNSH (VTYSH_BGPD, + address_family_encapv6, + address_family_encapv6_cmd, + "address-family encapv6", + "Enter Address Family command mode\n" + "Address family\n") +{ + vty->node = BGP_ENCAPV6_NODE; + return CMD_SUCCESS; +} + DEFUNSH (VTYSH_BGPD, address_family_ipv4_unicast, address_family_ipv4_unicast_cmd, @@ -1016,17 +1041,6 @@ DEFUNSH (VTYSH_OSPF6D, return CMD_SUCCESS; } -DEFUNSH (VTYSH_BABELD, - router_babel, - router_babel_cmd, - "router babel", - ROUTER_STR - "Babel") -{ - vty->node = BABEL_NODE; - return CMD_SUCCESS; -} - DEFUNSH (VTYSH_ISISD, router_isis, router_isis_cmd, @@ -1126,6 +1140,9 @@ vtysh_exit (struct vty *vty) vty->node = CONFIG_NODE; break; case BGP_VPNV4_NODE: + case BGP_VPNV6_NODE: + case BGP_ENCAP_NODE: + case BGP_ENCAPV6_NODE: case BGP_IPV4_NODE: case BGP_IPV4M_NODE: case BGP_IPV6_NODE: @@ -1135,6 +1152,9 @@ vtysh_exit (struct vty *vty) case KEYCHAIN_KEY_NODE: vty->node = KEYCHAIN_NODE; break; + case LINK_PARAMS_NODE: + vty->node = INTERFACE_NODE; + break; default: break; } @@ -1164,6 +1184,9 @@ DEFUNSH (VTYSH_BGPD, if (vty->node == BGP_IPV4_NODE || vty->node == BGP_IPV4M_NODE || vty->node == BGP_VPNV4_NODE + || vty->node == BGP_VPNV6_NODE + || vty->node == BGP_ENCAP_NODE + || vty->node == BGP_ENCAPV6_NODE || vty->node == BGP_IPV6_NODE || vty->node == BGP_IPV6M_NODE) vty->node = BGP_NODE; @@ -1307,14 +1330,29 @@ DEFUNSH (VTYSH_INTERFACE, return CMD_SUCCESS; } -/* TODO Implement "no interface command in isisd. */ -DEFSH (VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D, +ALIAS_SH (VTYSH_ZEBRA, + vtysh_interface, + vtysh_interface_vrf_cmd, + "interface IFNAME " VRF_CMD_STR, + "Select an interface to configure\n" + "Interface's name\n" + VRF_CMD_HELP_STR) + +DEFSH (VTYSH_INTERFACE, vtysh_no_interface_cmd, "no interface IFNAME", NO_STR "Delete a pseudo interface's configuration\n" "Interface's name\n") +DEFSH (VTYSH_ZEBRA, + vtysh_no_interface_vrf_cmd, + "no interface IFNAME " VRF_CMD_STR, + NO_STR + "Delete a pseudo interface's configuration\n" + "Interface's name\n" + VRF_CMD_HELP_STR) + /* TODO Implement interface description commands in ripngd, ospf6d * and isisd. */ DEFSH (VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_OSPFD, @@ -1329,6 +1367,158 @@ DEFSH (VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_OSPFD, NO_STR "Interface specific description\n") +DEFSH (VTYSH_RIPD|VTYSH_RIPNGD, + distribute_list_all_cmd, + "distribute-list WORD (in|out)", + "Filter networks in routing updates\n" + "Access-list name\n" + "Filter incoming routing updates\n" + "Filter outgoing routing updates\n") + +DEFSH (VTYSH_RIPD|VTYSH_RIPNGD, + no_distribute_list_all_cmd, + "no distribute-list WORD (in|out)", + NO_STR + "Filter networks in routing updates\n" + "Access-list name\n" + "Filter incoming routing updates\n" + "Filter outgoing routing updates\n") + +DEFSH (VTYSH_RIPD|VTYSH_RIPNGD, + distribute_list_cmd, + "distribute-list WORD (in|out) WORD", + "Filter networks in routing updates\n" + "Access-list name\n" + "Filter incoming routing updates\n" + "Filter outgoing routing updates\n" + "Interface name\n") + +DEFSH (VTYSH_RIPD|VTYSH_RIPNGD, + no_distribute_list_cmd, + "no distribute-list WORD (in|out) WORD", + NO_STR + "Filter networks in routing updates\n" + "Access-list name\n" + "Filter incoming routing updates\n" + "Filter outgoing routing updates\n" + "Interface name\n") + +DEFSH (VTYSH_RIPD|VTYSH_RIPNGD, + distribute_list_prefix_all_cmd, + "distribute-list prefix WORD (in|out)", + "Filter networks in routing updates\n" + "Filter prefixes in routing updates\n" + "Name of an IP prefix-list\n" + "Filter incoming routing updates\n" + "Filter outgoing routing updates\n") + +DEFSH (VTYSH_RIPD|VTYSH_RIPNGD, + no_distribute_list_prefix_all_cmd, + "no distribute-list prefix WORD (in|out)", + NO_STR + "Filter networks in routing updates\n" + "Filter prefixes in routing updates\n" + "Name of an IP prefix-list\n" + "Filter incoming routing updates\n" + "Filter outgoing routing updates\n") + +DEFSH (VTYSH_RIPD|VTYSH_RIPNGD, + distribute_list_prefix_cmd, + "distribute-list prefix WORD (in|out) WORD", + "Filter networks in routing updates\n" + "Filter prefixes in routing updates\n" + "Name of an IP prefix-list\n" + "Filter incoming routing updates\n" + "Filter outgoing routing updates\n" + "Interface name\n") + +DEFSH (VTYSH_RIPD|VTYSH_RIPNGD, + no_distribute_list_prefix_cmd, + "no distribute-list prefix WORD (in|out) WORD", + NO_STR + "Filter networks in routing updates\n" + "Filter prefixes in routing updates\n" + "Name of an IP prefix-list\n" + "Filter incoming routing updates\n" + "Filter outgoing routing updates\n" + "Interface name\n") + +DEFSH (VTYSH_RIPNGD, + ipv6_distribute_list_all_cmd, + "ipv6 distribute-list WORD (in|out)", + "Filter networks in routing updates\n" + "Access-list name\n" + "Filter incoming routing updates\n" + "Filter outgoing routing updates\n") + +DEFSH (VTYSH_RIPNGD, + no_ipv6_distribute_list_all_cmd, + "no ipv6 distribute-list WORD (in|out)", + NO_STR + "Filter networks in routing updates\n" + "Access-list name\n" + "Filter incoming routing updates\n" + "Filter outgoing routing updates\n") + +DEFSH (VTYSH_RIPNGD, + ipv6_distribute_list_cmd, + "ipv6 distribute-list WORD (in|out) WORD", + "Filter networks in routing updates\n" + "Access-list name\n" + "Filter incoming routing updates\n" + "Filter outgoing routing updates\n" + "Interface name\n") + +DEFSH (VTYSH_RIPNGD, + no_ipv6_distribute_list_cmd, + "no ipv6 distribute-list WORD (in|out) WORD", + NO_STR + "Filter networks in routing updates\n" + "Access-list name\n" + "Filter incoming routing updates\n" + "Filter outgoing routing updates\n" + "Interface name\n") + +DEFSH (VTYSH_RIPNGD, + ipv6_distribute_list_prefix_all_cmd, + "ipv6 distribute-list prefix WORD (in|out)", + "Filter networks in routing updates\n" + "Filter prefixes in routing updates\n" + "Name of an IP prefix-list\n" + "Filter incoming routing updates\n" + "Filter outgoing routing updates\n") + +DEFSH (VTYSH_RIPNGD, + no_ipv6_distribute_list_prefix_all_cmd, + "no ipv6 distribute-list prefix WORD (in|out)", + NO_STR + "Filter networks in routing updates\n" + "Filter prefixes in routing updates\n" + "Name of an IP prefix-list\n" + "Filter incoming routing updates\n" + "Filter outgoing routing updates\n") + +DEFSH (VTYSH_RIPNGD, + ipv6_distribute_list_prefix_cmd, + "ipv6 distribute-list prefix WORD (in|out) WORD", + "Filter networks in routing updates\n" + "Filter prefixes in routing updates\n" + "Name of an IP prefix-list\n" + "Filter incoming routing updates\n" + "Filter outgoing routing updates\n" + "Interface name\n") + +DEFSH (VTYSH_RIPNGD, + no_ipv6_distribute_list_prefix_cmd, + "no ipv6 distribute-list prefix WORD (in|out) WORD", + NO_STR + "Filter networks in routing updates\n" + "Filter prefixes in routing updates\n" + "Name of an IP prefix-list\n" + "Filter incoming routing updates\n" + "Filter outgoing routing updates\n" + "Interface name\n") + DEFUNSH (VTYSH_INTERFACE, vtysh_exit_interface, vtysh_exit_interface_cmd, @@ -1343,6 +1533,101 @@ ALIAS (vtysh_exit_interface, "quit", "Exit current mode and down to previous mode\n") +DEFUN (vtysh_show_thread, + vtysh_show_thread_cmd, + "show thread cpu [FILTER]", + SHOW_STR + "Thread information\n" + "Thread CPU usage\n" + "Display filter (rwtexb)\n") +{ + unsigned int i; + int ret = CMD_SUCCESS; + char line[100]; + + sprintf(line, "show thread cpu %s\n", (argc == 1) ? argv[0] : ""); + for (i = 0; i < array_size(vtysh_client); i++) + if ( vtysh_client[i].fd >= 0 ) + { + fprintf (stdout, "Thread statistics for %s:\n", + vtysh_client[i].name); + ret = vtysh_client_execute (&vtysh_client[i], line, stdout); + fprintf (stdout,"\n"); + } + return ret; +} + +DEFUN (vtysh_show_work_queues, + vtysh_show_work_queues_cmd, + "show work-queues", + SHOW_STR + "Work Queue information\n") +{ + unsigned int i; + int ret = CMD_SUCCESS; + char line[] = "show work-queues\n"; + + for (i = 0; i < array_size(vtysh_client); i++) + if ( vtysh_client[i].fd >= 0 ) + { + fprintf (stdout, "Work queue statistics for %s:\n", + vtysh_client[i].name); + ret = vtysh_client_execute (&vtysh_client[i], line, stdout); + fprintf (stdout,"\n"); + } + + return ret; +} + +DEFUN (vtysh_show_work_queues_daemon, + vtysh_show_work_queues_daemon_cmd, + "show work-queues (zebra|ripd|ripngd|ospfd|ospf6d|bgpd|isisd)", + SHOW_STR + "Work Queue information\n" + "For the zebra daemon\n" + "For the rip daemon\n" + "For the ripng daemon\n" + "For the ospf daemon\n" + "For the ospfv6 daemon\n" + "For the bgp daemon\n" + "For the isis daemon\n") +{ + unsigned int i; + int ret = CMD_SUCCESS; + + for (i = 0; i < array_size(vtysh_client); i++) + { + if (begins_with(vtysh_client[i].name, argv[0])) + break; + } + + ret = vtysh_client_execute(&vtysh_client[i], "show work-queues\n", stdout); + + return ret; +} + +DEFUNSH (VTYSH_ZEBRA, + vtysh_link_params, + vtysh_link_params_cmd, + "link-params", + LINK_PARAMS_STR + ) +{ + vty->node = LINK_PARAMS_NODE; + return CMD_SUCCESS; +} + +DEFUNSH (VTYSH_ZEBRA, + exit_link_params, + exit_link_params_cmd, + "exit-link-params", + "Exit from Link Params configuration node\n") +{ + if (vty->node == LINK_PARAMS_NODE) + vty->node = INTERFACE_NODE; + return CMD_SUCCESS; +} + /* Memory */ DEFUN (vtysh_show_memory, vtysh_show_memory_cmd, @@ -1354,7 +1639,7 @@ DEFUN (vtysh_show_memory, int ret = CMD_SUCCESS; char line[] = "show memory\n"; - for (i = 0; i < VTYSH_INDEX_MAX; i++) + for (i = 0; i < array_size(vtysh_client); i++) if ( vtysh_client[i].fd >= 0 ) { fprintf (stdout, "Memory statistics for %s:\n", @@ -1377,7 +1662,7 @@ DEFUN (vtysh_show_logging, int ret = CMD_SUCCESS; char line[] = "show logging\n"; - for (i = 0; i < VTYSH_INDEX_MAX; i++) + for (i = 0; i < array_size(vtysh_client); i++) if ( vtysh_client[i].fd >= 0 ) { fprintf (stdout,"Logging configuration for %s:\n", @@ -1712,7 +1997,6 @@ DEFUN (vtysh_write_terminal, "Write to terminal\n") { u_int i; - int ret; char line[] = "write terminal\n"; FILE *fp = NULL; @@ -1733,8 +2017,8 @@ DEFUN (vtysh_write_terminal, VTY_NEWLINE); vty_out (vty, "!%s", VTY_NEWLINE); - for (i = 0; i < VTYSH_INDEX_MAX; i++) - ret = vtysh_client_config (&vtysh_client[i], line); + for (i = 0; i < array_size(vtysh_client); i++) + vtysh_client_execute (&vtysh_client[i], line, NULL); /* Integrate vtysh specific configuration. */ vtysh_config_write (); @@ -1757,6 +2041,37 @@ DEFUN (vtysh_write_terminal, return CMD_SUCCESS; } +DEFUN (vtysh_write_terminal_daemon, + vtysh_write_terminal_daemon_cmd, + "write terminal (zebra|ripd|ripngd|ospfd|ospf6d|bgpd|isisd|babeld)", + "Write running configuration to memory, network, or terminal\n" + "Write to terminal\n" + "For the zebra daemon\n" + "For the rip daemon\n" + "For the ripng daemon\n" + "For the ospf daemon\n" + "For the ospfv6 daemon\n" + "For the bgp daemon\n" + "For the isis daemon\n" + "For the babel daemon\n") +{ + unsigned int i; + int ret = CMD_SUCCESS; + + for (i = 0; i < array_size(vtysh_client); i++) + { + if (strcmp(vtysh_client[i].name, argv[0]) == 0) + break; + } + + if (i == array_size(vtysh_client)) + return CMD_ERR_NO_MATCH; + + ret = vtysh_client_execute(&vtysh_client[i], "show running-config\n", stdout); + + return ret; +} + DEFUN (vtysh_integrated_config, vtysh_integrated_config_cmd, "service integrated-vtysh-config", @@ -1782,7 +2097,6 @@ static int write_config_integrated(void) { u_int i; - int ret; char line[] = "write terminal\n"; FILE *fp; char *integrate_sav = NULL; @@ -1807,9 +2121,10 @@ write_config_integrated(void) return CMD_SUCCESS; } - for (i = 0; i < VTYSH_INDEX_MAX; i++) - ret = vtysh_client_config (&vtysh_client[i], line); + for (i = 0; i < array_size(vtysh_client); i++) + vtysh_client_execute (&vtysh_client[i], line, NULL); + vtysh_config_write (); vtysh_config_dump (fp); fclose (fp); @@ -1844,7 +2159,7 @@ DEFUN (vtysh_write_memory, fprintf (stdout,"Building Configuration...\n"); - for (i = 0; i < VTYSH_INDEX_MAX; i++) + for (i = 0; i < array_size(vtysh_client); i++) ret = vtysh_client_execute (&vtysh_client[i], line, stdout); fprintf (stdout,"[OK]\n"); @@ -1876,6 +2191,20 @@ ALIAS (vtysh_write_terminal, SHOW_STR "Current operating configuration\n") +ALIAS (vtysh_write_terminal_daemon, + vtysh_show_running_config_daemon_cmd, + "show running-config (zebra|ripd|ripngd|ospfd|ospf6d|bgpd|isisd|babeld)", + SHOW_STR + "Current operating configuration\n" + "For the zebra daemon\n" + "For the rip daemon\n" + "For the ripng daemon\n" + "For the ospf daemon\n" + "For the ospfv6 daemon\n" + "For the bgp daemon\n" + "For the isis daemon\n" + "For the babel daemon\n") + DEFUN (vtysh_terminal_length, vtysh_terminal_length_cmd, "terminal length <0-512>", @@ -1934,7 +2263,7 @@ DEFUN (vtysh_show_daemons, { u_int i; - for (i = 0; i < VTYSH_INDEX_MAX; i++) + for (i = 0; i < array_size(vtysh_client); i++) if ( vtysh_client[i].fd >= 0 ) vty_out(vty, " %s", vtysh_client[i].name); vty_out(vty, "%s", VTY_NEWLINE); @@ -1947,7 +2276,6 @@ static int execute_command (const char *command, int argc, const char *arg1, const char *arg2) { - int ret; pid_t pid; int status; @@ -1966,13 +2294,13 @@ execute_command (const char *command, int argc, const char *arg1, switch (argc) { case 0: - ret = execlp (command, command, (const char *)NULL); + execlp (command, command, (const char *)NULL); break; case 1: - ret = execlp (command, command, arg1, (const char *)NULL); + execlp (command, command, arg1, (const char *)NULL); break; case 2: - ret = execlp (command, command, arg1, arg2, (const char *)NULL); + execlp (command, command, arg1, arg2, (const char *)NULL); break; } @@ -1984,7 +2312,7 @@ execute_command (const char *command, int argc, const char *arg1, { /* This is parent. */ execute_flag = 1; - ret = wait4 (pid, &status, 0, NULL); + wait4 (pid, &status, 0, NULL); execute_flag = 0; } return 0; @@ -2184,7 +2512,7 @@ vtysh_connect_all(const char *daemon_name) int rc = 0; int matches = 0; - for (i = 0; i < VTYSH_INDEX_MAX; i++) + for (i = 0; i < array_size(vtysh_client); i++) { if (!daemon_name || !strcmp(daemon_name, vtysh_client[i].name)) { @@ -2212,12 +2540,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 * @@ -2257,9 +2582,13 @@ vtysh_init_vty (void) install_node (&bgp_node, NULL); install_node (&rip_node, NULL); install_node (&interface_node, NULL); + install_node (&link_params_node, NULL); install_node (&rmap_node, NULL); install_node (&zebra_node, NULL); install_node (&bgp_vpnv4_node, NULL); + install_node (&bgp_vpnv6_node, NULL); + install_node (&bgp_encap_node, NULL); + install_node (&bgp_encapv6_node, NULL); install_node (&bgp_ipv4_node, NULL); install_node (&bgp_ipv4m_node, NULL); /* #ifdef HAVE_IPV6 */ @@ -2283,9 +2612,13 @@ vtysh_init_vty (void) vtysh_install_default (BGP_NODE); vtysh_install_default (RIP_NODE); vtysh_install_default (INTERFACE_NODE); + vtysh_install_default (LINK_PARAMS_NODE); vtysh_install_default (RMAP_NODE); vtysh_install_default (ZEBRA_NODE); vtysh_install_default (BGP_VPNV4_NODE); + vtysh_install_default (BGP_VPNV6_NODE); + vtysh_install_default (BGP_ENCAP_NODE); + vtysh_install_default (BGP_ENCAPV6_NODE); vtysh_install_default (BGP_IPV4_NODE); vtysh_install_default (BGP_IPV4M_NODE); vtysh_install_default (BGP_IPV6_NODE); @@ -2322,6 +2655,12 @@ vtysh_init_vty (void) install_element (BGP_NODE, &vtysh_quit_bgpd_cmd); install_element (BGP_VPNV4_NODE, &vtysh_exit_bgpd_cmd); install_element (BGP_VPNV4_NODE, &vtysh_quit_bgpd_cmd); + install_element (BGP_VPNV6_NODE, &vtysh_exit_bgpd_cmd); + install_element (BGP_VPNV6_NODE, &vtysh_quit_bgpd_cmd); + install_element (BGP_ENCAP_NODE, &vtysh_exit_bgpd_cmd); + install_element (BGP_ENCAP_NODE, &vtysh_quit_bgpd_cmd); + install_element (BGP_ENCAPV6_NODE, &vtysh_exit_bgpd_cmd); + install_element (BGP_ENCAPV6_NODE, &vtysh_quit_bgpd_cmd); install_element (BGP_IPV4_NODE, &vtysh_exit_bgpd_cmd); install_element (BGP_IPV4_NODE, &vtysh_quit_bgpd_cmd); install_element (BGP_IPV4M_NODE, &vtysh_exit_bgpd_cmd); @@ -2353,6 +2692,9 @@ vtysh_init_vty (void) install_element (BGP_IPV4_NODE, &vtysh_end_all_cmd); install_element (BGP_IPV4M_NODE, &vtysh_end_all_cmd); install_element (BGP_VPNV4_NODE, &vtysh_end_all_cmd); + install_element (BGP_VPNV6_NODE, &vtysh_end_all_cmd); + install_element (BGP_ENCAP_NODE, &vtysh_end_all_cmd); + install_element (BGP_ENCAPV6_NODE, &vtysh_end_all_cmd); install_element (BGP_IPV6_NODE, &vtysh_end_all_cmd); install_element (BGP_IPV6M_NODE, &vtysh_end_all_cmd); install_element (ISIS_NODE, &vtysh_end_all_cmd); @@ -2365,6 +2707,9 @@ vtysh_init_vty (void) install_element (INTERFACE_NODE, &no_interface_desc_cmd); install_element (INTERFACE_NODE, &vtysh_end_all_cmd); install_element (INTERFACE_NODE, &vtysh_exit_interface_cmd); + install_element (LINK_PARAMS_NODE, &exit_link_params_cmd); + install_element (LINK_PARAMS_NODE, &vtysh_end_all_cmd); + install_element (LINK_PARAMS_NODE, &vtysh_exit_interface_cmd); install_element (INTERFACE_NODE, &vtysh_quit_interface_cmd); install_element (CONFIG_NODE, &router_rip_cmd); #ifdef HAVE_IPV6 @@ -2374,19 +2719,26 @@ vtysh_init_vty (void) #ifdef HAVE_IPV6 install_element (CONFIG_NODE, &router_ospf6_cmd); #endif - install_element (CONFIG_NODE, &router_babel_cmd); install_element (CONFIG_NODE, &router_isis_cmd); install_element (CONFIG_NODE, &router_bgp_cmd); install_element (CONFIG_NODE, &router_bgp_view_cmd); install_element (BGP_NODE, &address_family_vpnv4_cmd); install_element (BGP_NODE, &address_family_vpnv4_unicast_cmd); + install_element (BGP_NODE, &address_family_vpnv6_cmd); + install_element (BGP_NODE, &address_family_vpnv6_unicast_cmd); + install_element (BGP_NODE, &address_family_encap_cmd); + install_element (BGP_NODE, &address_family_encapv6_cmd); install_element (BGP_NODE, &address_family_ipv4_unicast_cmd); install_element (BGP_NODE, &address_family_ipv4_multicast_cmd); #ifdef HAVE_IPV6 install_element (BGP_NODE, &address_family_ipv6_cmd); install_element (BGP_NODE, &address_family_ipv6_unicast_cmd); + install_element (BGP_NODE, &address_family_ipv6_multicast_cmd); #endif install_element (BGP_VPNV4_NODE, &exit_address_family_cmd); + install_element (BGP_VPNV6_NODE, &exit_address_family_cmd); + install_element (BGP_ENCAP_NODE, &exit_address_family_cmd); + install_element (BGP_ENCAPV6_NODE, &exit_address_family_cmd); install_element (BGP_IPV4_NODE, &exit_address_family_cmd); install_element (BGP_IPV4M_NODE, &exit_address_family_cmd); install_element (BGP_IPV6_NODE, &exit_address_family_cmd); @@ -2399,14 +2751,44 @@ vtysh_init_vty (void) install_element (KEYCHAIN_KEY_NODE, &key_chain_cmd); install_element (CONFIG_NODE, &vtysh_interface_cmd); install_element (CONFIG_NODE, &vtysh_no_interface_cmd); + install_element (CONFIG_NODE, &vtysh_interface_vrf_cmd); + install_element (CONFIG_NODE, &vtysh_no_interface_vrf_cmd); + install_element (INTERFACE_NODE, &vtysh_link_params_cmd); install_element (ENABLE_NODE, &vtysh_show_running_config_cmd); + install_element (ENABLE_NODE, &vtysh_show_running_config_daemon_cmd); install_element (ENABLE_NODE, &vtysh_copy_runningconfig_startupconfig_cmd); install_element (ENABLE_NODE, &vtysh_write_file_cmd); install_element (ENABLE_NODE, &vtysh_write_cmd); + /* distribute-list commands. (based on lib/distribute.c distribute_list_init()) */ + install_element (RIP_NODE, &distribute_list_all_cmd); + install_element (RIP_NODE, &no_distribute_list_all_cmd); + install_element (RIP_NODE, &distribute_list_cmd); + install_element (RIP_NODE, &no_distribute_list_cmd); + install_element (RIP_NODE, &distribute_list_prefix_all_cmd); + install_element (RIP_NODE, &no_distribute_list_prefix_all_cmd); + install_element (RIP_NODE, &distribute_list_prefix_cmd); + install_element (RIP_NODE, &no_distribute_list_prefix_cmd); + install_element (RIPNG_NODE, &ipv6_distribute_list_all_cmd); + install_element (RIPNG_NODE, &no_ipv6_distribute_list_all_cmd); + install_element (RIPNG_NODE, &ipv6_distribute_list_cmd); + install_element (RIPNG_NODE, &no_ipv6_distribute_list_cmd); + install_element (RIPNG_NODE, &ipv6_distribute_list_prefix_all_cmd); + install_element (RIPNG_NODE, &no_ipv6_distribute_list_prefix_all_cmd); + install_element (RIPNG_NODE, &ipv6_distribute_list_prefix_cmd); + install_element (RIPNG_NODE, &no_ipv6_distribute_list_prefix_cmd); + install_element (RIPNG_NODE, &distribute_list_all_cmd); + install_element (RIPNG_NODE, &no_distribute_list_all_cmd); + install_element (RIPNG_NODE, &distribute_list_cmd); + install_element (RIPNG_NODE, &no_distribute_list_cmd); + install_element (RIPNG_NODE, &distribute_list_prefix_all_cmd); + install_element (RIPNG_NODE, &no_distribute_list_prefix_all_cmd); + install_element (RIPNG_NODE, &distribute_list_prefix_cmd); + install_element (RIPNG_NODE, &no_distribute_list_prefix_cmd); /* "write terminal" command. */ install_element (ENABLE_NODE, &vtysh_write_terminal_cmd); - + install_element (ENABLE_NODE, &vtysh_write_terminal_daemon_cmd); + install_element (CONFIG_NODE, &vtysh_integrated_config_cmd); install_element (CONFIG_NODE, &no_vtysh_integrated_config_cmd); @@ -2449,6 +2831,14 @@ vtysh_init_vty (void) install_element (VIEW_NODE, &vtysh_show_memory_cmd); install_element (ENABLE_NODE, &vtysh_show_memory_cmd); + install_element (VIEW_NODE, &vtysh_show_work_queues_cmd); + install_element (ENABLE_NODE, &vtysh_show_work_queues_cmd); + install_element (ENABLE_NODE, &vtysh_show_work_queues_daemon_cmd); + install_element (VIEW_NODE, &vtysh_show_work_queues_daemon_cmd); + + install_element (VIEW_NODE, &vtysh_show_thread_cmd); + install_element (ENABLE_NODE, &vtysh_show_thread_cmd); + /* Logging */ install_element (ENABLE_NODE, &vtysh_show_logging_cmd); install_element (VIEW_NODE, &vtysh_show_logging_cmd); diff --git a/vtysh/vtysh.h b/vtysh/vtysh.h index 3cc7bafe0..02ff7fd7a 100644 --- a/vtysh/vtysh.h +++ b/vtysh/vtysh.h @@ -30,9 +30,11 @@ #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_NHRPD 0x200 +#define VTYSH_ALL VTYSH_ZEBRA|VTYSH_RIPD|VTYSH_RIPNGD|VTYSH_OSPFD|VTYSH_OSPF6D|VTYSH_BGPD|VTYSH_ISISD|VTYSH_BABELD|VTYSH_PIMD|VTYSH_NHRPD #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_NHRPD /* vtysh local configuration file. */ #define VTYSH_DEFAULT_CONFIG "vtysh.conf" diff --git a/vtysh/vtysh_config.c b/vtysh/vtysh_config.c index f9cb6a79b..2834ef4d6 100644 --- a/vtysh/vtysh_config.c +++ b/vtysh/vtysh_config.c @@ -47,19 +47,19 @@ struct config struct list *config_top; -int +static int line_cmp (char *c1, char *c2) { return strcmp (c1, c2); } -void +static void line_del (char *line) { XFREE (MTYPE_VTYSH_CONFIG_LINE, line); } -struct config * +static struct config * config_new () { struct config *config; @@ -67,13 +67,13 @@ config_new () return config; } -int +static int config_cmp (struct config *c1, struct config *c2) { return strcmp (c1->name, c2->name); } -void +static void config_del (struct config* config) { list_delete (config->line); @@ -82,7 +82,7 @@ config_del (struct config* config) XFREE (MTYPE_VTYSH_CONFIG, config); } -struct config * +static struct config * config_get (int index, const char *line) { struct config *config; @@ -121,13 +121,13 @@ config_get (int index, const char *line) return config; } -void +static void config_add_line (struct list *config, const char *line) { listnode_add (config, XSTRDUP (MTYPE_VTYSH_CONFIG_LINE, line)); } -void +static void config_add_line_uniq (struct list *config, const char *line) { struct listnode *node, *nnode; @@ -141,7 +141,7 @@ config_add_line_uniq (struct list *config, const char *line) listnode_add_sort (config, XSTRDUP (MTYPE_VTYSH_CONFIG_LINE, line)); } -void +static void vtysh_config_parse_line (const char *line) { char c; @@ -166,18 +166,38 @@ vtysh_config_parse_line (const char *line) /* Store line to current configuration. */ if (config) { - if (strncmp (line, " address-family vpnv4", + if (strncmp (line, " address-family vpnv4", strlen (" address-family vpnv4")) == 0) config = config_get (BGP_VPNV4_NODE, line); + else if (strncmp (line, " address-family vpn6", + strlen (" address-family vpn6")) == 0) + config = config_get (BGP_VPNV6_NODE, line); + else if (strncmp (line, " address-family encapv6", + strlen (" address-family encapv6")) == 0) + config = config_get (BGP_ENCAPV6_NODE, line); + else if (strncmp (line, " address-family encap", + strlen (" address-family encap")) == 0) + config = config_get (BGP_ENCAP_NODE, line); else if (strncmp (line, " address-family ipv4 multicast", strlen (" address-family ipv4 multicast")) == 0) config = config_get (BGP_IPV4M_NODE, line); else if (strncmp (line, " address-family ipv6", strlen (" address-family ipv6")) == 0) config = config_get (BGP_IPV6_NODE, line); + else if (strncmp (line, " link-params", strlen (" link-params")) == 0) + { + config_add_line (config->line, line); + config->index = LINK_PARAMS_NODE; + } + else if (config->index == LINK_PARAMS_NODE && + strncmp (line, " exit-link-params", strlen (" exit")) == 0) + { + config_add_line (config->line, line); + config->index = INTERFACE_NODE; + } else if (config->index == RMAP_NODE || - config->index == INTERFACE_NODE || - config->index == VTY_NODE) + config->index == INTERFACE_NODE || + config->index == VTY_NODE) config_add_line_uniq (config->line, line); else config_add_line (config->line, line); @@ -198,8 +218,6 @@ vtysh_config_parse_line (const char *line) config = config_get (OSPF_NODE, line); else if (strncmp (line, "router ospf6", strlen ("router ospf6")) == 0) config = config_get (OSPF6_NODE, line); - else if (strncmp (line, "router babel", strlen ("router babel")) == 0) - config = config_get (BABEL_NODE, line); else if (strncmp (line, "router bgp", strlen ("router bgp")) == 0) config = config_get (BGP_NODE, line); else if (strncmp (line, "router isis", strlen ("router isis")) == 0) diff --git a/vtysh/vtysh_main.c b/vtysh/vtysh_main.c index 4a315a5c8..e82771b34 100644 --- a/vtysh/vtysh_main.c +++ b/vtysh/vtysh_main.c @@ -63,7 +63,7 @@ struct thread_master *master; FILE *logfile; /* SIGTSTP handler. This function care user's ^Z input. */ -void +static void sigtstp (int sig) { /* Execute "end" command. */ @@ -84,7 +84,7 @@ sigtstp (int sig) } /* SIGINT handler. This function care user's ^Z input. */ -void +static void sigint (int sig) { /* Check this process is not child process. */ @@ -98,10 +98,9 @@ sigint (int sig) /* Signale wrapper for vtysh. We don't use sigevent because * vtysh doesn't use threads. TODO */ -RETSIGTYPE * +static void vtysh_signal_set (int signo, void (*func)(int)) { - int ret; struct sigaction sig; struct sigaction osig; @@ -112,16 +111,11 @@ vtysh_signal_set (int signo, void (*func)(int)) sig.sa_flags |= SA_RESTART; #endif /* SA_RESTART */ - ret = sigaction (signo, &sig, &osig); - - if (ret < 0) - return (SIG_ERR); - else - return (osig.sa_handler); + sigaction (signo, &sig, &osig); } /* Initialization of signal handles. */ -void +static void vtysh_signal_init () { vtysh_signal_set (SIGINT, sigint); @@ -168,7 +162,7 @@ struct option longopts[] = }; /* Read a string, and return a pointer to it. Returns NULL on EOF. */ -char * +static char * vtysh_rl_gets () { HIST_ENTRY *last; @@ -202,9 +196,12 @@ static void log_it(const char *line) { time_t t = time(NULL); struct tm *tmp = localtime(&t); - char *user = getenv("USER") ? : "boot"; + const char *user = getenv("USER"); char tod[64]; + if (!user) + user = "boot"; + strftime(tod, sizeof tod, "%Y%m%d-%H:%M.%S", tmp); fprintf(logfile, "%s:%s %s\n", tod, user, line); @@ -226,6 +223,7 @@ main (int argc, char **argv, char **env) struct cmd_rec *tail = NULL; int echo_command = 0; int no_error = 0; + char *homedir = NULL; /* Preserve name of myself. */ progname = ((p = strrchr (argv[0], '/')) ? ++p : argv[0]); @@ -253,7 +251,7 @@ main (int argc, char **argv, char **env) case 'c': { struct cmd_rec *cr; - cr = XMALLOC(0, sizeof(*cr)); + cr = XMALLOC(MTYPE_TMP, sizeof(*cr)); cr->line = optarg; cr->next = NULL; if (tail) @@ -299,8 +297,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); @@ -322,6 +318,27 @@ main (int argc, char **argv, char **env) exit(1); } + /* + * Setup history file for use by both -c and regular input + * If we can't find the home directory, then don't store + * the history information + */ + homedir = vtysh_get_home (); + if (homedir) + { + snprintf(history_file, sizeof(history_file), "%s/.history_quagga", homedir); + if (read_history (history_file) != 0) + { + int fp; + + fp = open (history_file, O_CREAT | O_EXCL, S_IRUSR | S_IWUSR); + if (fp) + close (fp); + + read_history (history_file); + } + } + /* If eval mode. */ if (cmd) { @@ -337,6 +354,9 @@ main (int argc, char **argv, char **env) { *eol = '\0'; + add_history (cmd->line); + append_history (1, history_file); + if (echo_command) printf("%s%s\n", vtysh_prompt(), cmd->line); @@ -353,6 +373,9 @@ main (int argc, char **argv, char **env) cmd->line = eol+1; } + add_history (cmd->line); + append_history (1, history_file); + if (echo_command) printf("%s%s\n", vtysh_prompt(), cmd->line); @@ -373,6 +396,8 @@ main (int argc, char **argv, char **env) XFREE(0, cr); } } + + history_truncate_file(history_file,1000); exit (0); } @@ -402,8 +427,6 @@ main (int argc, char **argv, char **env) sigsetjmp (jmpbuf, 1); jmpflag = 1; - snprintf(history_file, sizeof(history_file), "%s/.history_quagga", getenv("HOME")); - read_history(history_file); /* Main command loop. */ while (vtysh_rl_gets ()) vtysh_execute (line_read); diff --git a/vtysh/vtysh_user.c b/vtysh/vtysh_user.c index 58676c10c..584b61f51 100644 --- a/vtysh/vtysh_user.c +++ b/vtysh/vtysh_user.c @@ -37,6 +37,7 @@ #include "memory.h" #include "linklist.h" #include "command.h" +#include "vtysh_user.h" #ifdef USE_PAM static struct pam_conv conv = @@ -45,7 +46,7 @@ static struct pam_conv conv = NULL }; -int +static int vtysh_pam (const char *user) { int ret; @@ -98,19 +99,21 @@ struct vtysh_user struct list *userlist; -struct vtysh_user * +static struct vtysh_user * user_new () { - return XCALLOC (0, sizeof (struct vtysh_user)); + return XCALLOC (MTYPE_TMP, sizeof (struct vtysh_user)); } -void +#if 0 +static void user_free (struct vtysh_user *user) { XFREE (0, user); } +#endif -struct vtysh_user * +static struct vtysh_user * user_lookup (const char *name) { struct listnode *node, *nnode; @@ -124,7 +127,8 @@ user_lookup (const char *name) return NULL; } -void +#if 0 +static void user_config_write () { struct listnode *node, *nnode; @@ -136,8 +140,9 @@ user_config_write () printf (" username %s nopassword\n", user->name); } } +#endif -struct vtysh_user * +static struct vtysh_user * user_get (const char *name) { struct vtysh_user *user; @@ -166,12 +171,16 @@ DEFUN (username_nopassword, } int -vtysh_auth () +vtysh_auth (void) { struct vtysh_user *user; struct passwd *passwd; - passwd = getpwuid (geteuid ()); + if ((passwd = getpwuid (geteuid ())) == NULL) + { + fprintf (stderr, "could not lookup user ID %d\n", (int) geteuid()); + exit (1); + } user = user_lookup (passwd->pw_name); if (user && user->nopassword) @@ -186,8 +195,18 @@ vtysh_auth () return 0; } +char * +vtysh_get_home (void) +{ + struct passwd *passwd; + + passwd = getpwuid (getuid ()); + + return passwd ? passwd->pw_dir : NULL; +} + void -vtysh_user_init () +vtysh_user_init (void) { userlist = list_new (); install_element (CONFIG_NODE, &username_nopassword_cmd); diff --git a/vtysh/vtysh_user.h b/vtysh/vtysh_user.h index 8d0a4cf6a..a6c8b99b5 100644 --- a/vtysh/vtysh_user.h +++ b/vtysh/vtysh_user.h @@ -22,6 +22,9 @@ #ifndef _VTYSH_USER_H #define _VTYSH_USER_H -int vtysh_auth (); +int vtysh_auth (void); +void vtysh_user_init (void); + +char *vtysh_get_home (void); #endif /* _VTYSH_USER_H */ diff --git a/watchquagga/Makefile.am b/watchquagga/Makefile.am index a49f62e8f..1f05f26ce 100644 --- a/watchquagga/Makefile.am +++ b/watchquagga/Makefile.am @@ -1,10 +1,9 @@ ## Process this file with Automake to create Makefile.in -INCLUDES = @INCLUDES@ -I$(top_srcdir) -I$(top_srcdir)/lib +AM_CPPFLAGS = -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib DEFS = @DEFS@ -DSTATEDIR=\"$(localstatedir)/\" -AM_CFLAGS = $(PICFLAGS) -AM_LDFLAGS = $(PILDFLAGS) +AM_CFLAGS = $(WERROR) sbin_PROGRAMS = watchquagga diff --git a/watchquagga/watchquagga.c b/watchquagga/watchquagga.c index b57fd3ae0..9bd7a5f25 100644 --- a/watchquagga/watchquagga.c +++ b/watchquagga/watchquagga.c @@ -27,6 +27,7 @@ #include #include #include +#include #ifndef MIN #define MIN(X,Y) (((X) <= (Y)) ? (X) : (Y)) @@ -214,7 +215,8 @@ usage(const char *progname, int status) if (status != 0) fprintf(stderr, "Try `%s --help' for more information.\n", progname); else - printf("Usage : %s [OPTION...] ...\n\n\ + { + printf("Usage : %s [OPTION...] ...\n\n\ Watchdog program to monitor status of quagga daemons and try to restart\n\ them if they are down or unresponsive. It determines whether a daemon is\n\ up based on whether it can connect to the daemon's vty unix stream socket.\n\ @@ -258,8 +260,12 @@ the -m and -M options allow you to control the minimum delay between\n\ restart commands. The minimum restart delay is recalculated each time\n\ a restart is attempted: if the time since the last restart attempt exceeds\n\ twice the -M value, then the restart delay is set to the -m value.\n\ -Otherwise, the interval is doubled (but capped at the -M value).\n\n\ -Options:\n\ +Otherwise, the interval is doubled (but capped at the -M value).\n\n", + progname,mode_str[0],progname,mode_str[1],progname,mode_str[2], + progname,mode_str[3],progname,mode_str[4],progname,mode_str[2], + mode_str[3]); + + printf("Options:\n\ -d, --daemon Run in daemon mode. In this mode, error messages are sent\n\ to syslog instead of stdout.\n\ -S, --statedir Set the vty socket directory (default is %s)\n\ @@ -317,18 +323,18 @@ Options:\n\ it with a space. This is an ugly hack to circumvent problems\n\ passing command-line arguments with embedded spaces.\n\ -v, --version Print program version\n\ --h, --help Display this help and exit\n\ -", progname,mode_str[0],progname,mode_str[1],progname,mode_str[2], -progname,mode_str[3],progname,mode_str[4],progname,mode_str[2],mode_str[3], -VTYDIR,DEFAULT_LOGLEVEL,LOG_EMERG,LOG_DEBUG,LOG_DEBUG, -DEFAULT_MIN_RESTART,DEFAULT_MAX_RESTART, -DEFAULT_PERIOD,DEFAULT_TIMEOUT,DEFAULT_RESTART_TIMEOUT,DEFAULT_PIDFILE); +-h, --help Display this help and exit\n", + VTYDIR,DEFAULT_LOGLEVEL,LOG_EMERG,LOG_DEBUG,LOG_DEBUG, + DEFAULT_MIN_RESTART,DEFAULT_MAX_RESTART, + DEFAULT_PERIOD,DEFAULT_TIMEOUT,DEFAULT_RESTART_TIMEOUT, + DEFAULT_PIDFILE); + } return status; } static pid_t -run_background(const char *shell_cmd) +run_background(char *shell_cmd) { pid_t child; @@ -344,8 +350,10 @@ run_background(const char *shell_cmd) if (setpgid(0,0) < 0) zlog_warn("warning: setpgid(0,0) failed: %s",safe_strerror(errno)); { - const char *argv[4] = { "sh", "-c", shell_cmd, NULL}; - execv("/bin/sh",(char *const *)argv); + char shell[] = "sh"; + char dashc[] = "-c"; + char *const argv[4] = { shell, dashc, shell_cmd, NULL}; + execv("/bin/sh", argv); zlog_err("execv(/bin/sh -c '%s') failed: %s", shell_cmd,safe_strerror(errno)); _exit(127); @@ -380,7 +388,7 @@ restart_kill(struct thread *t_kill) time_elapsed(&delay,&restart->time); zlog_warn("Warning: %s %s child process %d still running after " "%ld seconds, sending signal %d", - restart->what,restart->name,(int)restart->pid,delay.tv_sec, + restart->what,restart->name,(int)restart->pid, (long)delay.tv_sec, (restart->kills ? SIGKILL : SIGTERM)); kill(-restart->pid,(restart->kills ? SIGKILL : SIGTERM)); restart->kills++; @@ -653,15 +661,17 @@ handle_read(struct thread *t_read) { dmn->state = DAEMON_UP; zlog_warn("%s state -> up : echo response received after %ld.%06ld " - "seconds", dmn->name,delay.tv_sec,delay.tv_usec); + "seconds", dmn->name, + (long)delay.tv_sec, (long)delay.tv_usec); } else zlog_warn("%s: slow echo response finally received after %ld.%06ld " - "seconds", dmn->name,delay.tv_sec,delay.tv_usec); + "seconds", dmn->name, + (long)delay.tv_sec, (long)delay.tv_usec); } else if (gs.loglevel > LOG_DEBUG+1) zlog_debug("%s: echo response received after %ld.%06ld seconds", - dmn->name,delay.tv_sec,delay.tv_usec); + dmn->name, (long)delay.tv_sec, (long)delay.tv_usec); SET_READ_HANDLER(dmn); if (dmn->t_wakeup) @@ -1279,7 +1289,7 @@ main(int argc, char **argv) gs.restart.interval = gs.min_restart_interval; master = thread_master_create(); - signal_init (master, Q_SIGC(my_signals), my_signals); + signal_init (master, array_size(my_signals), my_signals); srandom(time(NULL)); { diff --git a/zebra/Makefile.am b/zebra/Makefile.am index 542f36f4b..b23f9f1b9 100644 --- a/zebra/Makefile.am +++ b/zebra/Makefile.am @@ -1,26 +1,36 @@ +include ../common.am + ## Process this file with automake to produce Makefile.in. -INCLUDES = @INCLUDES@ -I.. -I$(top_srcdir) -I$(top_srcdir)/lib @SNMP_INCLUDES@ -DEFS = @DEFS@ -DSYSCONFDIR=\"$(sysconfdir)/\" -DMULTIPATH_NUM=@MULTIPATH_NUM@ +AM_CPPFLAGS = -I.. -I$(top_srcdir) -I$(top_srcdir)/lib -I$(top_builddir)/lib +DEFS = @DEFS@ -DSYSCONFDIR=\"$(sysconfdir)/\" 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 + +if HAVE_PROTOBUF +protobuf_srcs = zebra_fpm_protobuf.c +endif + +if DEV_BUILD +dev_srcs = zebra_fpm_dt.c +endif -AM_CFLAGS = $(PICFLAGS) -AM_LDFLAGS = $(PILDFLAGS) +AM_CFLAGS = $(WERROR) sbin_PROGRAMS = zebra @@ -29,32 +39,36 @@ 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 \ + zebra_rnh.c \ + $(othersrc) $(protobuf_srcs) $(dev_srcs) testzebra_SOURCES = test_main.c zebra_rib.c interface.c connected.c debug.c \ zebra_vty.c \ - kernel_null.c redistribute_null.c ioctl_null.c misc_null.c + kernel_null.c redistribute_null.c ioctl_null.c misc_null.c zebra_rnh_null.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 \ + ioctl_solaris.h zebra_rnh.h -zebra_LDADD = $(otherobj) $(LIBCAP) $(LIB_IPV6) ../lib/libzebra.la +zebra_LDADD = $(otherobj) ../lib/libzebra.la $(LIBCAP) $(Q_FPM_PB_CLIENT_LDOPTS) -testzebra_LDADD = $(LIBCAP) $(LIB_IPV6) ../lib/libzebra.la +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/client_main.c b/zebra/client_main.c index 8b95907bc..75c886773 100644 --- a/zebra/client_main.c +++ b/zebra/client_main.c @@ -54,6 +54,7 @@ zebra_test_ipv4 (int command, int type, char *prefix, char *gateway, inet_aton (gateway, &gate); gpnt = &gate; + api.vrf_id = VRF_DEFAULT; api.type = type; api.flags = 0; @@ -119,6 +120,7 @@ struct zebra_info { "ospf", ZEBRA_ROUTE_OSPF }, { "ospf6", ZEBRA_ROUTE_OSPF6 }, { "bgp", ZEBRA_ROUTE_BGP }, + { "nhrp", ZEBRA_ROUTE_NHRP }, { NULL, 0 } }; @@ -192,13 +194,15 @@ zebra_sim (FILE *fp) int main (int argc, char **argv) { + struct thread_master *master; FILE *fp; if (argc == 1) usage_exit (); + master = thread_master_create (); /* Establish connection to zebra. */ - zclient = zclient_new (); + zclient = zclient_new (master); zclient->enable = 1; #ifdef HAVE_TCP_ZEBRA zclient->sock = zclient_socket (); diff --git a/zebra/connected.c b/zebra/connected.c index f699b147d..1980007f4 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); @@ -73,31 +77,34 @@ connected_announce (struct interface *ifp, struct connected *ifc) { if (!ifc) return; - - listnode_add (ifp->connected, ifc); - /* Update interface address information to protocol daemon. */ - if (! CHECK_FLAG (ifc->conf, ZEBRA_IFC_REAL)) + if (!if_is_loopback(ifp) && ifc->address->family == AF_INET) { - if (ifc->address->family == AF_INET) - if_subnet_add (ifp, ifc); + if (ifc->address->prefixlen == 32) + SET_FLAG (ifc->flags, ZEBRA_IFA_UNNUMBERED); + else + UNSET_FLAG (ifc->flags, ZEBRA_IFA_UNNUMBERED); + } - SET_FLAG (ifc->conf, ZEBRA_IFC_REAL); + listnode_add (ifp->connected, ifc); - zebra_interface_address_add_update (ifp, ifc); + /* Update interface address information to protocol daemon. */ + if (ifc->address->family == AF_INET) + if_subnet_add (ifp, ifc); - if (if_is_operative(ifp)) - { - if (ifc->address->family == AF_INET) - connected_up_ipv4 (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); #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 +119,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 +139,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 +162,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(). */ @@ -189,9 +200,12 @@ connected_up_ipv4 (struct interface *ifp, struct connected *ifc) return; rib_add_ipv4 (ZEBRA_ROUTE_CONNECT, 0, &p, NULL, NULL, ifp->ifindex, - RT_TABLE_MAIN, ifp->metric, 0, SAFI_UNICAST); + ifp->vrf_id, RT_TABLE_MAIN, ifp->metric, 0, 0, SAFI_UNICAST); + + rib_add_ipv4 (ZEBRA_ROUTE_CONNECT, 0, &p, NULL, NULL, ifp->ifindex, + ifp->vrf_id, RT_TABLE_MAIN, ifp->metric, 0, 0, SAFI_MULTICAST); - rib_update (); + rib_update (ifp->vrf_id); } /* Add connected IPv4 route to the interface. */ @@ -207,6 +221,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 (); @@ -269,11 +286,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 @@ -295,9 +312,13 @@ connected_down_ipv4 (struct interface *ifp, struct connected *ifc) return; /* Same logic as for connected_up_ipv4(): push the changes into the head. */ - rib_delete_ipv4 (ZEBRA_ROUTE_CONNECT, 0, &p, NULL, ifp->ifindex, 0, SAFI_UNICAST); + rib_delete_ipv4 (ZEBRA_ROUTE_CONNECT, 0, &p, NULL, ifp->ifindex, ifp->vrf_id, + SAFI_UNICAST); + + rib_delete_ipv4 (ZEBRA_ROUTE_CONNECT, 0, &p, NULL, ifp->ifindex, ifp->vrf_id, + SAFI_MULTICAST); - rib_update (); + rib_update (ifp->vrf_id); } /* Delete connected IPv4 route to the interface. */ @@ -319,7 +340,7 @@ connected_delete_ipv4 (struct interface *ifp, int flags, struct in_addr *addr, connected_withdraw (ifc); - rib_update(); + rib_update (ifp->vrf_id); } #ifdef HAVE_IPV6 @@ -336,16 +357,16 @@ 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; #endif - rib_add_ipv6 (ZEBRA_ROUTE_CONNECT, 0, &p, NULL, ifp->ifindex, RT_TABLE_MAIN, - ifp->metric, 0, SAFI_UNICAST); + rib_add_ipv6 (ZEBRA_ROUTE_CONNECT, 0, &p, NULL, ifp->ifindex, ifp->vrf_id, + RT_TABLE_MAIN, ifp->metric, 0, 0, SAFI_UNICAST); - rib_update (); + rib_update (ifp->vrf_id); } /* Add connected IPv6 route to the interface. */ @@ -361,6 +382,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 (); @@ -395,11 +419,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 @@ -417,9 +446,10 @@ connected_down_ipv6 (struct interface *ifp, struct connected *ifc) if (IN6_IS_ADDR_UNSPECIFIED (&p.prefix)) return; - rib_delete_ipv6 (ZEBRA_ROUTE_CONNECT, 0, &p, NULL, ifp->ifindex, 0, SAFI_UNICAST); + rib_delete_ipv6 (ZEBRA_ROUTE_CONNECT, 0, &p, NULL, ifp->ifindex, ifp->vrf_id, + SAFI_UNICAST); - rib_update (); + rib_update (ifp->vrf_id); } void @@ -440,6 +470,6 @@ connected_delete_ipv6 (struct interface *ifp, struct in6_addr *address, connected_withdraw (ifc); - rib_update(); + rib_update (ifp->vrf_id); } #endif /* HAVE_IPV6 */ diff --git a/zebra/debug.c b/zebra/debug.c index 175029b8a..7d023ce5a 100644 --- a/zebra/debug.c +++ b/zebra/debug.c @@ -29,13 +29,15 @@ 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; +unsigned long zebra_debug_nht; 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 +73,11 @@ 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); + if (IS_ZEBRA_DEBUG_NHT) + vty_out (vty, " Zebra next-hop tracking debugging is on%s", VTY_NEWLINE); + return CMD_SUCCESS; } @@ -85,6 +92,17 @@ DEFUN (debug_zebra_events, return CMD_WARNING; } +DEFUN (debug_zebra_nht, + debug_zebra_nht_cmd, + "debug zebra nht", + DEBUG_STR + "Zebra configuration\n" + "Debug option set for zebra next hop tracking\n") +{ + zebra_debug_nht = ZEBRA_DEBUG_NHT; + return CMD_WARNING; +} + DEFUN (debug_zebra_packet, debug_zebra_packet_cmd, "debug zebra packet", @@ -93,14 +111,14 @@ DEFUN (debug_zebra_packet, "Debug option set for zebra packet\n") { zebra_debug_packet = ZEBRA_DEBUG_PACKET; - zebra_debug_packet |= ZEBRA_DEBUG_SEND; - zebra_debug_packet |= ZEBRA_DEBUG_RECV; + SET_FLAG(zebra_debug_packet, ZEBRA_DEBUG_SEND); + SET_FLAG(zebra_debug_packet, ZEBRA_DEBUG_RECV); return CMD_SUCCESS; } DEFUN (debug_zebra_packet_direct, debug_zebra_packet_direct_cmd, - "debug zebra packet (recv|send)", + "debug zebra packet (recv|send|detail)", DEBUG_STR "Zebra configuration\n" "Debug option set for zebra packet\n" @@ -109,10 +127,11 @@ DEFUN (debug_zebra_packet_direct, { zebra_debug_packet = ZEBRA_DEBUG_PACKET; if (strncmp ("send", argv[0], strlen (argv[0])) == 0) - zebra_debug_packet |= ZEBRA_DEBUG_SEND; + SET_FLAG(zebra_debug_packet, ZEBRA_DEBUG_SEND); if (strncmp ("recv", argv[0], strlen (argv[0])) == 0) - zebra_debug_packet |= ZEBRA_DEBUG_RECV; - zebra_debug_packet &= ~ZEBRA_DEBUG_DETAIL; + SET_FLAG(zebra_debug_packet, ZEBRA_DEBUG_RECV); + if (strncmp ("detail", argv[0], strlen (argv[0])) == 0) + SET_FLAG(zebra_debug_packet, ZEBRA_DEBUG_DETAIL); return CMD_SUCCESS; } @@ -124,14 +143,14 @@ 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) - zebra_debug_packet |= ZEBRA_DEBUG_SEND; + SET_FLAG(zebra_debug_packet, ZEBRA_DEBUG_SEND); if (strncmp ("recv", argv[0], strlen (argv[0])) == 0) - zebra_debug_packet |= ZEBRA_DEBUG_RECV; - zebra_debug_packet |= ZEBRA_DEBUG_DETAIL; + SET_FLAG(zebra_debug_packet, ZEBRA_DEBUG_RECV); + SET_FLAG(zebra_debug_packet, ZEBRA_DEBUG_DETAIL); return CMD_SUCCESS; } @@ -169,6 +188,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", @@ -181,6 +211,18 @@ DEFUN (no_debug_zebra_events, return CMD_SUCCESS; } +DEFUN (no_debug_zebra_nht, + no_debug_zebra_nht_cmd, + "no debug zebra nht", + NO_STR + DEBUG_STR + "Zebra configuration\n" + "Debug option set for zebra next hop tracking\n") +{ + zebra_debug_nht = 0; + return CMD_SUCCESS; +} + DEFUN (no_debug_zebra_packet, no_debug_zebra_packet_cmd, "no debug zebra packet", @@ -204,9 +246,9 @@ DEFUN (no_debug_zebra_packet_direct, "Debug option set for send packet\n") { if (strncmp ("send", argv[0], strlen (argv[0])) == 0) - zebra_debug_packet &= ~ZEBRA_DEBUG_SEND; + UNSET_FLAG(zebra_debug_packet, ZEBRA_DEBUG_SEND); if (strncmp ("recv", argv[0], strlen (argv[0])) == 0) - zebra_debug_packet &= ~ZEBRA_DEBUG_RECV; + UNSET_FLAG(zebra_debug_packet, ZEBRA_DEBUG_RECV); return CMD_SUCCESS; } @@ -247,6 +289,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 +356,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,35 +371,43 @@ 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); install_element (VIEW_NODE, &show_debugging_zebra_cmd); - install_element (ENABLE_NODE, &show_debugging_zebra_cmd); install_element (ENABLE_NODE, &debug_zebra_events_cmd); + install_element (ENABLE_NODE, &debug_zebra_nht_cmd); install_element (ENABLE_NODE, &debug_zebra_packet_cmd); install_element (ENABLE_NODE, &debug_zebra_packet_direct_cmd); install_element (ENABLE_NODE, &debug_zebra_packet_detail_cmd); 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_nht_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_nht_cmd); install_element (CONFIG_NODE, &debug_zebra_packet_cmd); install_element (CONFIG_NODE, &debug_zebra_packet_direct_cmd); install_element (CONFIG_NODE, &debug_zebra_packet_detail_cmd); 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_nht_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 9d9412bca..0fb4dd9fe 100644 --- a/zebra/debug.h +++ b/zebra/debug.h @@ -36,6 +36,9 @@ #define ZEBRA_DEBUG_RIB 0x01 #define ZEBRA_DEBUG_RIB_Q 0x02 +#define ZEBRA_DEBUG_FPM 0x01 +#define ZEBRA_DEBUG_NHT 0x01 + /* Debug related macro. */ #define IS_ZEBRA_DEBUG_EVENT (zebra_debug_event & ZEBRA_DEBUG_EVENT) @@ -49,10 +52,15 @@ #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) +#define IS_ZEBRA_DEBUG_NHT (zebra_debug_nht & ZEBRA_DEBUG_NHT) + 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 unsigned long zebra_debug_nht; extern void zebra_debug_init (void); diff --git a/zebra/if_ioctl.c b/zebra/if_ioctl.c index f357e1544..99328a1d2 100644 --- a/zebra/if_ioctl.c +++ b/zebra/if_ioctl.c @@ -29,8 +29,11 @@ #include "connected.h" #include "memory.h" #include "log.h" +#include "vrf.h" +#include "vty.h" #include "zebra/interface.h" +#include "zebra/rib.h" /* Interface looking up using infamous SIOCGIFCONF. */ static int @@ -442,8 +445,13 @@ interface_info_ioctl () /* Lookup all interface information. */ void -interface_list () +interface_list (struct zebra_vrf *zvrf) { + if (zvrf->vrf_id != VRF_DEFAULT) + { + zlog_warn ("interface_list: ignore VRF %u", zvrf->vrf_id); + return; + } /* Linux can do both proc & ioctl, ioctl is the only way to get interface aliases in 2.2 series kernels. */ #ifdef HAVE_PROC_NET_DEV diff --git a/zebra/if_ioctl_solaris.c b/zebra/if_ioctl_solaris.c index fc384ea29..b399812e4 100644 --- a/zebra/if_ioctl_solaris.c +++ b/zebra/if_ioctl_solaris.c @@ -30,16 +30,18 @@ #include "memory.h" #include "log.h" #include "privs.h" +#include "vrf.h" +#include "vty.h" #include "zebra/interface.h" +#include "zebra/ioctl_solaris.h" +#include "zebra/rib.h" -void lifreq_set_name (struct lifreq *, const char *); -int if_get_flags_direct (const char *, uint64_t *, unsigned int af); static int if_get_addr (struct interface *, struct sockaddr *, const char *); static void interface_info_ioctl (struct interface *); extern struct zebra_privs_t zserv_privs; -int +static int interface_list_ioctl (int af) { int ret; @@ -208,7 +210,7 @@ interface_list_ioctl (int af) } /* Get interface's index by ioctl. */ -int +static int if_get_index (struct interface *ifp) { int ret; @@ -349,8 +351,13 @@ interface_info_ioctl (struct interface *ifp) /* Lookup all interface information. */ void -interface_list () +interface_list (struct zebra_vrf *zvrf) { + if (zvrf->vrf_id != VRF_DEFAULT) + { + zlog_warn ("interface_list: ignore VRF %u", zvrf->vrf_id); + return; + } interface_list_ioctl (AF_INET); interface_list_ioctl (AF_INET6); interface_list_ioctl (AF_UNSPEC); diff --git a/zebra/if_netlink.c b/zebra/if_netlink.c index 86bd8ffe4..245b7b250 100644 --- a/zebra/if_netlink.c +++ b/zebra/if_netlink.c @@ -23,12 +23,11 @@ #include #include "zebra/zserv.h" - -extern int interface_lookup_netlink (void); +#include "rt_netlink.h" /* Interface information read by netlink. */ void -interface_list (void) +interface_list (struct zebra_vrf *zvrf) { - interface_lookup_netlink (); + interface_lookup_netlink (zvrf); } diff --git a/zebra/if_proc.c b/zebra/if_proc.c deleted file mode 100644 index 3aec530bf..000000000 --- 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/if_sysctl.c b/zebra/if_sysctl.c index 5e8099647..bb48f6182 100644 --- a/zebra/if_sysctl.c +++ b/zebra/if_sysctl.c @@ -29,9 +29,12 @@ #include "memory.h" #include "ioctl.h" #include "log.h" +#include "interface.h" +#include "vrf.h" #include "zebra/rt.h" #include "zebra/kernel_socket.h" +#include "zebra/rib.h" void ifstat_update_sysctl (void) @@ -89,7 +92,7 @@ ifstat_update_sysctl (void) /* Interface listing up function using sysctl(). */ void -interface_list () +interface_list (struct zebra_vrf *zvrf) { caddr_t ref, buf, end; size_t bufsiz; @@ -106,6 +109,12 @@ interface_list () 0 }; + if (zvrf->vrf_id != VRF_DEFAULT) + { + zlog_warn ("interface_list: ignore VRF %u", zvrf->vrf_id); + return; + } + /* Query buffer size. */ if (sysctl (mib, MIBSIZ, NULL, &bufsiz, NULL, 0) < 0) { diff --git a/zebra/interface.c b/zebra/interface.c index 22422594c..f8b946ff4 100644 --- a/zebra/interface.c +++ b/zebra/interface.c @@ -32,6 +32,8 @@ #include "connected.h" #include "log.h" #include "zclient.h" +#include "vrf.h" +#include "command.h" #include "zebra/interface.h" #include "zebra/rtadv.h" @@ -41,11 +43,55 @@ #include "zebra/debug.h" #include "zebra/irdp.h" -#ifdef RTADV +#if defined (HAVE_RTADV) /* Order is intentional. Matches RFC4191. This array is also used for command matching, so only modify with care. */ const char *rtadv_pref_strs[] = { "medium", "high", "INVALID", "low", 0 }; -#endif /* RTADV */ +#endif /* HAVE_RTADV */ + +/* We don't have a tidy top-level instance object for zebra, or interfaces */ +static struct zebra_if_defaults zif_defaults = { + .linkdetect = IF_LINKDETECT_UNSPEC, +}; + +/* helper only for if_zebra_linkdetect */ +static void +if_zebra_linkdetect_set_val (struct interface *ifp, zebra_if_linkdetect val) +{ + switch (val) + { + case IF_LINKDETECT_ON: + SET_FLAG(ifp->status, ZEBRA_INTERFACE_LINKDETECTION); + break; + case IF_LINKDETECT_OFF: + UNSET_FLAG(ifp->status, ZEBRA_INTERFACE_LINKDETECTION); + break; + default: break; + } +} + +static void +if_zebra_linkdetect_set (struct interface *ifp) +{ + struct zebra_if *zif = ifp->info; + assert (zif != NULL); + int if_was_operative = if_is_operative(ifp); + + /* If user has explicitly configured for the interface, let that set */ + if (zif->linkdetect != IF_LINKDETECT_UNSPEC) + if_zebra_linkdetect_set_val (ifp, zif->linkdetect); + else + { + /* general compiled in default is to set */ + SET_FLAG(ifp->status, ZEBRA_INTERFACE_LINKDETECTION); + /* but user can specify a default too */ + if_zebra_linkdetect_set_val (ifp, zif_defaults.linkdetect); + } + /* When linkdetection is enabled, interface might come down */ + if (!if_is_operative(ifp) && if_was_operative) if_down(ifp); + /* Alternatively, it may come up after disabling link detection */ + if (if_is_operative(ifp) && !if_was_operative) if_up(ifp); +} /* Called when new interface is added. */ static int @@ -56,9 +102,21 @@ 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 + switch (zif_defaults.linkdetect) + { + case IF_LINKDETECT_OFF: + UNSET_FLAG(ifp->status, ZEBRA_INTERFACE_LINKDETECTION); + break; + case IF_LINKDETECT_UNSPEC: + case IF_LINKDETECT_ON: + default: + SET_FLAG(ifp->status, ZEBRA_INTERFACE_LINKDETECTION); + break; + } + +#if defined (HAVE_RTADV) { /* Set default router advertise values. */ struct rtadvconf *rtadv; @@ -84,7 +142,7 @@ if_zebra_new_hook (struct interface *ifp) rtadv->AdvPrefixList = list_new (); } -#endif /* RTADV */ +#endif /* HAVE_RTADV */ /* Initialize installed address chains tree. */ zebra_if->ipv4_subnets = route_table_init (); @@ -161,11 +219,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); @@ -175,9 +248,12 @@ if_subnet_delete (struct interface *ifp, struct connected *ifc) /* If deleted address is primary, mark subsequent one as such and distribute. */ if (! CHECK_FLAG (ifc->flags, ZEBRA_IFA_SECONDARY)) { - ifc = listgetdata (listhead (addr_list)); + ifc = listgetdata ((struct listnode *)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 +350,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 +386,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,18 +408,45 @@ 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 */ } } } +static void if_count_up(struct zebra_if *zif) +{ + event_counter_inc(&zif->up_events); +} + +static void if_count_down(struct zebra_if *zif) +{ + event_counter_inc(&zif->down_events); +} + +void +if_startup_count_up (void) +{ + vrf_iter_t iter; + struct interface *ifp; + struct zebra_if *zif; + struct listnode *node; + + for (iter = vrf_first(); iter != VRF_ITER_INVALID; iter = vrf_next(iter)) + { + for (ALL_LIST_ELEMENTS_RO (vrf_iter2iflist(iter), node, ifp)) + { + zif = ifp->info; + if (!zif->up_events.count && if_is_operative(ifp)) + if_count_up(zif); + } + } +} + /* Handle interface addition */ void if_add_update (struct interface *ifp) @@ -349,6 +454,8 @@ if_add_update (struct interface *ifp) struct zebra_if *if_data; if_data = ifp->info; + assert(if_data); + if (if_data->multicast == IF_ZEBRA_MULTICAST_ON) if_set_flags (ifp, IFF_MULTICAST); else if (if_data->multicast == IF_ZEBRA_MULTICAST_OFF) @@ -360,16 +467,37 @@ 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 vrf %u index %d is shutdown. " + "Won't wake it up.", + ifp->name, ifp->vrf_id, ifp->ifindex); + return; + } + if_addr_wakeup (ifp); if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug ("interface %s index %d becomes active.", - ifp->name, ifp->ifindex); + zlog_debug ("interface %s vrf %u index %d becomes active.", + ifp->name, ifp->vrf_id, ifp->ifindex); } else { if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug ("interface %s index %d is added.", ifp->name, ifp->ifindex); + zlog_debug ("interface %s vrf %u index %d is added.", + ifp->name, ifp->vrf_id, ifp->ifindex); + } + + if (host_config_get()) + { + /* If configuration and therefore link-detect have already been + * loaded, count an initial up event when new interfaces are added + * in up state. + * If configuration has not been loaded yet, this is handled by + * if_startup_count_up which is called after reading the config. */ + if (!if_data->up_events.count && if_is_operative(ifp)) + if_count_up(if_data); } } @@ -386,8 +514,8 @@ if_delete_update (struct interface *ifp) if (if_is_up(ifp)) { - zlog_err ("interface %s index %d is still up while being deleted.", - ifp->name, ifp->ifindex); + zlog_err ("interface %s vrf %u index %d is still up while being deleted.", + ifp->name, ifp->vrf_id, ifp->ifindex); return; } @@ -395,8 +523,8 @@ if_delete_update (struct interface *ifp) UNSET_FLAG (ifp->status, ZEBRA_INTERFACE_ACTIVE); if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug ("interface %s index %d is now inactive.", - ifp->name, ifp->ifindex); + zlog_debug ("interface %s vrf %u index %d is now inactive.", + ifp->name, ifp->vrf_id, ifp->ifindex); /* Delete connected routes from the kernel. */ if (ifp->connected) @@ -433,12 +561,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 +598,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; @@ -502,6 +634,8 @@ if_up (struct interface *ifp) struct connected *ifc; struct prefix *p; + if_count_up(ifp->info); + /* Notify the protocol daemons. */ zebra_interface_up_update (ifp); @@ -522,7 +656,7 @@ if_up (struct interface *ifp) } /* Examine all static routes. */ - rib_update (); + rib_update (ifp->vrf_id); } /* Interface goes down. We have to manage different behavior of based @@ -534,6 +668,11 @@ if_down (struct interface *ifp) struct listnode *next; struct connected *ifc; struct prefix *p; + struct zebra_if *zif; + + zif = ifp->info; + if (zif->up_events.count) + if_count_down(zif); /* Notify to the protocol daemons. */ zebra_interface_down_update (ifp); @@ -555,7 +694,7 @@ if_down (struct interface *ifp) } /* Examine all static routes which direct to the interface. */ - rib_update (); + rib_update (ifp->vrf_id); } void @@ -597,13 +736,16 @@ connected_dump_vty (struct vty *vty, struct connected *connected) if (CHECK_FLAG (connected->flags, ZEBRA_IFA_SECONDARY)) vty_out (vty, " secondary"); + if (CHECK_FLAG (connected->flags, ZEBRA_IFA_UNNUMBERED)) + vty_out (vty, " unnumbered"); + if (connected->label) vty_out (vty, " %s", connected->label); vty_out (vty, "%s", VTY_NEWLINE); } -#ifdef RTADV +#if defined (HAVE_RTADV) /* Dump interface ND information to vty. */ static void nd_dump_vty (struct vty *vty, struct interface *ifp) @@ -664,15 +806,12 @@ nd_dump_vty (struct vty *vty, struct interface *ifp) VTY_NEWLINE); } } -#endif /* RTADV */ +#endif /* HAVE_RTADV */ /* Interface's information print out to vty interface. */ static void if_dump_vty (struct vty *vty, struct interface *ifp) { -#ifdef HAVE_STRUCT_SOCKADDR_DL - struct sockaddr_dl *sdl; -#endif /* HAVE_STRUCT_SOCKADDR_DL */ struct connected *connected; struct listnode *node; struct route_node *rn; @@ -696,6 +835,13 @@ if_dump_vty (struct vty *vty, struct interface *ifp) vty_out (vty, "down%s", VTY_NEWLINE); } + vty_out (vty, " Link ups: %s%s", + event_counter_format(&zebra_if->up_events), VTY_NEWLINE); + vty_out (vty, " Link downs: %s%s", + event_counter_format(&zebra_if->down_events), VTY_NEWLINE); + + vty_out (vty, " vrf: %u%s", ifp->vrf_id, VTY_NEWLINE); + if (ifp->desc) vty_out (vty, " Description: %s%s", ifp->desc, VTY_NEWLINE); @@ -722,19 +868,7 @@ if_dump_vty (struct vty *vty, struct interface *ifp) if_flag_dump (ifp->flags), VTY_NEWLINE); /* Hardware address. */ -#ifdef HAVE_STRUCT_SOCKADDR_DL - sdl = &ifp->sdl; - if (sdl != NULL && sdl->sdl_alen != 0) - { - int i; - u_char *ptr; - - vty_out (vty, " HWaddr: "); - for (i = 0, ptr = (u_char *)LLADDR (sdl); i < sdl->sdl_alen; i++, ptr++) - vty_out (vty, "%s%02x", i == 0 ? "" : ":", *ptr); - vty_out (vty, "%s", VTY_NEWLINE); - } -#else + vty_out (vty, " Type: %s%s", if_link_type_str (ifp->ll_type), VTY_NEWLINE); if (ifp->hw_addr_len != 0) { int i; @@ -744,7 +878,6 @@ if_dump_vty (struct vty *vty, struct interface *ifp) vty_out (vty, "%s%02x", i == 0 ? "" : ":", ifp->hw_addr[i]); vty_out (vty, "%s", VTY_NEWLINE); } -#endif /* HAVE_STRUCT_SOCKADDR_DL */ /* Bandwidth in kbps */ if (ifp->bandwidth != 0) @@ -757,7 +890,7 @@ if_dump_vty (struct vty *vty, struct interface *ifp) { if (! rn->info) continue; - + for (ALL_LIST_ELEMENTS_RO ((struct list *)rn->info, node, connected)) connected_dump_vty (vty, connected); } @@ -769,9 +902,56 @@ if_dump_vty (struct vty *vty, struct interface *ifp) connected_dump_vty (vty, connected); } -#ifdef RTADV + if (HAS_LINK_PARAMS(ifp)) + { + int i; + struct if_link_params *iflp = ifp->link_params; + vty_out(vty, " Traffic Engineering Link Parameters:%s", VTY_NEWLINE); + if (IS_PARAM_SET(iflp, LP_TE)) + vty_out(vty, " TE metric %u%s",iflp->te_metric, VTY_NEWLINE); + if (IS_PARAM_SET(iflp, LP_MAX_BW)) + vty_out(vty, " Maximum Bandwidth %g (Byte/s)%s", iflp->max_bw, VTY_NEWLINE); + if (IS_PARAM_SET(iflp, LP_MAX_RSV_BW)) + vty_out(vty, " Maximum Reservable Bandwidth %g (Byte/s)%s", iflp->max_rsv_bw, VTY_NEWLINE); + if (IS_PARAM_SET(iflp, LP_UNRSV_BW)) { + vty_out(vty, " Unreserved Bandwidth per Class Type in Byte/s:%s", VTY_NEWLINE); + for (i = 0; i < MAX_CLASS_TYPE; i+=2) + vty_out(vty, " [%d]: %g (Bytes/sec),\t[%d]: %g (Bytes/sec)%s", + i, iflp->unrsv_bw[i], i+1, iflp->unrsv_bw[i+1], VTY_NEWLINE); + } + + if (IS_PARAM_SET(iflp, LP_ADM_GRP)) + vty_out(vty, " Administrative Group:%u%s", iflp->admin_grp, VTY_NEWLINE); + if (IS_PARAM_SET(iflp, LP_DELAY)) + { + vty_out(vty, " Link Delay Average: %u (micro-sec.)", iflp->av_delay); + if (IS_PARAM_SET(iflp, LP_MM_DELAY)) + { + vty_out(vty, " Min: %u (micro-sec.)", iflp->min_delay); + vty_out(vty, " Max: %u (micro-sec.)", iflp->max_delay); + } + vty_out(vty, "%s", VTY_NEWLINE); + } + if (IS_PARAM_SET(iflp, LP_DELAY_VAR)) + vty_out(vty, " Link Delay Variation %u (micro-sec.)%s", iflp->delay_var, VTY_NEWLINE); + if (IS_PARAM_SET(iflp, LP_PKT_LOSS)) + vty_out(vty, " Link Packet Loss %g (in %%)%s", iflp->pkt_loss, VTY_NEWLINE); + if (IS_PARAM_SET(iflp, LP_AVA_BW)) + vty_out(vty, " Available Bandwidth %g (Byte/s)%s", iflp->ava_bw, VTY_NEWLINE); + if (IS_PARAM_SET(iflp, LP_RES_BW)) + vty_out(vty, " Residual Bandwidth %g (Byte/s)%s", iflp->res_bw, VTY_NEWLINE); + if (IS_PARAM_SET(iflp, LP_USE_BW)) + vty_out(vty, " Utilized Bandwidth %g (Byte/s)%s", iflp->use_bw, VTY_NEWLINE); + if (IS_PARAM_SET(iflp, LP_RMT_AS)) + vty_out(vty, " Neighbor ASBR IP: %s AS: %u %s", inet_ntoa(iflp->rmt_ip), iflp->rmt_as, VTY_NEWLINE); + } + + #ifdef RTADV + nd_dump_vty (vty, ifp); + #endif /* RTADV */ +#if defined (HAVE_RTADV) nd_dump_vty (vty, ifp); -#endif /* RTADV */ +#endif /* HAVE_RTADV */ #ifdef HAVE_PROC_NET_DEV /* Statistics print out using proc file system. */ @@ -806,24 +986,29 @@ if_dump_vty (struct vty *vty, struct interface *ifp) #ifdef HAVE_NET_RT_IFLIST #if defined (__bsdi__) || defined (__NetBSD__) /* Statistics print out using sysctl (). */ - vty_out (vty, " input packets %qu, bytes %qu, dropped %qu," - " multicast packets %qu%s", - ifp->stats.ifi_ipackets, ifp->stats.ifi_ibytes, - ifp->stats.ifi_iqdrops, ifp->stats.ifi_imcasts, - VTY_NEWLINE); - - vty_out (vty, " input errors %qu%s", - ifp->stats.ifi_ierrors, VTY_NEWLINE); - - vty_out (vty, " output packets %qu, bytes %qu, multicast packets %qu%s", - ifp->stats.ifi_opackets, ifp->stats.ifi_obytes, - ifp->stats.ifi_omcasts, VTY_NEWLINE); - - vty_out (vty, " output errors %qu%s", - ifp->stats.ifi_oerrors, VTY_NEWLINE); - - vty_out (vty, " collisions %qu%s", - ifp->stats.ifi_collisions, VTY_NEWLINE); + vty_out (vty, " input packets %llu, bytes %llu, dropped %llu," + " multicast packets %llu%s", + (unsigned long long)ifp->stats.ifi_ipackets, + (unsigned long long)ifp->stats.ifi_ibytes, + (unsigned long long)ifp->stats.ifi_iqdrops, + (unsigned long long)ifp->stats.ifi_imcasts, + VTY_NEWLINE); + + vty_out (vty, " input errors %llu%s", + (unsigned long long)ifp->stats.ifi_ierrors, VTY_NEWLINE); + + vty_out (vty, " output packets %llu, bytes %llu," + " multicast packets %llu%s", + (unsigned long long)ifp->stats.ifi_opackets, + (unsigned long long)ifp->stats.ifi_obytes, + (unsigned long long)ifp->stats.ifi_omcasts, + VTY_NEWLINE); + + vty_out (vty, " output errors %llu%s", + (unsigned long long)ifp->stats.ifi_oerrors, VTY_NEWLINE); + + vty_out (vty, " collisions %llu%s", + (unsigned long long)ifp->stats.ifi_collisions, VTY_NEWLINE); #else /* Statistics print out using sysctl (). */ vty_out (vty, " input packets %lu, bytes %lu, dropped %lu," @@ -859,13 +1044,13 @@ DEFUN_NOSH (zebra_interface, "Interface's name\n") { int ret; - struct interface * ifp; + struct interface *ifp; /* Call lib interface() */ if ((ret = interface_cmd.func (self, vty, argc, argv)) != CMD_SUCCESS) return ret; - ifp = vty->index; + ifp = vty->index; if (ifp->ifindex == IFINDEX_INTERNAL) /* Is this really necessary? Shouldn't status be initialized to 0 @@ -875,6 +1060,13 @@ DEFUN_NOSH (zebra_interface, return ret; } +ALIAS (zebra_interface, + zebra_interface_vrf_cmd, + "interface IFNAME " VRF_CMD_STR, + "Select an interface to configure\n" + "Interface's name\n" + VRF_CMD_HELP_STR) + struct cmd_node interface_node = { INTERFACE_NODE, @@ -882,16 +1074,80 @@ struct cmd_node interface_node = 1 }; -/* Show all or specified interface to vty. */ +/* Show all interfaces to vty. */ DEFUN (show_interface, show_interface_cmd, - "show interface [IFNAME]", + "show interface", + SHOW_STR + "Interface status and configuration\n") +{ + struct listnode *node; + struct interface *ifp; + vrf_id_t vrf_id = VRF_DEFAULT; + +#ifdef HAVE_PROC_NET_DEV + /* If system has interface statistics via proc file system, update + statistics. */ + ifstat_update_proc (); +#endif /* HAVE_PROC_NET_DEV */ +#ifdef HAVE_NET_RT_IFLIST + ifstat_update_sysctl (); +#endif /* HAVE_NET_RT_IFLIST */ + + if (argc > 0) + VTY_GET_INTEGER ("VRF ID", vrf_id, argv[0]); + + /* All interface print. */ + for (ALL_LIST_ELEMENTS_RO (vrf_iflist (vrf_id), node, ifp)) + if_dump_vty (vty, ifp); + + return CMD_SUCCESS; +} + +ALIAS (show_interface, + show_interface_vrf_cmd, + "show interface " VRF_CMD_STR, SHOW_STR "Interface status and configuration\n" - "Inteface name\n") + VRF_CMD_HELP_STR) + +/* Show all interfaces to vty. */ +DEFUN (show_interface_vrf_all, show_interface_vrf_all_cmd, + "show interface " VRF_ALL_CMD_STR, + SHOW_STR + "Interface status and configuration\n" + VRF_ALL_CMD_HELP_STR) { struct listnode *node; struct interface *ifp; - + vrf_iter_t iter; + +#ifdef HAVE_PROC_NET_DEV + /* If system has interface statistics via proc file system, update + statistics. */ + ifstat_update_proc (); +#endif /* HAVE_PROC_NET_DEV */ +#ifdef HAVE_NET_RT_IFLIST + ifstat_update_sysctl (); +#endif /* HAVE_NET_RT_IFLIST */ + + /* All interface print. */ + for (iter = vrf_first (); iter != VRF_ITER_INVALID; iter = vrf_next (iter)) + for (ALL_LIST_ELEMENTS_RO (vrf_iter2iflist (iter), node, ifp)) + if_dump_vty (vty, ifp); + + return CMD_SUCCESS; +} + +/* Show specified interface to vty. */ +DEFUN (show_interface_name, show_interface_name_cmd, + "show interface IFNAME", + SHOW_STR + "Interface status and configuration\n" + "Inteface name\n") +{ + struct interface *ifp; + vrf_id_t vrf_id = VRF_DEFAULT; + #ifdef HAVE_PROC_NET_DEV /* If system has interface statistics via proc file system, update statistics. */ @@ -901,39 +1157,80 @@ DEFUN (show_interface, show_interface_cmd, ifstat_update_sysctl (); #endif /* HAVE_NET_RT_IFLIST */ + if (argc > 1) + VTY_GET_INTEGER ("VRF ID", vrf_id, argv[1]); + /* Specified interface print. */ - if (argc != 0) + ifp = if_lookup_by_name_vrf (argv[0], vrf_id); + if (ifp == NULL) { - ifp = if_lookup_by_name (argv[0]); - if (ifp == NULL) - { - vty_out (vty, "%% Can't find interface %s%s", argv[0], - VTY_NEWLINE); - return CMD_WARNING; - } - if_dump_vty (vty, ifp); - return CMD_SUCCESS; + vty_out (vty, "%% Can't find interface %s%s", argv[0], + VTY_NEWLINE); + return CMD_WARNING; } - - /* All interface print. */ - for (ALL_LIST_ELEMENTS_RO (iflist, node, ifp)) - if_dump_vty (vty, ifp); + if_dump_vty (vty, ifp); return CMD_SUCCESS; } -DEFUN (show_interface_desc, - show_interface_desc_cmd, - "show interface description", +ALIAS (show_interface_name, + show_interface_name_vrf_cmd, + "show interface IFNAME " VRF_CMD_STR, SHOW_STR "Interface status and configuration\n" - "Interface description\n") + "Inteface name\n" + VRF_CMD_HELP_STR) + +/* Show specified interface to vty. */ +DEFUN (show_interface_name_vrf_all, show_interface_name_vrf_all_cmd, + "show interface IFNAME " VRF_ALL_CMD_STR, + SHOW_STR + "Interface status and configuration\n" + "Inteface name\n" + VRF_ALL_CMD_HELP_STR) +{ + struct interface *ifp; + vrf_iter_t iter; + int found = 0; + +#ifdef HAVE_PROC_NET_DEV + /* If system has interface statistics via proc file system, update + statistics. */ + ifstat_update_proc (); +#endif /* HAVE_PROC_NET_DEV */ +#ifdef HAVE_NET_RT_IFLIST + ifstat_update_sysctl (); +#endif /* HAVE_NET_RT_IFLIST */ + + /* All interface print. */ + for (iter = vrf_first (); iter != VRF_ITER_INVALID; iter = vrf_next (iter)) + { + /* Specified interface print. */ + ifp = if_lookup_by_name_vrf (argv[0], vrf_iter2id (iter)); + if (ifp) + { + if_dump_vty (vty, ifp); + found++; + } + } + + if (!found) + { + vty_out (vty, "%% Can't find interface %s%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + + return CMD_SUCCESS; +} + +static void +if_show_description (struct vty *vty, vrf_id_t vrf_id) { struct listnode *node; struct interface *ifp; vty_out (vty, "Interface Status Protocol Description%s", VTY_NEWLINE); - for (ALL_LIST_ELEMENTS_RO (iflist, node, ifp)) + for (ALL_LIST_ELEMENTS_RO (vrf_iflist (vrf_id), node, ifp)) { int len; @@ -964,6 +1261,52 @@ DEFUN (show_interface_desc, vty_out (vty, "%s", ifp->desc); vty_out (vty, "%s", VTY_NEWLINE); } +} + +DEFUN (show_interface_desc, + show_interface_desc_cmd, + "show interface description", + SHOW_STR + "Interface status and configuration\n" + "Interface description\n") +{ + vrf_id_t vrf_id = VRF_DEFAULT; + + if (argc > 0) + VTY_GET_INTEGER ("VRF ID", vrf_id, argv[0]); + + if_show_description (vty, vrf_id); + + return CMD_SUCCESS; +} + +ALIAS (show_interface_desc, + show_interface_desc_vrf_cmd, + "show interface description " VRF_CMD_STR, + SHOW_STR + "Interface status and configuration\n" + "Interface description\n" + VRF_CMD_HELP_STR) + +DEFUN (show_interface_desc_vrf_all, + show_interface_desc_vrf_all_cmd, + "show interface description " VRF_ALL_CMD_STR, + SHOW_STR + "Interface status and configuration\n" + "Interface description\n" + VRF_ALL_CMD_HELP_STR) +{ + vrf_iter_t iter; + + for (iter = vrf_first (); iter != VRF_ITER_INVALID; iter = vrf_next (iter)) + if (!list_isempty (vrf_iter2iflist (iter))) + { + vty_out (vty, "%s\tVRF %u%s%s", VTY_NEWLINE, + vrf_iter2id (iter), + VTY_NEWLINE, VTY_NEWLINE); + if_show_description (vty, vrf_iter2id (iter)); + } + return CMD_SUCCESS; } @@ -1020,21 +1363,65 @@ DEFUN (no_multicast, return CMD_SUCCESS; } +/* Hacky: create a dummy node just to hang a config-writer callback off it */ +static struct cmd_node zebra_if_defaults_node = { + ZEBRA_IF_DEFAULTS_NODE, + "", + 1, +}; + +static int +config_write_zebra_if_defaults (struct vty *vty) +{ + if (zif_defaults.linkdetect != IF_LINKDETECT_UNSPEC) + vty_out (vty, "default link-detect %s%s", + zif_defaults.linkdetect == IF_LINKDETECT_ON ? "on" : "off", + VTY_NEWLINE); + return 0; +} + +DEFUN(default_linkdetect, + default_linkdetect_cmd, + "default link-detect (on|off)", + "Configure defaults of settings\n" + "Interface link detection\n" + "Interface link-detect defaults to enabled\n" + "Interface link-detect defaults to disabled\n") +{ + zebra_if_linkdetect prev = zif_defaults.linkdetect; + struct listnode *node; + struct interface *ifp; + vrf_iter_t iter; + + if (strcmp (argv[1], "on") == 0) + zif_defaults.linkdetect = IF_LINKDETECT_ON; + else + zif_defaults.linkdetect = IF_LINKDETECT_OFF; + + if (zif_defaults.linkdetect != prev) + for (iter = vrf_first (); iter != VRF_ITER_INVALID; iter = vrf_next (iter)) + for (ALL_LIST_ELEMENTS_RO (vrf_iter2iflist (iter), node, ifp)) + if_zebra_linkdetect_set (ifp); + + return CMD_SUCCESS; +} + DEFUN (linkdetect, linkdetect_cmd, - "link-detect", - "Enable link detection on interface\n") + "link-detect [default]", + "Enable link detection on interface\n" + "Leave link-detect to the default\n") { struct interface *ifp; - int if_was_operative; + struct zebra_if *zif; ifp = (struct interface *) vty->index; - if_was_operative = if_is_operative(ifp); - SET_FLAG(ifp->status, ZEBRA_INTERFACE_LINKDETECTION); - - /* When linkdetection is enabled, if might come down */ - if (!if_is_operative(ifp) && if_was_operative) if_down(ifp); - + zif = ifp->info; + assert (zif != NULL); + + zif->linkdetect = IF_LINKDETECT_ON; + if_zebra_linkdetect_set (ifp); + /* FIXME: Will defer status change forwarding if interface does not come down! */ @@ -1049,15 +1436,15 @@ DEFUN (no_linkdetect, "Disable link detection on interface\n") { struct interface *ifp; - int if_was_operative; - + struct zebra_if *zif; + ifp = (struct interface *) vty->index; - if_was_operative = if_is_operative(ifp); - UNSET_FLAG(ifp->status, ZEBRA_INTERFACE_LINKDETECTION); + zif = ifp->info; + assert (zif != NULL); + + zif->linkdetect = IF_LINKDETECT_OFF; + if_zebra_linkdetect_set (ifp); - /* Interface may come up after disabling link detection */ - if (if_is_operative(ifp) && !if_was_operative) if_up(ifp); - /* FIXME: see linkdetect_cmd */ return CMD_SUCCESS; @@ -1073,13 +1460,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 +1487,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,16 +1563,715 @@ 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 cmd_node link_params_node = { - struct prefix_ipv4 cp; - struct connected *ifc; - struct prefix_ipv4 *p; - int ret; + LINK_PARAMS_NODE, + "%s(config-link-params)# ", + 1, +}; + +static void +link_param_cmd_set_uint32 (struct interface *ifp, uint32_t *field, + uint32_t type, uint32_t value) +{ + /* Update field as needed */ + if (IS_PARAM_UNSET(ifp->link_params, type) || *field != value) + { + *field = value; + SET_PARAM(ifp->link_params, type); + + /* force protocols to update LINK STATE due to parameters change */ + if (if_is_operative (ifp)) + zebra_interface_parameters_update (ifp); + } +} +static void +link_param_cmd_set_float (struct interface *ifp, float *field, + uint32_t type, float value) +{ + + /* Update field as needed */ + if (IS_PARAM_UNSET(ifp->link_params, type) || *field != value) + { + *field = value; + SET_PARAM(ifp->link_params, type); + + /* force protocols to update LINK STATE due to parameters change */ + if (if_is_operative (ifp)) + zebra_interface_parameters_update (ifp); + } +} + +static void +link_param_cmd_unset (struct interface *ifp, uint32_t type) +{ + + /* Unset field */ + UNSET_PARAM(ifp->link_params, type); + + /* force protocols to update LINK STATE due to parameters change */ + if (if_is_operative (ifp)) + zebra_interface_parameters_update (ifp); +} + +DEFUN (link_params, + link_params_cmd, + "link-params", + LINK_PARAMS_STR) +{ + vty->node = LINK_PARAMS_NODE; + + return CMD_SUCCESS; +} + +DEFUN (exit_link_params, + exit_link_params_cmd, + "exit-link-params", + "Exit from Link Params configuration mode\n") +{ + if (vty->node == LINK_PARAMS_NODE) + vty->node = INTERFACE_NODE; + return CMD_SUCCESS; +} + +/* Specific Traffic Engineering parameters commands */ +DEFUN (link_params_enable, + link_params_enable_cmd, + "enable", + "Activate link parameters on this interface\n") +{ + struct interface *ifp = (struct interface *) vty->index; + + /* This command could be issue at startup, when activate MPLS TE */ + /* on a new interface or after a ON / OFF / ON toggle */ + /* In all case, TE parameters are reset to their default factory */ + if (IS_ZEBRA_DEBUG_EVENT) + zlog_debug ("Link-params: enable TE link parameters on interface %s", ifp->name); + + if (!if_link_params_get (ifp)) + { + if (IS_ZEBRA_DEBUG_EVENT) + zlog_debug ("Link-params: failed to init TE link parameters %s", ifp->name); + + return CMD_WARNING; + } + + /* force protocols to update LINK STATE due to parameters change */ + if (if_is_operative (ifp)) + zebra_interface_parameters_update (ifp); + + return CMD_SUCCESS; +} + +DEFUN (no_link_params_enable, + no_link_params_enable_cmd, + "no enable", + NO_STR + "Disable link parameters on this interface\n") +{ + struct interface *ifp = (struct interface *) vty->index; + + zlog_debug ("MPLS-TE: disable TE link parameters on interface %s", ifp->name); + + if_link_params_free (ifp); + + /* force protocols to update LINK STATE due to parameters change */ + if (if_is_operative (ifp)) + zebra_interface_parameters_update (ifp); + + return CMD_SUCCESS; +} + +/* STANDARD TE metrics */ +DEFUN (link_params_metric, + link_params_metric_cmd, + "metric <0-4294967295>", + "Link metric for MPLS-TE purpose\n" + "Metric value in decimal\n") +{ + struct interface *ifp = (struct interface *) vty->index; + struct if_link_params *iflp = if_link_params_get (ifp); + u_int32_t metric; + + VTY_GET_ULONG("metric", metric, argv[0]); + + /* Update TE metric if needed */ + link_param_cmd_set_uint32 (ifp, &iflp->te_metric, LP_TE, metric); + + return CMD_SUCCESS; +} + +DEFUN (no_link_params_metric, + no_link_params_metric_cmd, + "no metric", + NO_STR + "Disbale Link Metric on this interface\n") +{ + struct interface *ifp = (struct interface *) vty->index; + + /* Unset TE Metric */ + link_param_cmd_unset(ifp, LP_TE); + + return CMD_SUCCESS; +} + +DEFUN (link_params_maxbw, + link_params_maxbw_cmd, + "max-bw BANDWIDTH", + "Maximum bandwidth that can be used\n" + "Bytes/second (IEEE floating point format)\n") +{ + struct interface *ifp = (struct interface *) vty->index; + struct if_link_params *iflp = if_link_params_get (ifp); + + float bw; + + if (sscanf (argv[0], "%g", &bw) != 1) + { + vty_out (vty, "link_params_maxbw: fscanf: %s%s", safe_strerror (errno), + VTY_NEWLINE); + return CMD_WARNING; + } + + /* Check that Maximum bandwidth is not lower than other bandwidth parameters */ + if ((bw <= iflp->max_rsv_bw) + || (bw <= iflp->unrsv_bw[0]) + || (bw <= iflp->unrsv_bw[1]) + || (bw <= iflp->unrsv_bw[2]) + || (bw <= iflp->unrsv_bw[3]) + || (bw <= iflp->unrsv_bw[4]) + || (bw <= iflp->unrsv_bw[5]) + || (bw <= iflp->unrsv_bw[6]) + || (bw <= iflp->unrsv_bw[7]) + || (bw <= iflp->ava_bw) + || (bw <= iflp->res_bw) + || (bw <= iflp->use_bw)) + { + vty_out (vty, + "Maximum Bandwidth could not be lower than others bandwidth%s", + VTY_NEWLINE); + return CMD_WARNING; + } + + /* Update Maximum Bandwidth if needed */ + link_param_cmd_set_float (ifp, &iflp->max_bw, LP_MAX_BW, bw); + + return CMD_SUCCESS; +} + +DEFUN (link_params_max_rsv_bw, + link_params_max_rsv_bw_cmd, + "max-rsv-bw BANDWIDTH", + "Maximum bandwidth that may be reserved\n" + "Bytes/second (IEEE floating point format)\n") +{ + struct interface *ifp = (struct interface *) vty->index; + struct if_link_params *iflp = if_link_params_get (ifp); + float bw; + + if (sscanf (argv[0], "%g", &bw) != 1) + { + vty_out (vty, "link_params_max_rsv_bw: fscanf: %s%s", safe_strerror (errno), + VTY_NEWLINE); + return CMD_WARNING; + } + + /* Check that bandwidth is not greater than maximum bandwidth parameter */ + if (bw > iflp->max_bw) + { + vty_out (vty, + "Maximum Reservable Bandwidth could not be greater than Maximum Bandwidth (%g)%s", + iflp->max_bw, VTY_NEWLINE); + return CMD_WARNING; + } + + /* Update Maximum Reservable Bandwidth if needed */ + link_param_cmd_set_float (ifp, &iflp->max_rsv_bw, LP_MAX_RSV_BW, bw); + + return CMD_SUCCESS; +} + +DEFUN (link_params_unrsv_bw, + link_params_unrsv_bw_cmd, + "unrsv-bw <0-7> BANDWIDTH", + "Unreserved bandwidth at each priority level\n" + "Priority\n" + "Bytes/second (IEEE floating point format)\n") +{ + struct interface *ifp = (struct interface *) vty->index; + struct if_link_params *iflp = if_link_params_get (ifp); + int priority; + float bw; + + /* We don't have to consider about range check here. */ + if (sscanf (argv[0], "%d", &priority) != 1) + { + vty_out (vty, "link_params_unrsv_bw: fscanf: %s%s", safe_strerror (errno), + VTY_NEWLINE); + return CMD_WARNING; + } + + if (sscanf (argv[1], "%g", &bw) != 1) + { + vty_out (vty, "link_params_unrsv_bw: fscanf: %s%s", safe_strerror (errno), + VTY_NEWLINE); + return CMD_WARNING; + } + + /* Check that bandwidth is not greater than maximum bandwidth parameter */ + if (bw > iflp->max_bw) + { + vty_out (vty, + "UnReserved Bandwidth could not be greater than Maximum Bandwidth (%g)%s", + iflp->max_bw, VTY_NEWLINE); + return CMD_WARNING; + } + + /* Update Unreserved Bandwidth if needed */ + link_param_cmd_set_float (ifp, &iflp->unrsv_bw[priority], LP_UNRSV_BW, bw); + + return CMD_SUCCESS; +} + +DEFUN (link_params_admin_grp, + link_params_admin_grp_cmd, + "admin-grp BITPATTERN", + "Administrative group membership\n" + "32-bit Hexadecimal value (e.g. 0xa1)\n") +{ + struct interface *ifp = (struct interface *) vty->index; + struct if_link_params *iflp = if_link_params_get (ifp); + unsigned long value; + + if (sscanf (argv[0], "0x%lx", &value) != 1) + { + vty_out (vty, "link_params_admin_grp: fscanf: %s%s", + safe_strerror (errno), VTY_NEWLINE); + return CMD_WARNING; + } + + /* Update Administrative Group if needed */ + link_param_cmd_set_uint32 (ifp, &iflp->admin_grp, LP_ADM_GRP, value); + + return CMD_SUCCESS; +} + +DEFUN (no_link_params_admin_grp, + no_link_params_admin_grp_cmd, + "no admin-grp", + NO_STR + "Disbale Administrative group membership on this interface\n") +{ + struct interface *ifp = (struct interface *) vty->index; + + /* Unset Admin Group */ + link_param_cmd_unset(ifp, LP_ADM_GRP); + + return CMD_SUCCESS; +} + +/* RFC5392 & RFC5316: INTER-AS */ +DEFUN (link_params_inter_as, + link_params_inter_as_cmd, + "neighbor A.B.C.D as <1-4294967295>", + "Configure remote ASBR information (Neighbor IP address and AS number)\n" + "Remote IP address in dot decimal A.B.C.D\n" + "Remote AS number\n" + "AS number in the range <1-4294967295>\n") +{ + + struct interface *ifp = (struct interface *) vty->index; + struct if_link_params *iflp = if_link_params_get (ifp); + struct in_addr addr; + u_int32_t as; + + if (!inet_aton (argv[0], &addr)) + { + vty_out (vty, "Please specify Router-Addr by A.B.C.D%s", VTY_NEWLINE); + return CMD_WARNING; + } + + VTY_GET_ULONG("AS number", as, argv[1]); + + /* Update Remote IP and Remote AS fields if needed */ + if (IS_PARAM_UNSET(iflp, LP_RMT_AS) + || iflp->rmt_as != as + || iflp->rmt_ip.s_addr != addr.s_addr) + { + + iflp->rmt_as = as; + iflp->rmt_ip.s_addr = addr.s_addr; + SET_PARAM(iflp, LP_RMT_AS); + + /* force protocols to update LINK STATE due to parameters change */ + if (if_is_operative (ifp)) + zebra_interface_parameters_update (ifp); + } + return CMD_SUCCESS; +} + +DEFUN (no_link_params_inter_as, + no_link_params_inter_as_cmd, + "no neighbor", + NO_STR + "Remove Neighbor IP address and AS number for Inter-AS TE\n") +{ + + struct interface *ifp = (struct interface *) vty->index; + struct if_link_params *iflp = if_link_params_get (ifp); + + /* Reset Remote IP and AS neighbor */ + iflp->rmt_as = 0; + iflp->rmt_ip.s_addr = 0; + UNSET_PARAM(iflp, LP_RMT_AS); + + /* force protocols to update LINK STATE due to parameters change */ + if (if_is_operative (ifp)) + zebra_interface_parameters_update (ifp); + + return CMD_SUCCESS; +} + +/* RFC7471: OSPF Traffic Engineering (TE) Metric extensions & draft-ietf-isis-metric-extensions-07.txt */ +DEFUN (link_params_delay, + link_params_delay_cmd, + "delay <0-16777215>", + "Unidirectional Average Link Delay\n" + "Average delay in micro-second as decimal (0...16777215)\n") +{ + + struct interface *ifp = (struct interface *) vty->index; + struct if_link_params *iflp = if_link_params_get (ifp); + u_int32_t delay = 0, low = 0, high = 0; + u_int8_t update = 0; + + /* Get and Check new delay values */ + VTY_GET_ULONG("delay", delay, argv[0]); + switch (argc) + { + case 1: + /* Check new delay value against old Min and Max delays if set */ + if (IS_PARAM_SET(iflp, LP_MM_DELAY) + && (delay <= iflp->min_delay || delay >= iflp->max_delay)) + { + vty_out (vty, "Average delay should be comprise between Min (%d) and Max (%d) delay%s", + iflp->min_delay, iflp->max_delay, VTY_NEWLINE); + return CMD_WARNING; + } + /* Update delay if value is not set or change */ + if (IS_PARAM_UNSET(iflp, LP_DELAY)|| iflp->av_delay != delay) + { + iflp->av_delay = delay; + SET_PARAM(iflp, LP_DELAY); + update = 1; + } + /* Unset Min and Max delays if already set */ + if (IS_PARAM_SET(iflp, LP_MM_DELAY)) + { + iflp->min_delay = 0; + iflp->max_delay = 0; + UNSET_PARAM(iflp, LP_MM_DELAY); + update = 1; + } + break; + case 2: + vty_out (vty, "You should specify both Minimum and Maximum delay with Average delay%s", + VTY_NEWLINE); + return CMD_WARNING; + break; + case 3: + VTY_GET_ULONG("minimum delay", low, argv[1]); + VTY_GET_ULONG("maximum delay", high, argv[2]); + /* Check new delays value coherency */ + if (delay <= low || delay >= high) + { + vty_out (vty, "Average delay should be comprise between Min (%d) and Max (%d) delay%s", + low, high, VTY_NEWLINE); + return CMD_WARNING; + } + /* Update Delays if needed */ + if (IS_PARAM_UNSET(iflp, LP_DELAY) + || IS_PARAM_UNSET(iflp, LP_MM_DELAY) + || iflp->av_delay != delay + || iflp->min_delay != low + || iflp->max_delay != high) + { + iflp->av_delay = delay; + SET_PARAM(iflp, LP_DELAY); + iflp->min_delay = low; + iflp->max_delay = high; + SET_PARAM(iflp, LP_MM_DELAY); + update = 1; + } + break; + default: + return CMD_WARNING; + break; + } + + /* force protocols to update LINK STATE due to parameters change */ + if (update == 1 && if_is_operative (ifp)) + zebra_interface_parameters_update (ifp); + + return CMD_SUCCESS; +} + +ALIAS (link_params_delay, + link_params_delay_mm_cmd, + "delay <0-16777215> min <0-16777215> max <0-16777215>", + "Unidirectional Average Link Delay (optionally Minimum and Maximum delays)\n" + "Average delay in micro-second as decimal (0...16777215)\n" + "Minimum delay\n" + "Minimum delay in micro-second as decimal (0...16777215)\n" + "Maximum delay\n" + "Maximum delay in micro-second as decimal (0...16777215)\n") + +DEFUN (no_link_params_delay, + no_link_params_delay_cmd, + "no delay", + NO_STR + "Disbale Unidirectional Average, Min & Max Link Delay on this interface\n") +{ + struct interface *ifp = (struct interface *) vty->index; + struct if_link_params *iflp = if_link_params_get (ifp); + + /* Unset Delays */ + iflp->av_delay = 0; + UNSET_PARAM(iflp, LP_DELAY); + iflp->min_delay = 0; + iflp->max_delay = 0; + UNSET_PARAM(iflp, LP_MM_DELAY); + + /* force protocols to update LINK STATE due to parameters change */ + if (if_is_operative (ifp)) + zebra_interface_parameters_update (ifp); + + return CMD_SUCCESS; +} + +DEFUN (link_params_delay_var, + link_params_delay_var_cmd, + "delay-variation <0-16777215>", + "Unidirectional Link Delay Variation\n" + "delay variation in micro-second as decimal (0...16777215)\n") +{ + struct interface *ifp = (struct interface *) vty->index; + struct if_link_params *iflp = if_link_params_get (ifp); + u_int32_t value; + + VTY_GET_ULONG("delay variation", value, argv[0]); + + /* Update Delay Variation if needed */ + link_param_cmd_set_uint32 (ifp, &iflp->delay_var, LP_DELAY_VAR, value); + + return CMD_SUCCESS; +} + +DEFUN (no_link_params_delay_var, + no_link_params_delay_var_cmd, + "no delay-variation", + NO_STR + "Disbale Unidirectional Delay Variation on this interface\n") +{ + struct interface *ifp = (struct interface *) vty->index; + + /* Unset Delay Variation */ + link_param_cmd_unset(ifp, LP_DELAY_VAR); + + return CMD_SUCCESS; +} + +DEFUN (link_params_pkt_loss, + link_params_pkt_loss_cmd, + "packet-loss PERCENTAGE", + "Unidirectional Link Packet Loss\n" + "percentage of total traffic by 0.000003% step and less than 50.331642%\n") +{ + struct interface *ifp = (struct interface *) vty->index; + struct if_link_params *iflp = if_link_params_get (ifp); + float fval; + + if (sscanf (argv[0], "%g", &fval) != 1) + { + vty_out (vty, "link_params_pkt_loss: fscanf: %s%s", safe_strerror (errno), + VTY_NEWLINE); + return CMD_WARNING; + } + + if (fval > MAX_PKT_LOSS) + fval = MAX_PKT_LOSS; + + /* Update Packet Loss if needed */ + link_param_cmd_set_float (ifp, &iflp->pkt_loss, LP_PKT_LOSS, fval); + + return CMD_SUCCESS; +} + +DEFUN (no_link_params_pkt_loss, + no_link_params_pkt_loss_cmd, + "no packet-loss", + NO_STR + "Disbale Unidirectional Link Packet Loss on this interface\n") +{ + struct interface *ifp = (struct interface *) vty->index; + + /* Unset Packet Loss */ + link_param_cmd_unset(ifp, LP_PKT_LOSS); + + return CMD_SUCCESS; +} + +DEFUN (link_params_res_bw, + link_params_res_bw_cmd, + "res-bw BANDWIDTH", + "Unidirectional Residual Bandwidth\n" + "Bytes/second (IEEE floating point format)\n") +{ + struct interface *ifp = (struct interface *) vty->index; + struct if_link_params *iflp = if_link_params_get (ifp); + float bw; + + if (sscanf (argv[0], "%g", &bw) != 1) + { + vty_out (vty, "link_params_res_bw: fscanf: %s%s", safe_strerror (errno), + VTY_NEWLINE); + return CMD_WARNING; + } + + /* Check that bandwidth is not greater than maximum bandwidth parameter */ + if (bw > iflp->max_bw) + { + vty_out (vty, + "Residual Bandwidth could not be greater than Maximum Bandwidth (%g)%s", + iflp->max_bw, VTY_NEWLINE); + return CMD_WARNING; + } + + /* Update Residual Bandwidth if needed */ + link_param_cmd_set_float (ifp, &iflp->res_bw, LP_RES_BW, bw); + + return CMD_SUCCESS; +} + +DEFUN (no_link_params_res_bw, + no_link_params_res_bw_cmd, + "no res-bw", + NO_STR + "Disbale Unidirectional Residual Bandwidth on this interface\n") +{ + struct interface *ifp = (struct interface *) vty->index; + + /* Unset Residual Bandwidth */ + link_param_cmd_unset(ifp, LP_RES_BW); + + return CMD_SUCCESS; +} + +DEFUN (link_params_ava_bw, + link_params_ava_bw_cmd, + "ava-bw BANDWIDTH", + "Unidirectional Available Bandwidth\n" + "Bytes/second (IEEE floating point format)\n") +{ + struct interface *ifp = (struct interface *) vty->index; + struct if_link_params *iflp = if_link_params_get (ifp); + float bw; + + if (sscanf (argv[0], "%g", &bw) != 1) + { + vty_out (vty, "link_params_ava_bw: fscanf: %s%s", safe_strerror (errno), + VTY_NEWLINE); + return CMD_WARNING; + } + + /* Check that bandwidth is not greater than maximum bandwidth parameter */ + if (bw > iflp->max_bw) + { + vty_out (vty, + "Available Bandwidth could not be greater than Maximum Bandwidth (%g)%s", + iflp->max_bw, VTY_NEWLINE); + return CMD_WARNING; + } + + /* Update Residual Bandwidth if needed */ + link_param_cmd_set_float (ifp, &iflp->ava_bw, LP_AVA_BW, bw); + + return CMD_SUCCESS; +} + +DEFUN (no_link_params_ava_bw, + no_link_params_ava_bw_cmd, + "no ava-bw", + NO_STR + "Disbale Unidirectional Available Bandwidth on this interface\n") +{ + struct interface *ifp = (struct interface *) vty->index; + + /* Unset Available Bandwidth */ + link_param_cmd_unset(ifp, LP_AVA_BW); + + return CMD_SUCCESS; +} + +DEFUN (link_params_use_bw, + link_params_use_bw_cmd, + "use-bw BANDWIDTH", + "Unidirectional Utilised Bandwidth\n" + "Bytes/second (IEEE floating point format)\n") +{ + struct interface *ifp = (struct interface *) vty->index; + struct if_link_params *iflp = if_link_params_get (ifp); + float bw; + + if (sscanf (argv[0], "%g", &bw) != 1) + { + vty_out (vty, "link_params_use_bw: fscanf: %s%s", safe_strerror (errno), + VTY_NEWLINE); + return CMD_WARNING; + } + + /* Check that bandwidth is not greater than maximum bandwidth parameter */ + if (bw > iflp->max_bw) + { + vty_out (vty, + "Utilised Bandwidth could not be greater than Maximum Bandwidth (%g)%s", + iflp->max_bw, VTY_NEWLINE); + return CMD_WARNING; + } + + /* Update Utilized Bandwidth if needed */ + link_param_cmd_set_float (ifp, &iflp->use_bw, LP_USE_BW, bw); + + return CMD_SUCCESS; +} + +DEFUN (no_link_params_use_bw, + no_link_params_use_bw_cmd, + "no use-bw", + NO_STR + "Disbale Unidirectional Utilised Bandwidth on this interface\n") +{ + struct interface *ifp = (struct interface *) vty->index; + + /* Unset Utilised Bandwidth */ + link_param_cmd_unset(ifp, LP_USE_BW); + + return CMD_SUCCESS; +} + +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 +2313,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 +2332,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 +2373,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,19 +2389,9 @@ ip_address_uninstall (struct vty *vty, struct interface *ifp, safe_strerror(errno), VTY_NEWLINE); return CMD_WARNING; } - -#if 0 - /* Redistribute this information. */ - zebra_interface_address_delete_update (ifp, ifc); - - /* Remove connected route. */ - connected_down_ipv4 (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; } @@ -1367,11 +2449,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) { @@ -1407,8 +2492,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)) @@ -1426,15 +2512,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; @@ -1469,8 +2549,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); @@ -1487,16 +2569,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; } @@ -1522,13 +2597,68 @@ DEFUN (no_ipv6_address, } #endif /* HAVE_IPV6 */ +static int +link_params_config_write (struct vty *vty, struct interface *ifp) +{ + int i; + + if ((ifp == NULL) || !HAS_LINK_PARAMS(ifp)) + return -1; + + struct if_link_params *iflp = ifp->link_params; + + vty_out (vty, " link-params%s", VTY_NEWLINE); + vty_out(vty, " enable%s", VTY_NEWLINE); + if (IS_PARAM_SET(iflp, LP_TE)) + vty_out(vty, " metric %u%s",iflp->te_metric, VTY_NEWLINE); + if (IS_PARAM_SET(iflp, LP_MAX_BW)) + vty_out(vty, " max-bw %g%s", iflp->max_bw, VTY_NEWLINE); + if (IS_PARAM_SET(iflp, LP_MAX_RSV_BW)) + vty_out(vty, " max-rsv-bw %g%s", iflp->max_rsv_bw, VTY_NEWLINE); + if (IS_PARAM_SET(iflp, LP_UNRSV_BW)) + { + for (i = 0; i < 8; i++) + vty_out(vty, " unrsv-bw %d %g%s", + i, iflp->unrsv_bw[i], VTY_NEWLINE); + } + if (IS_PARAM_SET(iflp, LP_ADM_GRP)) + vty_out(vty, " admin-grp %u%s", iflp->admin_grp, VTY_NEWLINE); + if (IS_PARAM_SET(iflp, LP_DELAY)) + { + vty_out(vty, " delay %u", iflp->av_delay); + if (IS_PARAM_SET(iflp, LP_MM_DELAY)) + { + vty_out(vty, " min %u", iflp->min_delay); + vty_out(vty, " max %u", iflp->max_delay); + } + vty_out(vty, "%s", VTY_NEWLINE); + } + if (IS_PARAM_SET(iflp, LP_DELAY_VAR)) + vty_out(vty, " delay-variation %u%s", iflp->delay_var, VTY_NEWLINE); + if (IS_PARAM_SET(iflp, LP_PKT_LOSS)) + vty_out(vty, " packet-loss %g%s", iflp->pkt_loss, VTY_NEWLINE); + if (IS_PARAM_SET(iflp, LP_AVA_BW)) + vty_out(vty, " ava-bw %g%s", iflp->ava_bw, VTY_NEWLINE); + if (IS_PARAM_SET(iflp, LP_RES_BW)) + vty_out(vty, " res-bw %g%s", iflp->res_bw, VTY_NEWLINE); + if (IS_PARAM_SET(iflp, LP_USE_BW)) + vty_out(vty, " use-bw %g%s", iflp->use_bw, VTY_NEWLINE); + if (IS_PARAM_SET(iflp, LP_RMT_AS)) + vty_out(vty, " neighbor %s as %u%s", inet_ntoa(iflp->rmt_ip), + iflp->rmt_as, VTY_NEWLINE); + vty_out(vty, " exit-link-params%s", VTY_NEWLINE); + return 0; +} + static int if_config_write (struct vty *vty) { struct listnode *node; struct interface *ifp; + vrf_iter_t iter; - for (ALL_LIST_ELEMENTS_RO (iflist, node, ifp)) + for (iter = vrf_first (); iter != VRF_ITER_INVALID; iter = vrf_next (iter)) + for (ALL_LIST_ELEMENTS_RO (vrf_iter2iflist (iter), node, ifp)) { struct zebra_if *if_data; struct listnode *addrnode; @@ -1536,9 +2666,18 @@ if_config_write (struct vty *vty) struct prefix *p; if_data = ifp->info; - - vty_out (vty, "interface %s%s", ifp->name, - VTY_NEWLINE); + + if (ifp->vrf_id == VRF_DEFAULT) + vty_out (vty, "interface %s%s", ifp->name, VTY_NEWLINE); + else + vty_out (vty, "interface %s vrf %u%s", ifp->name, ifp->vrf_id, + 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, @@ -1548,20 +2687,27 @@ if_config_write (struct vty *vty) while processing config script */ if (ifp->bandwidth != 0) vty_out(vty, " bandwidth %u%s", ifp->bandwidth, VTY_NEWLINE); - - if (CHECK_FLAG(ifp->status, ZEBRA_INTERFACE_LINKDETECTION)) - vty_out(vty, " link-detect%s", VTY_NEWLINE); - + + switch (if_data->linkdetect) + { + case IF_LINKDETECT_ON: + vty_out(vty, " link-detect%s", VTY_NEWLINE); + break; + case IF_LINKDETECT_OFF: + vty_out(vty, " no link-detect%s", VTY_NEWLINE); + break; + default: break; + } + for (ALL_LIST_ELEMENTS_RO (ifp->connected, addrnode, ifc)) { if (CHECK_FLAG (ifc->conf, ZEBRA_IFC_CONFIGURED)) { char buf[INET6_ADDRSTRLEN]; p = ifc->address; - vty_out (vty, " ip%s address %s/%d", + vty_out (vty, " ip%s address %s", p->family == AF_INET ? "" : "v6", - inet_ntop (p->family, &p->u.prefix, buf, sizeof(buf)), - p->prefixlen); + prefix2str (p, buf, sizeof(buf))); if (ifc->label) vty_out (vty, " label %s", ifc->label); @@ -1572,45 +2718,54 @@ 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 ", VTY_NEWLINE); } -#ifdef RTADV +#if defined (HAVE_RTADV) rtadv_config_write (vty, ifp); -#endif /* RTADV */ +#endif /* HAVE_RTADV */ #ifdef HAVE_IRDP irdp_config_write (vty, ifp); #endif /* IRDP */ + link_params_config_write (vty, ifp); + vty_out (vty, "!%s", VTY_NEWLINE); } return 0; } + /* Allocate and initialize interface vector. */ void zebra_if_init (void) { /* Initialize interface and new hook. */ - if_init (); if_add_hook (IF_NEW_HOOK, if_zebra_new_hook); if_add_hook (IF_DELETE_HOOK, if_zebra_delete_hook); /* Install configuration write function. */ install_node (&interface_node, if_config_write); + + install_node (&zebra_if_defaults_node, config_write_zebra_if_defaults); + install_node (&link_params_node, NULL); + install_element (VIEW_NODE, &show_interface_cmd); - install_element (ENABLE_NODE, &show_interface_cmd); - install_element (ENABLE_NODE, &show_interface_desc_cmd); + install_element (VIEW_NODE, &show_interface_vrf_cmd); + install_element (VIEW_NODE, &show_interface_vrf_all_cmd); + install_element (VIEW_NODE, &show_interface_name_cmd); + install_element (VIEW_NODE, &show_interface_name_vrf_cmd); + install_element (VIEW_NODE, &show_interface_name_vrf_all_cmd); install_element (CONFIG_NODE, &zebra_interface_cmd); + install_element (CONFIG_NODE, &zebra_interface_vrf_cmd); install_element (CONFIG_NODE, &no_interface_cmd); + install_element (CONFIG_NODE, &no_interface_vrf_cmd); + install_element (CONFIG_NODE, &default_linkdetect_cmd); install_default (INTERFACE_NODE); install_element (INTERFACE_NODE, &interface_desc_cmd); install_element (INTERFACE_NODE, &no_interface_desc_cmd); @@ -1633,4 +2788,30 @@ zebra_if_init (void) install_element (INTERFACE_NODE, &ip_address_label_cmd); install_element (INTERFACE_NODE, &no_ip_address_label_cmd); #endif /* HAVE_NETLINK */ + install_element(INTERFACE_NODE, &link_params_cmd); + install_default(LINK_PARAMS_NODE); + install_element(LINK_PARAMS_NODE, &link_params_enable_cmd); + install_element(LINK_PARAMS_NODE, &no_link_params_enable_cmd); + install_element(LINK_PARAMS_NODE, &link_params_metric_cmd); + install_element(LINK_PARAMS_NODE, &link_params_maxbw_cmd); + install_element(LINK_PARAMS_NODE, &link_params_max_rsv_bw_cmd); + install_element(LINK_PARAMS_NODE, &link_params_unrsv_bw_cmd); + install_element(LINK_PARAMS_NODE, &link_params_admin_grp_cmd); + install_element(LINK_PARAMS_NODE, &no_link_params_admin_grp_cmd); + install_element(LINK_PARAMS_NODE, &link_params_inter_as_cmd); + install_element(LINK_PARAMS_NODE, &no_link_params_inter_as_cmd); + install_element(LINK_PARAMS_NODE, &link_params_delay_cmd); + install_element(LINK_PARAMS_NODE, &no_link_params_delay_cmd); + install_element(LINK_PARAMS_NODE, &link_params_delay_mm_cmd); + install_element(LINK_PARAMS_NODE, &link_params_delay_var_cmd); + install_element(LINK_PARAMS_NODE, &no_link_params_delay_var_cmd); + install_element(LINK_PARAMS_NODE, &link_params_pkt_loss_cmd); + install_element(LINK_PARAMS_NODE, &no_link_params_pkt_loss_cmd); + install_element(LINK_PARAMS_NODE, &link_params_ava_bw_cmd); + install_element(LINK_PARAMS_NODE, &no_link_params_ava_bw_cmd); + install_element(LINK_PARAMS_NODE, &link_params_res_bw_cmd); + install_element(LINK_PARAMS_NODE, &no_link_params_res_bw_cmd); + install_element(LINK_PARAMS_NODE, &link_params_use_bw_cmd); + install_element(LINK_PARAMS_NODE, &no_link_params_use_bw_cmd); + install_element(LINK_PARAMS_NODE, &exit_link_params_cmd); } diff --git a/zebra/interface.h b/zebra/interface.h index 0777a2fa3..223caf80f 100644 --- a/zebra/interface.h +++ b/zebra/interface.h @@ -23,6 +23,7 @@ #define _ZEBRA_INTERFACE_H #include "redistribute.h" +#include "event_counter.h" #ifdef HAVE_IRDP #include "zebra/irdp.h" @@ -34,18 +35,23 @@ #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) - #ifdef HAVE_RTADV - #define RTADV - #endif -#endif +/* Global user-configured default for interface link-detect */ +typedef enum { + IF_LINKDETECT_UNSPEC = 0, + IF_LINKDETECT_ON, + IF_LINKDETECT_OFF, +} zebra_if_linkdetect; + +/* Global defaults for interfaces */ +struct zebra_if_defaults { + /* Link-detect default configuration */ + zebra_if_linkdetect linkdetect; +}; -#ifdef RTADV +#if defined (HAVE_RTADV) /* Router advertisement parameter. From RFC4861, RFC6275 and RFC4191. */ struct rtadvconf { @@ -179,7 +185,7 @@ struct rtadvconf #define RTADV_PREF_MEDIUM 0x0 /* Per RFC4191. */ }; -#endif /* RTADV */ +#endif /* HAVE_RTADV */ /* `zebra' daemon local interface structure. */ struct zebra_if @@ -192,11 +198,18 @@ struct zebra_if /* Router advertise configuration. */ u_char rtadv_enable; - + + /* Interface specific link-detect configuration state */ + zebra_if_linkdetect linkdetect; + /* Installed addresses chains tree. */ struct route_table *ipv4_subnets; -#ifdef RTADV + /* Information about up/down changes */ + struct event_counter up_events; + struct event_counter down_events; + +#if defined(HAVE_RTADV) struct rtadvconf rtadv; #endif /* RTADV */ @@ -204,6 +217,16 @@ struct zebra_if struct irdp_interface irdp; #endif +#ifdef HAVE_STRUCT_SOCKADDR_DL + 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; + }; +#endif + #ifdef SUNOS_5 /* the real IFF_UP state of the primary interface. * need this to differentiate between all interfaces being @@ -220,6 +243,7 @@ extern void if_up (struct interface *); extern void if_down (struct interface *); extern void if_refresh (struct interface *); extern void if_flags_update (struct interface *, uint64_t); +extern void if_startup_count_up (void); extern int if_subnet_add (struct interface *, struct connected *); extern int if_subnet_delete (struct interface *, struct connected *); @@ -237,8 +261,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.c b/zebra/ioctl.c index d783b0a38..e1ee4290c 100644 --- a/zebra/ioctl.c +++ b/zebra/ioctl.c @@ -52,7 +52,7 @@ if_ioctl (u_long request, caddr_t buffer) { int sock; int ret; - int err; + int err = 0; if (zserv_privs.change(ZPRIVS_RAISE)) zlog (NULL, LOG_ERR, "Can't raise privileges"); @@ -85,7 +85,7 @@ if_ioctl_ipv6 (u_long request, caddr_t buffer) { int sock; int ret; - int err; + int err = 0; if (zserv_privs.change(ZPRIVS_RAISE)) zlog (NULL, LOG_ERR, "Can't raise privileges"); @@ -196,7 +196,6 @@ if_set_prefix (struct interface *ifp, struct connected *ifc) struct prefix_ipv4 *p; p = (struct prefix_ipv4 *) ifc->address; - rib_lookup_and_pushup (p); memset (&addreq, 0, sizeof addreq); strncpy ((char *)&addreq.ifra_name, ifp->name, sizeof addreq.ifra_name); diff --git a/zebra/ioctl_null.c b/zebra/ioctl_null.c index 6d8e13a06..5a8be991d 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/ioctl_solaris.c b/zebra/ioctl_solaris.c index 6c1c254a7..b5bf1ccb0 100644 --- a/zebra/ioctl_solaris.c +++ b/zebra/ioctl_solaris.c @@ -28,10 +28,12 @@ #include "ioctl.h" #include "log.h" #include "privs.h" +#include "vty.h" #include "zebra/rib.h" #include "zebra/rt.h" #include "zebra/interface.h" +#include "zebra/ioctl_solaris.h" extern struct zebra_privs_t zserv_privs; @@ -309,7 +311,7 @@ if_get_flags_direct (const char *ifname, uint64_t *flags, unsigned int af) void if_get_flags (struct interface *ifp) { - int ret4, ret6; + int ret4 = 0, ret6 = 0; uint64_t newflags = 0; uint64_t tmpflags; @@ -407,11 +409,11 @@ if_unset_flags (struct interface *ifp, uint64_t flags) int if_prefix_add_ipv6 (struct interface *ifp, struct connected *ifc) { - char addrbuf[INET_ADDRSTRLEN]; + char addrbuf[PREFIX_STRLEN]; - inet_ntop (AF_INET6, &(((struct prefix_ipv6 *) (ifc->address))->prefix), - addrbuf, sizeof (addrbuf)); - zlog_warn ("Can't set %s on interface %s", addrbuf, ifp->name); + zlog_warn ("Can't set %s on interface %s", + prefix2str(ifc->address, addrbuf, sizeof(addrbuf)), + ifp->name); return 0; @@ -420,11 +422,11 @@ if_prefix_add_ipv6 (struct interface *ifp, struct connected *ifc) int if_prefix_delete_ipv6 (struct interface *ifp, struct connected *ifc) { - char addrbuf[INET_ADDRSTRLEN]; + char addrbuf[PREFIX_STRLEN]; - inet_ntop (AF_INET6, &(((struct prefix_ipv6 *) (ifc->address))->prefix), - addrbuf, sizeof (addrbuf)); - zlog_warn ("Can't delete %s on interface %s", addrbuf, ifp->name); + zlog_warn ("Can't delete %s on interface %s", + prefix2str(ifc->address, addrbuf, sizeof(addrbuf)), + ifp->name); return 0; diff --git a/zebra/ioctl_solaris.h b/zebra/ioctl_solaris.h new file mode 100644 index 000000000..188986be1 --- /dev/null +++ b/zebra/ioctl_solaris.h @@ -0,0 +1,29 @@ +/* + * Interface looking up by ioctl () on Solaris. + * Copyright (C) 1999 Kunihiro Ishiguro + * + * 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 _ZEBRA_IF_IOCTL_SOLARIS_H +#define _ZEBRA_IF_IOCTL_SOLARIS_H + +void lifreq_set_name (struct lifreq *, const char *); +int if_get_flags_direct (const char *, uint64_t *, unsigned int af); + +#endif /* _ZEBRA_IF_IOCTL_SOLARIS_H */ diff --git a/zebra/ipforward_aix.c b/zebra/ipforward_aix.c deleted file mode 100644 index c79e7f1c7..000000000 --- a/zebra/ipforward_aix.c +++ /dev/null @@ -1,64 +0,0 @@ -/* - * ipforward value get function for aix. - * 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, ret; - int af = AF_INET; - char netopt[] = "ipforwarding"; - struct optreq oq; - - fd = socket(af, SOCK_DGRAM, 0); - if (fd < 0) { - /* need logging here */ - return -1; - } - - strcpy (oq.name, netopt); - oq.getnext = 0; - - ret = ioctl (fd, SIOCGNETOPT, (caddr_t)&oq); - close(fd); - - if (ret < 0) { - /* need logging here */ - return -1; - } - - ret = atoi (oq.data); - return ret; -} - -int -ipforward_on () -{ - ; -} - -int -ipforward_off () -{ - ; -} diff --git a/zebra/ipforward_ews.c b/zebra/ipforward_ews.c deleted file mode 100644 index c872000a8..000000000 --- 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 185aee3ee..57ed18578 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 6403830b9..43c63a83f 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_main.c b/zebra/irdp_main.c index c297979cf..cf78a54e7 100644 --- a/zebra/irdp_main.c +++ b/zebra/irdp_main.c @@ -180,6 +180,7 @@ irdp_send(struct interface *ifp, struct prefix *p, struct stream *s) { struct zebra_if *zi=ifp->info; struct irdp_interface *irdp=&zi->irdp; + char buf[PREFIX_STRLEN]; u_int32_t dst; u_int32_t ttl=1; @@ -191,10 +192,9 @@ irdp_send(struct interface *ifp, struct prefix *p, struct stream *s) dst = htonl(INADDR_ALLHOSTS_GROUP); if(irdp->flags & IF_DEBUG_MESSAGES) - zlog_debug("IRDP: TX Advert on %s %s/%d Holdtime=%d Preference=%d", + zlog_debug("IRDP: TX Advert on %s %s Holdtime=%d Preference=%d", ifp->name, - inet_ntoa(p->u.prefix4), - p->prefixlen, + prefix2str(p, buf, sizeof buf), irdp->flags & IF_SHUTDOWN? 0 : irdp->Lifetime, get_pref(irdp, p)); diff --git a/zebra/irdp_packet.c b/zebra/irdp_packet.c index 28dc171ed..0d31050c5 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 6b96c6df1..1a16a75a4 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 @@ -7,14 +28,9 @@ #include "zebra/rt.h" #include "zebra/redistribute.h" #include "zebra/connected.h" +#include "zebra/rib.h" -int kernel_add_ipv4 (struct prefix *a, struct rib *b) { return 0; } -#pragma weak kernel_delete_ipv4 = kernel_add_ipv4 -int kernel_add_ipv6 (struct prefix *a, struct rib *b) { return 0; } -#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; } +int kernel_route_rib (struct prefix *a, struct rib *old, struct rib *new) { return 0; } int kernel_add_route (struct prefix_ipv4 *a, struct in_addr *b, int c, int d) { return 0; } @@ -37,5 +53,10 @@ int kernel_address_delete_ipv4 (struct interface *a, struct connected *b) return 0; } -void kernel_init (void) { return; } +void kernel_init (struct zebra_vrf *zvrf) { return; } +void kernel_terminate (struct zebra_vrf *zvrf) { return; } +#ifdef HAVE_SYS_WEAK_ALIAS_PRAGMA #pragma weak route_read = kernel_init +#else +void route_read (struct zebra_vrf *zvrf) { return; } +#endif diff --git a/zebra/kernel_socket.c b/zebra/kernel_socket.c index b7061e762..64c6cbbf2 100644 --- a/zebra/kernel_socket.c +++ b/zebra/kernel_socket.c @@ -20,6 +20,7 @@ */ #include +#include #include "if.h" #include "prefix.h" @@ -32,25 +33,75 @@ #include "table.h" #include "rib.h" #include "privs.h" +#include "vrf.h" #include "zebra/interface.h" #include "zebra/zserv.h" #include "zebra/debug.h" #include "zebra/kernel_socket.h" +#include "zebra/rib.h" 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. + * 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(SA_SIZE) +/* SAROUNDUP is the only thing we need, and SA_SIZE provides that */ +#define SAROUNDUP(a) SA_SIZE(a) +#else /* !SA_SIZE */ + +#if defined(RT_ROUNDUP) +#define ROUNDUP(a) RT_ROUNDUP(a) +#endif /* defined(RT_ROUNDUP) */ + +#if defined(SUNOS_5) +/* Solaris has struct sockaddr_in[6] definitions at 16 / 32 bytes size, + * so the whole concept doesn't really apply. */ +#define ROUNDUP(a) (a) +#endif + +/* + * 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_TYPE int +#else +#define ROUNDUP_TYPE long +#endif + #define ROUNDUP(a) \ - ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long)) + ((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 @@ -78,28 +129,43 @@ extern struct zebra_t zebrad; ROUNDUP(sizeof(struct sockaddr_dl)) : sizeof(struct sockaddr))) #endif /* HAVE_STRUCT_SOCKADDR_SA_LEN */ -/* We use an additional pointer in following, pdest, rather than (DEST) - * directly, because gcc will warn if the macro is expanded and DEST is NULL, - * complaining that memcpy is being passed a NULL value, despite the fact - * the if (NULL) makes it impossible. +#endif /* !SA_SIZE */ + +/* + * We use a call to an inline function to copy (PNT) to (DEST) + * 1. Calculating the length of the copy requires an #ifdef to determine + * if sa_len is a field and can't be used directly inside a #define + * 2. So the compiler doesn't complain when DEST is NULL, which is only true + * when we are skipping the copy and incrementing to the next SA */ +static inline void +rta_copy (union sockunion *dest, caddr_t src) { + int len; + if (!dest) + return; +#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN + len = (((struct sockaddr *)src)->sa_len > sizeof (*dest)) ? + sizeof (*dest) : ((struct sockaddr *)src)->sa_len ; +#else + len = (SAROUNDUP (src) > sizeof (*dest)) ? + sizeof (*dest) : SAROUNDUP (src) ; +#endif + memcpy (dest, src, len); +} + #define RTA_ADDR_GET(DEST, RTA, RTMADDRS, PNT) \ if ((RTMADDRS) & (RTA)) \ { \ - void *pdest = (DEST); \ int len = SAROUNDUP ((PNT)); \ - if ( ((DEST) != NULL) && \ - af_check (((struct sockaddr *)(PNT))->sa_family)) \ - memcpy (pdest, (PNT), len); \ + if (af_check (((struct sockaddr *)(PNT))->sa_family)) \ + rta_copy((DEST), (PNT)); \ (PNT) += len; \ } #define RTA_ATTR_GET(DEST, RTA, RTMADDRS, PNT) \ if ((RTMADDRS) & (RTA)) \ { \ - void *pdest = (DEST); \ int len = SAROUNDUP ((PNT)); \ - if ((DEST) != NULL) \ - memcpy (pdest, (PNT), len); \ + rta_copy((DEST), (PNT)); \ (PNT) += len; \ } @@ -176,8 +242,12 @@ static const struct message rtm_flag_str[] = #ifdef RTF_CLONING {RTF_CLONING, "CLONING"}, #endif /* RTF_CLONING */ +#ifdef RTF_XRESOLVE {RTF_XRESOLVE, "XRESOLVE"}, +#endif /* RTF_XRESOLVE */ +#ifdef RTF_LLINFO {RTF_LLINFO, "LLINFO"}, +#endif /* RTF_LLINFO */ {RTF_STATIC, "STATIC"}, {RTF_BLACKHOLE, "BLACKHOLE"}, #ifdef RTF_PRIVATE @@ -222,7 +292,7 @@ int routing_sock = -1; /* #define DEBUG */ /* Supported address family check. */ -static int inline +static inline int af_check (int family) { if (family == AF_INET) @@ -233,7 +303,7 @@ af_check (int family) #endif /* HAVE_IPV6 */ return 0; } - + /* Dump routing table flag for debug purpose. */ static void rtm_flag_dump (int flag) @@ -280,6 +350,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) @@ -297,7 +368,7 @@ ifan_read (struct if_announcemsghdr *ifan) } #endif /* RTM_IFANNOUNCE */ -#ifdef HAVE_BSD_LINK_DETECT +#ifdef HAVE_BSD_IFI_LINK_STATE /* BSD link detect translation */ static void bsd_linkdetect_translate (struct if_msghdr *ifm) @@ -308,7 +379,30 @@ bsd_linkdetect_translate (struct if_msghdr *ifm) else UNSET_FLAG(ifm->ifm_flags, IFF_RUNNING); } -#endif /* HAVE_BSD_LINK_DETECT */ +#endif /* HAVE_BSD_IFI_LINK_STATE */ + +static enum zebra_link_type +sdl_to_zebra_link_type (unsigned int sdlt) +{ + switch (sdlt) + { + case IFT_ETHER: return ZEBRA_LLT_ETHER; + case IFT_X25: return ZEBRA_LLT_X25; + case IFT_FDDI: return ZEBRA_LLT_FDDI; + case IFT_PPP: return ZEBRA_LLT_PPP; + case IFT_LOOP: return ZEBRA_LLT_LOOPBACK; + case IFT_SLIP: return ZEBRA_LLT_SLIP; + case IFT_ARCNET: return ZEBRA_LLT_ARCNET; + case IFT_ATM: return ZEBRA_LLT_ATM; + case IFT_LOCALTALK: return ZEBRA_LLT_LOCALTLK; + case IFT_HIPPI: return ZEBRA_LLT_HIPPI; +#ifdef IFT_IEEE1394 + case IFT_IEEE1394: return ZEBRA_LLT_IEEE1394; +#endif + + default: return ZEBRA_LLT_UNKNOWN; + } +} /* * Handle struct if_msghdr obtained from reading routing socket or @@ -322,7 +416,7 @@ ifm_read (struct if_msghdr *ifm) struct sockaddr_dl *sdl; char ifname[IFNAMSIZ]; short ifnlen = 0; - caddr_t *cp; + caddr_t cp; /* terminate ifname at head (for strnlen) and tail (for safety) */ ifname[IFNAMSIZ - 1] = '\0'; @@ -444,9 +538,9 @@ ifm_read (struct if_msghdr *ifm) */ ifp->ifindex = ifm->ifm_index; -#ifdef HAVE_BSD_LINK_DETECT /* translate BSD kernel msg for link-state */ +#ifdef HAVE_BSD_IFI_LINK_STATE /* translate BSD kernel msg for link-state */ bsd_linkdetect_translate(ifm); -#endif /* HAVE_BSD_LINK_DETECT */ +#endif /* HAVE_BSD_IFI_LINK_STATE */ if_flags_update (ifp, ifm->ifm_flags); #if defined(__bsdi__) @@ -458,13 +552,31 @@ 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 */ + ifp->ll_type = ZEBRA_LLT_UNKNOWN; + ifp->hw_addr_len = 0; if (ifnlen) - memcpy (&ifp->sdl, sdl, sizeof (struct sockaddr_dl)); + { +#ifdef HAVE_STRUCT_SOCKADDR_DL_SDL_LEN + memcpy (&((struct zebra_if *)ifp->info)->sdl, sdl, sdl->sdl_len); +#else + memcpy (&((struct zebra_if *)ifp->info)->sdl, sdl, sizeof (struct sockaddr_dl)); +#endif /* HAVE_STRUCT_SOCKADDR_DL_SDL_LEN */ + + ifp->ll_type = sdl_to_zebra_link_type (sdl->sdl_type); + if (sdl->sdl_alen <= sizeof(ifp->hw_addr)) + { + memcpy (ifp->hw_addr, LLADDR(sdl), sdl->sdl_alen); + ifp->hw_addr_len = sdl->sdl_alen; + } + } if_add_update (ifp); } @@ -485,9 +597,9 @@ ifm_read (struct if_msghdr *ifm) return -1; } -#ifdef HAVE_BSD_LINK_DETECT /* translate BSD kernel msg for link-state */ +#ifdef HAVE_BSD_IFI_LINK_STATE /* translate BSD kernel msg for link-state */ bsd_linkdetect_translate(ifm); -#endif /* HAVE_BSD_LINK_DETECT */ +#endif /* HAVE_BSD_IFI_LINK_STATE */ /* update flags and handle operative->inoperative transition, if any */ if_flags_update (ifp, ifm->ifm_flags); @@ -528,7 +640,7 @@ ifm_read (struct if_msghdr *ifm) return 0; } - + /* Address read from struct ifa_msghdr. */ static void ifam_read_mesg (struct ifa_msghdr *ifm, @@ -564,50 +676,32 @@ ifam_read_mesg (struct ifa_msghdr *ifm, if (IS_ZEBRA_DEBUG_KERNEL) { - switch (sockunion_family(addr)) + int family = sockunion_family(addr); + switch (family) { case AF_INET: - { - char buf[4][INET_ADDRSTRLEN]; - zlog_debug ("%s: ifindex %d, ifname %s, ifam_addrs 0x%x, " - "ifam_flags 0x%x, addr %s/%d broad %s dst %s " - "gateway %s", - __func__, ifm->ifam_index, - (ifnlen ? ifname : "(nil)"), ifm->ifam_addrs, - ifm->ifam_flags, - inet_ntop(AF_INET,&addr->sin.sin_addr, - buf[0],sizeof(buf[0])), - ip_masklen(mask->sin.sin_addr), - inet_ntop(AF_INET,&brd->sin.sin_addr, - buf[1],sizeof(buf[1])), - inet_ntop(AF_INET,&dst.sin.sin_addr, - buf[2],sizeof(buf[2])), - inet_ntop(AF_INET,&gateway.sin.sin_addr, - buf[3],sizeof(buf[3]))); - } - break; #ifdef HAVE_IPV6 case AF_INET6: +#endif { char buf[4][INET6_ADDRSTRLEN]; zlog_debug ("%s: ifindex %d, ifname %s, ifam_addrs 0x%x, " "ifam_flags 0x%x, addr %s/%d broad %s dst %s " "gateway %s", - __func__, ifm->ifam_index, + __func__, ifm->ifam_index, (ifnlen ? ifname : "(nil)"), ifm->ifam_addrs, ifm->ifam_flags, - inet_ntop(AF_INET6,&addr->sin6.sin6_addr, + inet_ntop(family,&addr->sin.sin_addr, buf[0],sizeof(buf[0])), - ip6_masklen(mask->sin6.sin6_addr), - inet_ntop(AF_INET6,&brd->sin6.sin6_addr, + ip_masklen(mask->sin.sin_addr), + inet_ntop(family,&brd->sin.sin_addr, buf[1],sizeof(buf[1])), - inet_ntop(AF_INET6,&dst.sin6.sin6_addr, + inet_ntop(family,&dst.sin.sin_addr, buf[2],sizeof(buf[2])), - inet_ntop(AF_INET6,&gateway.sin6.sin6_addr, + inet_ntop(family,&gateway.sin.sin_addr, buf[3],sizeof(buf[3]))); } break; -#endif /* HAVE_IPV6 */ default: zlog_debug ("%s: ifindex %d, ifname %s, ifam_addrs 0x%x", __func__, ifm->ifam_index, @@ -681,7 +775,9 @@ ifam_read (struct ifa_msghdr *ifam) /* Unset interface index from link-local address when IPv6 stack is KAME. */ if (IN6_IS_ADDR_LINKLOCAL (&addr.sin6.sin6_addr)) - SET_IN6_LINKLOCAL_IFINDEX (addr.sin6.sin6_addr, 0); + { + SET_IN6_LINKLOCAL_IFINDEX (addr.sin6.sin6_addr, 0); + } if (ifam->ifam_type == RTM_NEWADDR) connected_add_ipv6 (ifp, flags, &addr.sin6.sin6_addr, @@ -722,7 +818,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, @@ -800,7 +896,7 @@ rtm_read (struct rt_msghdr *rtm) return; #endif - if ((rtm->rtm_type == RTM_ADD) && ! (flags & RTF_UP)) + if ((rtm->rtm_type == RTM_ADD || rtm->rtm_type == RTM_CHANGE) && ! (flags & RTF_UP)) return; /* This is connected route. */ @@ -837,12 +933,12 @@ rtm_read (struct rt_msghdr *rtm) */ if (rtm->rtm_type != RTM_GET && rtm->rtm_pid == pid) { - char buf[INET_ADDRSTRLEN], gate_buf[INET_ADDRSTRLEN]; + char buf[PREFIX_STRLEN], gate_buf[INET_ADDRSTRLEN]; int ret; if (! IS_ZEBRA_DEBUG_RIB) return; - ret = rib_lookup_ipv4_route (&p, &gate); - inet_ntop (AF_INET, &p.prefix, buf, INET_ADDRSTRLEN); + ret = rib_lookup_ipv4_route (&p, &gate, VRF_DEFAULT); + prefix2str (&p, buf, sizeof(buf)); switch (rtm->rtm_type) { case RTM_ADD: @@ -853,18 +949,18 @@ rtm_read (struct rt_msghdr *rtm) switch (ret) { case ZEBRA_RIB_NOTFOUND: - zlog_debug ("%s: %s %s/%d: desync: RR isn't yet in RIB, while already in FIB", - __func__, lookup (rtm_type_str, rtm->rtm_type), buf, p.prefixlen); + zlog_debug ("%s: %s %s: desync: RR isn't yet in RIB, while already in FIB", + __func__, lookup (rtm_type_str, rtm->rtm_type), buf); break; case ZEBRA_RIB_FOUND_CONNECTED: case ZEBRA_RIB_FOUND_NOGATE: inet_ntop (AF_INET, &gate.sin.sin_addr, gate_buf, INET_ADDRSTRLEN); - zlog_debug ("%s: %s %s/%d: desync: RR is in RIB, but gate differs (ours is %s)", - __func__, lookup (rtm_type_str, rtm->rtm_type), buf, p.prefixlen, gate_buf); + zlog_debug ("%s: %s %s: desync: RR is in RIB, but gate differs (ours is %s)", + __func__, lookup (rtm_type_str, rtm->rtm_type), buf, gate_buf); break; case ZEBRA_RIB_FOUND_EXACT: /* RIB RR == FIB RR */ - zlog_debug ("%s: %s %s/%d: done Ok", - __func__, lookup (rtm_type_str, rtm->rtm_type), buf, p.prefixlen); + zlog_debug ("%s: %s %s: done Ok", + __func__, lookup (rtm_type_str, rtm->rtm_type), buf); rib_lookup_and_dump (&p); return; break; @@ -876,27 +972,27 @@ rtm_read (struct rt_msghdr *rtm) switch (ret) { case ZEBRA_RIB_FOUND_EXACT: - zlog_debug ("%s: %s %s/%d: desync: RR is still in RIB, while already not in FIB", - __func__, lookup (rtm_type_str, rtm->rtm_type), buf, p.prefixlen); + zlog_debug ("%s: %s %s: desync: RR is still in RIB, while already not in FIB", + __func__, lookup (rtm_type_str, rtm->rtm_type), buf); rib_lookup_and_dump (&p); break; case ZEBRA_RIB_FOUND_CONNECTED: case ZEBRA_RIB_FOUND_NOGATE: - zlog_debug ("%s: %s %s/%d: desync: RR is still in RIB, plus gate differs", - __func__, lookup (rtm_type_str, rtm->rtm_type), buf, p.prefixlen); + zlog_debug ("%s: %s %s: desync: RR is still in RIB, plus gate differs", + __func__, lookup (rtm_type_str, rtm->rtm_type), buf); rib_lookup_and_dump (&p); break; case ZEBRA_RIB_NOTFOUND: /* RIB RR == FIB RR */ - zlog_debug ("%s: %s %s/%d: done Ok", - __func__, lookup (rtm_type_str, rtm->rtm_type), buf, p.prefixlen); + zlog_debug ("%s: %s %s: done Ok", + __func__, lookup (rtm_type_str, rtm->rtm_type), buf); rib_lookup_and_dump (&p); return; break; } break; default: - zlog_debug ("%s: %s/%d: warning: loopback RTM of type %s received", - __func__, buf, p.prefixlen, lookup (rtm_type_str, rtm->rtm_type)); + zlog_debug ("%s: %s: warning: loopback RTM of type %s received", + __func__, buf, lookup (rtm_type_str, rtm->rtm_type)); } return; } @@ -906,16 +1002,16 @@ rtm_read (struct rt_msghdr *rtm) */ if (rtm->rtm_type == RTM_CHANGE) rib_delete_ipv4 (ZEBRA_ROUTE_KERNEL, zebra_flags, &p, - NULL, 0, 0, SAFI_UNICAST); + NULL, 0, VRF_DEFAULT, SAFI_UNICAST); if (rtm->rtm_type == RTM_GET || rtm->rtm_type == RTM_ADD || rtm->rtm_type == RTM_CHANGE) - rib_add_ipv4 (ZEBRA_ROUTE_KERNEL, zebra_flags, - &p, &gate.sin.sin_addr, NULL, 0, 0, 0, 0, SAFI_UNICAST); + rib_add_ipv4 (ZEBRA_ROUTE_KERNEL, zebra_flags, &p, &gate.sin.sin_addr, + NULL, 0, VRF_DEFAULT, 0, 0, 0, 0, SAFI_UNICAST); else - rib_delete_ipv4 (ZEBRA_ROUTE_KERNEL, zebra_flags, - &p, &gate.sin.sin_addr, 0, 0, SAFI_UNICAST); + rib_delete_ipv4 (ZEBRA_ROUTE_KERNEL, zebra_flags, &p, + &gate.sin.sin_addr, 0, VRF_DEFAULT, SAFI_UNICAST); } #ifdef HAVE_IPV6 if (dest.sa.sa_family == AF_INET6) @@ -926,7 +1022,7 @@ rtm_read (struct rt_msghdr *rtm) if (rtm->rtm_type != RTM_GET && rtm->rtm_pid == pid) return; struct prefix_ipv6 p; - unsigned int ifindex = 0; + ifindex_t ifindex = 0; p.family = AF_INET6; p.prefix = dest.sin6.sin6_addr; @@ -948,16 +1044,17 @@ rtm_read (struct rt_msghdr *rtm) */ if (rtm->rtm_type == RTM_CHANGE) rib_delete_ipv6 (ZEBRA_ROUTE_KERNEL, zebra_flags, &p, - NULL, 0, 0, SAFI_UNICAST); + NULL, 0, VRF_DEFAULT, SAFI_UNICAST); if (rtm->rtm_type == RTM_GET || rtm->rtm_type == RTM_ADD || rtm->rtm_type == RTM_CHANGE) - rib_add_ipv6 (ZEBRA_ROUTE_KERNEL, zebra_flags, - &p, &gate.sin6.sin6_addr, ifindex, 0, 0, 0, SAFI_UNICAST); + rib_add_ipv6 (ZEBRA_ROUTE_KERNEL, zebra_flags, &p, &gate.sin6.sin6_addr, + ifindex, VRF_DEFAULT, RT_TABLE_MAIN, 0, 0, 0, SAFI_UNICAST); else - rib_delete_ipv6 (ZEBRA_ROUTE_KERNEL, zebra_flags, - &p, &gate.sin6.sin6_addr, ifindex, 0, SAFI_UNICAST); + rib_delete_ipv6 (ZEBRA_ROUTE_KERNEL, zebra_flags, &p, + &gate.sin6.sin6_addr, ifindex, + VRF_DEFAULT, SAFI_UNICAST); } #endif /* HAVE_IPV6 */ } @@ -1010,14 +1107,14 @@ rtm_write (int message, ifp = if_lookup_by_index (index); - if (gate && message == RTM_ADD) + if (gate && (message == RTM_ADD || message == RTM_CHANGE)) msg.rtm.rtm_flags |= RTF_GATEWAY; /* When RTF_CLONING is unavailable on BSD, should we set some * other flag instead? */ #ifdef RTF_CLONING - if (! gate && message == RTM_ADD && ifp && + if (! gate && (message == RTM_ADD || message == RTM_CHANGE) && ifp && (ifp->flags & IFF_POINTOPOINT) == 0) msg.rtm.rtm_flags |= RTF_CLONING; #endif /* RTF_CLONING */ @@ -1037,12 +1134,12 @@ rtm_write (int message, __func__, dest_buf, mask_buf, index); return -1; } - gate = (union sockunion *) & ifp->sdl; + gate = (union sockunion *) &((struct zebra_if *)ifp->info)->sdl; } if (mask) msg.rtm.rtm_addrs |= RTA_NETMASK; - else if (message == RTM_ADD) + else if (message == RTM_ADD || message == RTM_CHANGE) msg.rtm.rtm_flags |= RTF_HOST; /* Tagging route with flags */ @@ -1055,15 +1152,6 @@ rtm_write (int message, msg.rtm.rtm_flags |= RTF_REJECT; -#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN -#define SOCKADDRSET(X,R) \ - if (msg.rtm.rtm_addrs & (R)) \ - { \ - int len = ROUNDUP ((X)->sa.sa_len); \ - memcpy (pnt, (caddr_t)(X), len); \ - pnt += len; \ - } -#else #define SOCKADDRSET(X,R) \ if (msg.rtm.rtm_addrs & (R)) \ { \ @@ -1071,7 +1159,6 @@ rtm_write (int message, memcpy (pnt, (caddr_t)(X), len); \ pnt += len; \ } -#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */ pnt = (caddr_t) msg.buf; @@ -1099,7 +1186,7 @@ rtm_write (int message, return ZEBRA_ERR_NOERROR; } - + #include "thread.h" #include "zebra/zserv.h" @@ -1110,7 +1197,8 @@ rtmsg_debug (struct rt_msghdr *rtm) zlog_debug ("Kernel: Len: %d Type: %s", rtm->rtm_msglen, lookup (rtm_type_str, rtm->rtm_type)); rtm_flag_dump (rtm->rtm_flags); zlog_debug ("Kernel: message seq %d", rtm->rtm_seq); - zlog_debug ("Kernel: pid %d, rtm_addrs 0x%x", rtm->rtm_pid, rtm->rtm_addrs); + zlog_debug ("Kernel: pid %lld, rtm_addrs 0x%x", + (long long)rtm->rtm_pid, rtm->rtm_addrs); } /* This is pretty gross, better suggestions welcome -- mhandler */ @@ -1232,8 +1320,11 @@ kernel_read (struct thread *thread) /* Make routing socket. */ static void -routing_socket (void) +routing_socket (struct zebra_vrf *zvrf) { + if (zvrf->vrf_id != VRF_DEFAULT) + return; + if ( zserv_privs.change (ZPRIVS_RAISE) ) zlog_err ("routing_socket: Can't raise privileges"); @@ -1264,7 +1355,13 @@ routing_socket (void) /* Exported interface function. This function simply calls routing_socket (). */ void -kernel_init (void) +kernel_init (struct zebra_vrf *zvrf) +{ + routing_socket (zvrf); +} + +void +kernel_terminate (struct zebra_vrf *zvrf) { - routing_socket (); + return; } diff --git a/zebra/main.c b/zebra/main.c index 5f26ce243..35cb1593e 100644 --- a/zebra/main.c +++ b/zebra/main.c @@ -32,6 +32,7 @@ #include "plist.h" #include "privs.h" #include "sigevent.h" +#include "vrf.h" #include "zebra/rib.h" #include "zebra/zserv.h" @@ -39,6 +40,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 = @@ -69,6 +71,7 @@ struct option longopts[] = { "batch", no_argument, NULL, 'b'}, { "daemon", no_argument, NULL, 'd'}, { "keep_kernel", no_argument, NULL, 'k'}, + { "fpm_format", required_argument, NULL, 'F'}, { "config_file", required_argument, NULL, 'f'}, { "pid_file", required_argument, NULL, 'i'}, { "socket", required_argument, NULL, 'z'}, @@ -104,7 +107,7 @@ struct zebra_privs_t zserv_privs = .vty_group = VTY_GROUP, #endif .caps_p = _caps_p, - .cap_num_p = sizeof(_caps_p)/sizeof(_caps_p[0]), + .cap_num_p = array_size(_caps_p), .cap_num_i = 0 }; @@ -128,6 +131,7 @@ usage (char *progname, int status) "-b, --batch Runs in batch mode\n"\ "-d, --daemon Runs in daemon mode\n"\ "-f, --config_file Set configuration file name\n"\ + "-F, --fpm_format Set fpm format to 'netlink' or 'protobuf'\n"\ "-i, --pid_file Set process identifier file name\n"\ "-z, --socket Set path of zebra socket\n"\ "-k, --keep_kernel Don't delete old routes which installed by "\ @@ -150,7 +154,7 @@ usage (char *progname, int status) exit (status); } - + /* SIGHUP handler. */ static void sighup (void) @@ -202,7 +206,83 @@ struct quagga_signal_t zebra_signals[] = .handler = &sigint, }, }; - + +/* Callback upon creating a new VRF. */ +static int +zebra_vrf_new (vrf_id_t vrf_id, void **info) +{ + struct zebra_vrf *zvrf = *info; + + if (! zvrf) + { + zvrf = zebra_vrf_alloc (vrf_id); + *info = (void *)zvrf; + router_id_init (zvrf); + } + + return 0; +} + +/* Callback upon enabling a VRF. */ +static int +zebra_vrf_enable (vrf_id_t vrf_id, void **info) +{ + struct zebra_vrf *zvrf = (struct zebra_vrf *) (*info); + + assert (zvrf); + +#if defined (HAVE_RTADV) + rtadv_init (zvrf); +#endif + kernel_init (zvrf); + interface_list (zvrf); + route_read (zvrf); + + return 0; +} + +/* Callback upon disabling a VRF. */ +static int +zebra_vrf_disable (vrf_id_t vrf_id, void **info) +{ + struct zebra_vrf *zvrf = (struct zebra_vrf *) (*info); + struct listnode *list_node; + struct interface *ifp; + + assert (zvrf); + + rib_close_table (zvrf->table[AFI_IP][SAFI_UNICAST]); + rib_close_table (zvrf->table[AFI_IP6][SAFI_UNICAST]); + + for (ALL_LIST_ELEMENTS_RO (vrf_iflist (vrf_id), list_node, ifp)) + { + int operative = if_is_operative (ifp); + UNSET_FLAG (ifp->flags, IFF_UP); + if (operative) + if_down (ifp); + } + +#if defined (HAVE_RTADV) + rtadv_terminate (zvrf); +#endif + kernel_terminate (zvrf); + + list_delete_all_node (zvrf->rid_all_sorted_list); + list_delete_all_node (zvrf->rid_lo_sorted_list); + + return 0; +} + +/* Zebra VRF initialization. */ +static void +zebra_vrf_init (void) +{ + vrf_add_hook (VRF_NEW_HOOK, zebra_vrf_new); + vrf_add_hook (VRF_ENABLE_HOOK, zebra_vrf_enable); + vrf_add_hook (VRF_DISABLE_HOOK, zebra_vrf_disable); + vrf_init (); +} + /* Main startup routine. */ int main (int argc, char **argv) @@ -217,6 +297,7 @@ main (int argc, char **argv) char *progname; struct thread thread; char *zserv_path = NULL; + char *fpm_format = NULL; /* Set umask before anything for security */ umask (0027); @@ -232,9 +313,9 @@ main (int argc, char **argv) int opt; #ifdef HAVE_NETLINK - opt = getopt_long (argc, argv, "bdkf:i:z:hA:P:ru:g:vs:C", longopts, 0); + opt = getopt_long (argc, argv, "bdkf:F:i:z:hA:P:ru:g:vs:C", longopts, 0); #else - opt = getopt_long (argc, argv, "bdkf:i:z:hA:P:ru:g:vC", longopts, 0); + opt = getopt_long (argc, argv, "bdkf:F:i:z:hA:P:ru:g:vC", longopts, 0); #endif /* HAVE_NETLINK */ if (opt == EOF) @@ -258,6 +339,9 @@ main (int argc, char **argv) case 'f': config_file = optarg; break; + case 'F': + fpm_format = optarg; + break; case 'A': vty_addr = optarg; break; @@ -313,7 +397,7 @@ main (int argc, char **argv) zprivs_init (&zserv_privs); /* Vty related initialize. */ - signal_init (zebrad.master, Q_SIGC(zebra_signals), zebra_signals); + signal_init (zebrad.master, array_size(zebra_signals), zebra_signals); cmd_init (1); vty_init (zebrad.master); memory_init (); @@ -323,11 +407,13 @@ main (int argc, char **argv) rib_init (); zebra_if_init (); zebra_debug_init (); - router_id_init(); + router_id_cmd_init (); zebra_vty_init (); access_list_init (); prefix_list_init (); - rtadv_init (); +#if defined (HAVE_RTADV) + rtadv_cmd_init (); +#endif #ifdef HAVE_IRDP irdp_init(); #endif @@ -335,18 +421,19 @@ main (int argc, char **argv) /* For debug purpose. */ /* SET_FLAG (zebra_debug_event, ZEBRA_DEBUG_EVENT); */ - /* Make kernel routing socket. */ - kernel_init (); - interface_list (); - route_read (); - - /* Sort VTY commands. */ - sort_node (); + /* Initialize VRF module, and make kernel routing socket. */ + zebra_vrf_init (); #ifdef HAVE_SNMP zebra_snmp_init (); #endif /* HAVE_SNMP */ +#ifdef HAVE_FPM + zfpm_init (zebrad.master, 1, 0, fpm_format); +#else + zfpm_init (zebrad.master, 0, 0, fpm_format); +#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 @@ -359,7 +446,10 @@ main (int argc, char **argv) /* Don't start execution if we are in dry-run mode */ if (dryrun) return(0); - + + /* Count up events for interfaces */ + if_startup_count_up (); + /* Clean up rib. */ rib_weed_tables (); diff --git a/zebra/misc_null.c b/zebra/misc_null.c index 735943015..18977d2f8 100644 --- a/zebra/misc_null.c +++ b/zebra/misc_null.c @@ -1,11 +1,56 @@ +/* + * 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" +#ifdef HAVE_SYS_WEAK_ALIAS_PRAGMA +void _quagga_noop (void); +void _quagga_noop (void) { return; } +#pragma weak rtadv_config_write = _quagga_noop +#pragma weak irdp_config_write = _quagga_noop +#ifdef HAVE_NET_RT_IFLIST +#pragma weak ifstat_update_sysctl = _quagga_noop +#endif +#ifdef HAVE_PROC_NET_DEV +#pragma weak ifstat_update_proc = _quagga_noop +#endif +#else +void rtadv_config_write (struct vty *vty, struct interface *ifp) { return; } +void irdp_config_write (struct vty *vty, struct interface *ifp) { return; } +#ifdef HAVE_PROC_NET_DEV void ifstat_update_proc (void) { return; } -#pragma weak rtadv_config_write = ifstat_update_proc -#pragma weak irdp_config_write = ifstat_update_proc -#pragma weak ifstat_update_sysctl = ifstat_update_proc +#endif +#ifdef HAVE_NET_RT_IFLIST +void ifstat_update_sysctl (void) { return; } +#endif +#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 d37bb9bb7..000000000 --- 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 4276f1d04..a7a6b2580 100644 --- a/zebra/redistribute.c +++ b/zebra/redistribute.c @@ -30,6 +30,7 @@ #include "zclient.h" #include "linklist.h" #include "log.h" +#include "vrf.h" #include "zebra/rib.h" #include "zebra/zserv.h" @@ -67,7 +68,7 @@ zebra_check_addr (struct prefix *p) return 1; } -static int +int is_default (struct prefix *p) { if (p->family == AF_INET) @@ -85,7 +86,7 @@ is_default (struct prefix *p) } static void -zebra_redistribute_default (struct zserv *client) +zebra_redistribute_default (struct zserv *client, vrf_id_t vrf_id) { struct prefix_ipv4 p; struct route_table *table; @@ -101,13 +102,13 @@ zebra_redistribute_default (struct zserv *client) p.family = AF_INET; /* Lookup table. */ - table = vrf_table (AFI_IP, SAFI_UNICAST, 0); + table = zebra_vrf_table (AFI_IP, SAFI_UNICAST, vrf_id); if (table) { 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); @@ -121,13 +122,13 @@ zebra_redistribute_default (struct zserv *client) p6.family = AF_INET6; /* Lookup table. */ - table = vrf_table (AFI_IP6, SAFI_UNICAST, 0); + table = zebra_vrf_table (AFI_IP6, SAFI_UNICAST, vrf_id); if (table) { 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); @@ -139,32 +140,44 @@ zebra_redistribute_default (struct zserv *client) /* Redistribute routes. */ static void -zebra_redistribute (struct zserv *client, int type) +zebra_redistribute (struct zserv *client, int type, vrf_id_t vrf_id) { struct rib *newrib; struct route_table *table; struct route_node *rn; - table = vrf_table (AFI_IP, SAFI_UNICAST, 0); + table = zebra_vrf_table (AFI_IP, SAFI_UNICAST, vrf_id); if (table) for (rn = route_top (table); rn; rn = route_next (rn)) - for (newrib = rn->info; newrib; newrib = newrib->next) - if (CHECK_FLAG (newrib->flags, ZEBRA_FLAG_SELECTED) - && newrib->type == type - && newrib->distance != DISTANCE_INFINITY - && zebra_check_addr (&rn->p)) - zsend_route_multipath (ZEBRA_IPV4_ROUTE_ADD, client, &rn->p, newrib); - + RNODE_FOREACH_RIB (rn, newrib) + { + if (IS_ZEBRA_DEBUG_EVENT) + zlog_debug("%s: checking: selected=%d, type=%d, distance=%d, zebra_check_addr=%d", + __func__, CHECK_FLAG (newrib->flags, ZEBRA_FLAG_SELECTED), + newrib->type, newrib->distance, zebra_check_addr (&rn->p)); + if (CHECK_FLAG (newrib->flags, ZEBRA_FLAG_SELECTED) + && newrib->type == type + && newrib->distance != DISTANCE_INFINITY + && zebra_check_addr (&rn->p)) + { + client->redist_v4_add_cnt++; + zsend_route_multipath (ZEBRA_IPV4_ROUTE_ADD, client, &rn->p, newrib); + } + } + #ifdef HAVE_IPV6 - table = vrf_table (AFI_IP6, SAFI_UNICAST, 0); + table = zebra_vrf_table (AFI_IP6, SAFI_UNICAST, vrf_id); 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 && zebra_check_addr (&rn->p)) - zsend_route_multipath (ZEBRA_IPV6_ROUTE_ADD, client, &rn->p, newrib); + { + client->redist_v6_add_cnt++; + zsend_route_multipath (ZEBRA_IPV6_ROUTE_ADD, client, &rn->p, newrib); + } #endif /* HAVE_IPV6 */ } @@ -176,26 +189,20 @@ redistribute_add (struct prefix *p, struct rib *rib) for (ALL_LIST_ELEMENTS (zebrad.client_list, node, nnode, client)) { - if (is_default (p)) - { - if (client->redist_default || client->redist[rib->type]) - { - if (p->family == AF_INET) - zsend_route_multipath (ZEBRA_IPV4_ROUTE_ADD, client, p, rib); -#ifdef HAVE_IPV6 - if (p->family == AF_INET6) - zsend_route_multipath (ZEBRA_IPV6_ROUTE_ADD, client, p, rib); -#endif /* HAVE_IPV6 */ - } - } - else if (client->redist[rib->type]) + if ((is_default (p) && + vrf_bitmap_check (client->redist_default, rib->vrf_id)) + || vrf_bitmap_check (client->redist[rib->type], rib->vrf_id)) { if (p->family == AF_INET) - zsend_route_multipath (ZEBRA_IPV4_ROUTE_ADD, client, p, rib); -#ifdef HAVE_IPV6 + { + client->redist_v4_add_cnt++; + zsend_route_multipath (ZEBRA_IPV4_ROUTE_ADD, client, p, rib); + } if (p->family == AF_INET6) - zsend_route_multipath (ZEBRA_IPV6_ROUTE_ADD, client, p, rib); -#endif /* HAVE_IPV6 */ + { + client->redist_v6_add_cnt++; + zsend_route_multipath (ZEBRA_IPV6_ROUTE_ADD, client, p, rib); + } } } } @@ -212,21 +219,9 @@ redistribute_delete (struct prefix *p, struct rib *rib) for (ALL_LIST_ELEMENTS (zebrad.client_list, node, nnode, client)) { - if (is_default (p)) - { - if (client->redist_default || client->redist[rib->type]) - { - if (p->family == AF_INET) - zsend_route_multipath (ZEBRA_IPV4_ROUTE_DELETE, client, p, - rib); -#ifdef HAVE_IPV6 - if (p->family == AF_INET6) - zsend_route_multipath (ZEBRA_IPV6_ROUTE_DELETE, client, p, - rib); -#endif /* HAVE_IPV6 */ - } - } - else if (client->redist[rib->type]) + if ((is_default (p) && + vrf_bitmap_check (client->redist_default, rib->vrf_id)) + || vrf_bitmap_check (client->redist[rib->type], rib->vrf_id)) { if (p->family == AF_INET) zsend_route_multipath (ZEBRA_IPV4_ROUTE_DELETE, client, p, rib); @@ -239,7 +234,8 @@ redistribute_delete (struct prefix *p, struct rib *rib) } void -zebra_redistribute_add (int command, struct zserv *client, int length) +zebra_redistribute_add (int command, struct zserv *client, int length, + vrf_id_t vrf_id) { int type; @@ -248,15 +244,16 @@ zebra_redistribute_add (int command, struct zserv *client, int length) if (type == 0 || type >= ZEBRA_ROUTE_MAX) return; - if (! client->redist[type]) + if (! vrf_bitmap_check (client->redist[type], vrf_id)) { - client->redist[type] = 1; - zebra_redistribute (client, type); + vrf_bitmap_set (client->redist[type], vrf_id); + zebra_redistribute (client, type, vrf_id); } } void -zebra_redistribute_delete (int command, struct zserv *client, int length) +zebra_redistribute_delete (int command, struct zserv *client, int length, + vrf_id_t vrf_id) { int type; @@ -265,21 +262,22 @@ zebra_redistribute_delete (int command, struct zserv *client, int length) if (type == 0 || type >= ZEBRA_ROUTE_MAX) return; - client->redist[type] = 0; + vrf_bitmap_unset (client->redist[type], vrf_id); } void -zebra_redistribute_default_add (int command, struct zserv *client, int length) +zebra_redistribute_default_add (int command, struct zserv *client, int length, + vrf_id_t vrf_id) { - client->redist_default = 1; - zebra_redistribute_default (client); + vrf_bitmap_set (client->redist_default, vrf_id); + zebra_redistribute_default (client, vrf_id); } void zebra_redistribute_default_delete (int command, struct zserv *client, - int length) + int length, vrf_id_t vrf_id) { - client->redist_default = 0;; + vrf_bitmap_unset (client->redist_default, vrf_id); } /* Interface up information. */ @@ -293,7 +291,11 @@ zebra_interface_up_update (struct interface *ifp) zlog_debug ("MESSAGE: ZEBRA_INTERFACE_UP %s", ifp->name); for (ALL_LIST_ELEMENTS (zebrad.client_list, node, nnode, client)) - zsend_interface_update (ZEBRA_INTERFACE_UP, client, ifp); + if (client->ifinfo) + { + zsend_interface_update (ZEBRA_INTERFACE_UP, client, ifp); + zsend_interface_link_params (client, ifp); + } } /* Interface down information. */ @@ -307,7 +309,9 @@ zebra_interface_down_update (struct interface *ifp) zlog_debug ("MESSAGE: ZEBRA_INTERFACE_DOWN %s", ifp->name); for (ALL_LIST_ELEMENTS (zebrad.client_list, node, nnode, client)) - zsend_interface_update (ZEBRA_INTERFACE_DOWN, client, ifp); + { + zsend_interface_update (ZEBRA_INTERFACE_DOWN, client, ifp); + } } /* Interface information update. */ @@ -322,7 +326,11 @@ zebra_interface_add_update (struct interface *ifp) for (ALL_LIST_ELEMENTS (zebrad.client_list, node, nnode, client)) if (client->ifinfo) - zsend_interface_add (client, ifp); + { + client->ifadd_cnt++; + zsend_interface_add (client, ifp); + zsend_interface_link_params (client, ifp); + } } void @@ -336,7 +344,10 @@ zebra_interface_delete_update (struct interface *ifp) for (ALL_LIST_ELEMENTS (zebrad.client_list, node, nnode, client)) if (client->ifinfo) - zsend_interface_delete (client, ifp); + { + client->ifdel_cnt++; + zsend_interface_delete (client, ifp); + } } /* Interface address addition. */ @@ -350,19 +361,25 @@ zebra_interface_address_add_update (struct interface *ifp, if (IS_ZEBRA_DEBUG_EVENT) { - char buf[INET6_ADDRSTRLEN]; + char buf[PREFIX_STRLEN]; p = ifc->address; - zlog_debug ("MESSAGE: ZEBRA_INTERFACE_ADDRESS_ADD %s/%d on %s", - inet_ntop (p->family, &p->u.prefix, buf, INET6_ADDRSTRLEN), - p->prefixlen, ifc->ifp->name); + zlog_debug ("MESSAGE: ZEBRA_INTERFACE_ADDRESS_ADD %s on %s", + prefix2str (p, buf, sizeof(buf)), + 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)) if (client->ifinfo && CHECK_FLAG (ifc->conf, ZEBRA_IFC_REAL)) - zsend_interface_address (ZEBRA_INTERFACE_ADDRESS_ADD, client, ifp, ifc); + { + client->connected_rt_add_cnt++; + zsend_interface_address (ZEBRA_INTERFACE_ADDRESS_ADD, client, ifp, ifc); + } } /* Interface address deletion. */ @@ -376,17 +393,35 @@ zebra_interface_address_delete_update (struct interface *ifp, if (IS_ZEBRA_DEBUG_EVENT) { - char buf[INET6_ADDRSTRLEN]; + char buf[PREFIX_STRLEN]; p = ifc->address; - zlog_debug ("MESSAGE: ZEBRA_INTERFACE_ADDRESS_DELETE %s/%d on %s", - inet_ntop (p->family, &p->u.prefix, buf, INET6_ADDRSTRLEN), - p->prefixlen, ifc->ifp->name); + zlog_debug ("MESSAGE: ZEBRA_INTERFACE_ADDRESS_DELETE %s on %s", + prefix2str (p, buf, sizeof(buf)), + ifc->ifp->name); } router_id_del_address(ifc); for (ALL_LIST_ELEMENTS (zebrad.client_list, node, nnode, client)) if (client->ifinfo && CHECK_FLAG (ifc->conf, ZEBRA_IFC_REAL)) - zsend_interface_address (ZEBRA_INTERFACE_ADDRESS_DELETE, client, ifp, ifc); + { + client->connected_rt_del_cnt++; + zsend_interface_address (ZEBRA_INTERFACE_ADDRESS_DELETE, client, ifp, ifc); + } +} + +/* Interface parameters update */ +void +zebra_interface_parameters_update (struct interface *ifp) +{ + struct listnode *node, *nnode; + struct zserv *client; + + if (IS_ZEBRA_DEBUG_EVENT) + zlog_debug ("MESSAGE: ZEBRA_INTERFACE_LINK_PARAMS %s", ifp->name); + + for (ALL_LIST_ELEMENTS (zebrad.client_list, node, nnode, client)) + if (client->ifinfo) + zsend_interface_link_params (client, ifp); } diff --git a/zebra/redistribute.h b/zebra/redistribute.h index 9ed99bc59..ce8400941 100644 --- a/zebra/redistribute.h +++ b/zebra/redistribute.h @@ -26,11 +26,13 @@ #include "table.h" #include "zserv.h" -extern void zebra_redistribute_add (int, struct zserv *, int); -extern void zebra_redistribute_delete (int, struct zserv *, int); +extern void zebra_redistribute_add (int, struct zserv *, int, vrf_id_t); +extern void zebra_redistribute_delete (int, struct zserv *, int, vrf_id_t); -extern void zebra_redistribute_default_add (int, struct zserv *, int); -extern void zebra_redistribute_default_delete (int, struct zserv *, int); +extern void zebra_redistribute_default_add (int, struct zserv *, int, + vrf_id_t); +extern void zebra_redistribute_default_delete (int, struct zserv *, int, + vrf_id_t); extern void redistribute_add (struct prefix *, struct rib *); extern void redistribute_delete (struct prefix *, struct rib *); @@ -46,7 +48,11 @@ extern void zebra_interface_address_add_update (struct interface *, extern void zebra_interface_address_delete_update (struct interface *, struct connected *c); +extern void zebra_interface_parameters_update (struct interface *); + extern int zebra_check_addr (struct prefix *); +extern int is_default (struct prefix *); + #endif /* _ZEBRA_REDISTRIBUTE_H */ diff --git a/zebra/redistribute_null.c b/zebra/redistribute_null.c index e57a73b9c..5584d12ca 100644 --- a/zebra/redistribute_null.c +++ b/zebra/redistribute_null.c @@ -1,26 +1,84 @@ +/* + * 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" #include "zebra/redistribute.h" -void zebra_redistribute_add (int a, struct zserv *b, int c) +void zebra_redistribute_add (int a, struct zserv *b, int c, + vrf_id_t vrf_id) { 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, + vrf_id_t vrf_id) +{ return; } +void zebra_redistribute_default_add (int a, struct zserv *b, int c, + vrf_id_t vrf_id) +{ return; } +void zebra_redistribute_default_delete (int a, struct zserv *b, int c, + vrf_id_t vrf_id) +{ 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 + +/* Interface parameters update */ +void zebra_interface_parameters_update (struct interface *ifp) +{ return; }; diff --git a/zebra/rib.h b/zebra/rib.h index 2872fc030..0191f57ec 100644 --- a/zebra/rib.h +++ b/zebra/rib.h @@ -23,25 +23,17 @@ #ifndef _ZEBRA_RIB_H #define _ZEBRA_RIB_H +#include "zebra.h" +#include "linklist.h" #include "prefix.h" +#include "table.h" +#include "queue.h" +#include "nexthop.h" #define DISTANCE_INFINITY 255 -/* Routing information base. */ - -union g_addr { - struct in_addr ipv4; -#ifdef HAVE_IPV6 - struct in6_addr ipv6; -#endif /* HAVE_IPV6 */ -}; - 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; @@ -52,18 +44,28 @@ struct rib /* Refrence count. */ unsigned long refcnt; + /* Tag */ + route_tag_t tag; + /* Uptime. */ time_t uptime; /* Type fo this route. */ int type; + /* VRF identifier. */ + vrf_id_t vrf_id; + /* Which routing table */ int table; /* Metric */ u_int32_t metric; + /* MTU */ + u_int32_t mtu; + u_int32_t nexthop_mtu; + /* Distance. */ u_char distance; @@ -76,6 +78,8 @@ struct rib /* RIB internal status */ u_char status; #define RIB_ENTRY_REMOVED (1 << 0) +#define RIB_ENTRY_CHANGED (1 << 1) +#define RIB_ENTRY_SELECTED_FIB (1 << 2) /* Nexthop information. */ u_char nexthop_num; @@ -97,56 +101,101 @@ struct meta_queue u_int32_t size; /* sum of lengths of all subqueues */ }; -/* Static route information. */ -struct static_ipv4 +/* + * Structure that represents a single destination (prefix). + */ +typedef struct rib_dest_t_ { - /* For linked list. */ - struct static_ipv4 *prev; - struct static_ipv4 *next; - /* Administrative distance. */ - u_char distance; + /* + * 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; - /* Flag for this static route's type. */ - u_char type; -#define STATIC_IPV4_GATEWAY 1 -#define STATIC_IPV4_IFNAME 2 -#define STATIC_IPV4_BLACKHOLE 3 + /* + * Doubly-linked list of routes for this prefix. + */ + struct rib *routes; - /* Nexthop value. */ - union - { - struct in_addr ipv4; - char *ifname; - } gate; + /* + * 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)) - /* bit flags */ - u_char flags; /* - see ZEBRA_FLAG_REJECT - ZEBRA_FLAG_BLACKHOLE + * 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) -#ifdef HAVE_IPV6 /* Static route information. */ -struct static_ipv6 +struct static_route { /* For linked list. */ - struct static_ipv6 *prev; - struct static_ipv6 *next; + struct static_route *prev; + struct static_route *next; + + /* VRF identifier. */ + vrf_id_t vrf_id; /* Administrative distance. */ u_char distance; + /* Tag */ + route_tag_t tag; + /* Flag for this static route's type. */ u_char type; -#define STATIC_IPV6_GATEWAY 1 -#define STATIC_IPV6_GATEWAY_IFNAME 2 -#define STATIC_IPV6_IFNAME 3 +#define STATIC_IPV4_GATEWAY 1 +#define STATIC_IPV4_IFNAME 2 +#define STATIC_IPV4_BLACKHOLE 3 +#define STATIC_IPV6_GATEWAY 4 +#define STATIC_IPV6_GATEWAY_IFNAME 5 +#define STATIC_IPV6_IFNAME 6 /* Nexthop value. */ - struct in6_addr ipv6; + union g_addr addr; char *ifname; /* bit flags */ @@ -156,53 +205,92 @@ struct static_ipv6 ZEBRA_FLAG_BLACKHOLE */ }; -#endif /* HAVE_IPV6 */ -enum nexthop_types_t +/* 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))) + +/* Structure holding nexthop & VRF identifier, + * used for applying the route-map. */ +struct nexthop_vrfid { - NEXTHOP_TYPE_IFINDEX = 1, /* Directly connected. */ - NEXTHOP_TYPE_IFNAME, /* Interface route. */ - NEXTHOP_TYPE_IPV4, /* IPv4 nexthop. */ - NEXTHOP_TYPE_IPV4_IFINDEX, /* IPv4 nexthop with ifindex. */ - NEXTHOP_TYPE_IPV4_IFNAME, /* IPv4 nexthop with ifname. */ - NEXTHOP_TYPE_IPV6, /* IPv6 nexthop. */ - NEXTHOP_TYPE_IPV6_IFINDEX, /* IPv6 nexthop with ifindex. */ - NEXTHOP_TYPE_IPV6_IFNAME, /* IPv6 nexthop with ifname. */ - NEXTHOP_TYPE_BLACKHOLE, /* Null0 nexthop. */ + struct nexthop *nexthop; + vrf_id_t vrf_id; }; -/* Nexthop structure. */ -struct nexthop + +#if defined (HAVE_RTADV) +/* Structure which hold status of router advertisement. */ +struct rtadv { - struct nexthop *next; - struct nexthop *prev; + int sock; - /* Interface index. */ - char *ifname; - unsigned int ifindex; - - enum nexthop_types_t type; + int adv_if_count; + int adv_msec_if_count; - u_char flags; -#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. */ - - /* Nexthop address or interface name. */ - union g_addr gate; - - /* Recursive lookup nexthop. */ - u_char rtype; - unsigned int rifindex; - union g_addr rgate; - union g_addr src; + struct thread *ra_read; + struct thread *ra_timer; +}; +#endif /* HAVE_RTADV */ + +#ifdef HAVE_NETLINK +/* Socket interface to kernel */ +struct nlsock +{ + int sock; + int seq; + struct sockaddr_nl snl; + const char *name; }; +#endif /* Routing table instance. */ -struct vrf +struct zebra_vrf { - /* Identifier. This is same as routing table vector index. */ - u_int32_t id; + /* Identifier. */ + vrf_id_t vrf_id; /* Routing table name. */ char *name; @@ -218,89 +306,298 @@ struct vrf /* Static route configuration. */ struct route_table *stable[AFI_MAX][SAFI_MAX]; + +#ifdef HAVE_NETLINK + struct nlsock netlink; /* kernel messages */ + struct nlsock netlink_cmd; /* command channel */ + struct thread *t_netlink; +#endif + + /* 2nd pointer type used primarily to quell a warning on + * ALL_LIST_ELEMENTS_RO + */ + struct list _rid_all_sorted_list; + struct list _rid_lo_sorted_list; + struct list *rid_all_sorted_list; + struct list *rid_lo_sorted_list; + struct prefix rid_user_assigned; + +#if defined (HAVE_RTADV) + struct rtadv rtadv; +#endif /* HAVE_RTADV */ + + /* Recursive Nexthop table */ + struct route_table *rnh_table[AFI_MAX]; }; -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 *); -extern struct nexthop *nexthop_ipv4_add (struct rib *, struct in_addr *, - struct in_addr *); +/* + * 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 zebra_vrf. + */ + struct zebra_vrf *zvrf; + 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_ +{ + vrf_id_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 *rib_nexthop_ifindex_add (struct rib *, ifindex_t); +extern struct nexthop *rib_nexthop_ifname_add (struct rib *, char *); +extern struct nexthop *rib_nexthop_blackhole_add (struct rib *); +extern struct nexthop *rib_nexthop_ipv4_add (struct rib *, struct in_addr *, + struct in_addr *); +extern struct nexthop *rib_nexthop_ipv4_ifindex_add (struct rib *, + struct in_addr *, + struct in_addr *, + ifindex_t); + +extern void rib_nexthop_add (struct rib *rib, struct nexthop *nexthop); + +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 *); -extern int rib_lookup_ipv4_route (struct prefix_ipv4 *, union sockunion *); +#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 *, + vrf_id_t); #define ZEBRA_RIB_LOOKUP_ERROR -1 #define ZEBRA_RIB_FOUND_EXACT 0 #define ZEBRA_RIB_FOUND_NOGATE 1 #define ZEBRA_RIB_FOUND_CONNECTED 2 #define ZEBRA_RIB_NOTFOUND 3 -#ifdef HAVE_IPV6 -extern struct nexthop *nexthop_ipv6_add (struct rib *, struct in6_addr *); -#endif /* HAVE_IPV6 */ +extern struct nexthop *rib_nexthop_ipv6_add (struct rib *, struct in6_addr *); +extern struct nexthop *rib_nexthop_ipv6_ifindex_add (struct rib *, + struct in6_addr *, + ifindex_t); -extern struct vrf *vrf_lookup (u_int32_t); -extern struct route_table *vrf_table (afi_t afi, safi_t safi, u_int32_t id); -extern struct route_table *vrf_static_table (afi_t afi, safi_t safi, u_int32_t id); +extern struct zebra_vrf *zebra_vrf_lookup (vrf_id_t vrf_id); +extern struct zebra_vrf *zebra_vrf_alloc (vrf_id_t); +extern struct route_table *zebra_vrf_table (afi_t, safi_t, vrf_id_t); +extern struct route_table *zebra_vrf_static_table (afi_t, safi_t, vrf_id_t); /* NOTE: * All rib_add_ipv[46]* functions will not just add prefix into RIB, but * also implicitly withdraw equal prefix of same type. */ extern int rib_add_ipv4 (int type, int flags, struct prefix_ipv4 *p, struct in_addr *gate, struct in_addr *src, - unsigned int ifindex, u_int32_t vrf_id, - u_int32_t, u_char, safi_t); + ifindex_t ifindex, vrf_id_t vrf_id, int table_id, + u_int32_t, u_int32_t, u_char, safi_t); extern int rib_add_ipv4_multipath (struct prefix_ipv4 *, struct rib *, safi_t); 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); + struct in_addr *gate, ifindex_t ifindex, + vrf_id_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, + vrf_id_t); +extern struct rib *rib_match_ipv4_multicast (struct in_addr addr, + struct route_node **rn_out, + vrf_id_t); -extern struct rib *rib_lookup_ipv4 (struct prefix_ipv4 *); +extern struct rib *rib_lookup_ipv4 (struct prefix_ipv4 *, vrf_id_t); -extern void rib_update (void); +extern void rib_update (vrf_id_t); extern void rib_weed_tables (void); extern void rib_sweep_route (void); +extern void rib_close_table (struct route_table *); extern void rib_close (void); 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, route_tag_t, + u_char distance, vrf_id_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, route_tag_t tag, u_char distance, + vrf_id_t vrf_id); -#ifdef HAVE_IPV6 extern int rib_add_ipv6 (int type, int flags, struct prefix_ipv6 *p, - struct in6_addr *gate, unsigned int ifindex, u_int32_t vrf_id, - u_int32_t metric, u_char distance, safi_t safi); + struct in6_addr *gate, ifindex_t ifindex, vrf_id_t vrf_id, + int table_id, u_int32_t metric, u_int32_t mtu, + u_char distance, safi_t safi); extern int rib_delete_ipv6 (int type, int flags, struct prefix_ipv6 *p, - struct in6_addr *gate, unsigned int ifindex, u_int32_t vrf_id, safi_t safi); + struct in6_addr *gate, ifindex_t ifindex, vrf_id_t vrf_id, safi_t safi); -extern struct rib *rib_lookup_ipv6 (struct in6_addr *); +extern struct rib *rib_lookup_ipv6 (struct in6_addr *, vrf_id_t); -extern struct rib *rib_match_ipv6 (struct in6_addr *); +extern struct rib *rib_match_ipv6 (struct in6_addr *, vrf_id_t); extern struct route_table *rib_table_ipv6; extern int static_add_ipv6 (struct prefix *p, u_char type, struct in6_addr *gate, - const char *ifname, u_char flags, u_char distance, - u_int32_t vrf_id); + const char *ifname, u_char flags, route_tag_t, + u_char distance, vrf_id_t vrf_id); + +extern int +rib_add_ipv6_multipath (struct prefix_ipv6 *, struct rib *, safi_t); extern int static_delete_ipv6 (struct prefix *p, u_char type, struct in6_addr *gate, - const char *ifname, u_char distance, u_int32_t vrf_id); + const char *ifname, route_tag_t, u_char distance, + vrf_id_t vrf_id); + +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; +} -#endif /* HAVE_IPV6 */ +/* + * rib_dest_vrf + */ +static inline struct zebra_vrf * +rib_dest_vrf (rib_dest_t *dest) +{ + return rib_table_info (rib_dest_table (dest))->zvrf; +} + +/* + * 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 3d6b511c5..a4eb73ae5 100644 --- a/zebra/router-id.c +++ b/zebra/router-id.c @@ -36,15 +36,12 @@ #include "log.h" #include "table.h" #include "rib.h" +#include "vrf.h" #include "zebra/zserv.h" #include "zebra/router-id.h" #include "zebra/redistribute.h" -static struct list rid_all_sorted_list; -static struct list rid_lo_sorted_list; -static struct prefix rid_user_assigned; - /* master zebra server structure */ extern struct zebra_t zebrad; @@ -75,44 +72,55 @@ router_id_bad_address (struct connected *ifc) } void -router_id_get (struct prefix *p) +router_id_get (struct prefix *p, vrf_id_t vrf_id) { struct listnode *node; struct connected *c; + struct zebra_vrf *zvrf = vrf_info_get (vrf_id); p->u.prefix4.s_addr = 0; p->family = AF_INET; p->prefixlen = 32; - 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)) + if (zvrf->rid_user_assigned.u.prefix4.s_addr) + p->u.prefix4.s_addr = zvrf->rid_user_assigned.u.prefix4.s_addr; + else if (!list_isempty (zvrf->rid_lo_sorted_list)) { - node = listtail (&rid_lo_sorted_list); + node = listtail (zvrf->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 (zvrf->rid_all_sorted_list)) { - node = listtail (&rid_all_sorted_list); + node = listtail (zvrf->rid_all_sorted_list); c = listgetdata (node); p->u.prefix4.s_addr = c->address->u.prefix4.s_addr; } } static void -router_id_set (struct prefix *p) +router_id_set (struct prefix *p, vrf_id_t vrf_id) { struct prefix p2; struct listnode *node; struct zserv *client; + struct zebra_vrf *zvrf; + + if (p->u.prefix4.s_addr == 0) /* unset */ + { + zvrf = vrf_info_lookup (vrf_id); + if (! zvrf) + return; + } + else /* set */ + zvrf = vrf_info_get (vrf_id); - rid_user_assigned.u.prefix4.s_addr = p->u.prefix4.s_addr; + zvrf->rid_user_assigned.u.prefix4.s_addr = p->u.prefix4.s_addr; - router_id_get (&p2); + router_id_get (&p2, vrf_id); for (ALL_LIST_ELEMENTS_RO (zebrad.client_list, node, client)) - zsend_router_id_update (client, &p2); + zsend_router_id_update (client, &p2, vrf_id); } void @@ -123,28 +131,29 @@ router_id_add_address (struct connected *ifc) struct prefix before; struct prefix after; struct zserv *client; + struct zebra_vrf *zvrf = vrf_info_get (ifc->ifp->vrf_id); if (router_id_bad_address (ifc)) return; - router_id_get (&before); + router_id_get (&before, zvrf->vrf_id); if (!strncmp (ifc->ifp->name, "lo", 2) || !strncmp (ifc->ifp->name, "dummy", 5)) - l = &rid_lo_sorted_list; + l = zvrf->rid_lo_sorted_list; else - l = &rid_all_sorted_list; + l = zvrf->rid_all_sorted_list; if (!router_id_find_node (l, ifc)) listnode_add_sort (l, ifc); - router_id_get (&after); + router_id_get (&after, zvrf->vrf_id); if (prefix_same (&before, &after)) return; for (ALL_LIST_ELEMENTS_RO (zebrad.client_list, node, client)) - zsend_router_id_update (client, &after); + zsend_router_id_update (client, &after, zvrf->vrf_id); } void @@ -156,36 +165,51 @@ router_id_del_address (struct connected *ifc) struct prefix before; struct listnode *node; struct zserv *client; + struct zebra_vrf *zvrf = vrf_info_get (ifc->ifp->vrf_id); if (router_id_bad_address (ifc)) return; - router_id_get (&before); + router_id_get (&before, zvrf->vrf_id); if (!strncmp (ifc->ifp->name, "lo", 2) || !strncmp (ifc->ifp->name, "dummy", 5)) - l = &rid_lo_sorted_list; + l = zvrf->rid_lo_sorted_list; else - l = &rid_all_sorted_list; + l = zvrf->rid_all_sorted_list; if ((c = router_id_find_node (l, ifc))) listnode_delete (l, c); - router_id_get (&after); + router_id_get (&after, zvrf->vrf_id); if (prefix_same (&before, &after)) return; for (ALL_LIST_ELEMENTS_RO (zebrad.client_list, node, client)) - zsend_router_id_update (client, &after); + zsend_router_id_update (client, &after, zvrf->vrf_id); } void router_id_write (struct vty *vty) { - if (rid_user_assigned.u.prefix4.s_addr) - vty_out (vty, "router-id %s%s", inet_ntoa (rid_user_assigned.u.prefix4), - VTY_NEWLINE); + struct zebra_vrf *zvrf; + vrf_iter_t iter; + + for (iter = vrf_first (); iter != VRF_ITER_INVALID; iter = vrf_next (iter)) + if ((zvrf = vrf_iter2info (iter)) != NULL) + if (zvrf->rid_user_assigned.u.prefix4.s_addr) + { + if (zvrf->vrf_id == VRF_DEFAULT) + vty_out (vty, "router-id %s%s", + inet_ntoa (zvrf->rid_user_assigned.u.prefix4), + VTY_NEWLINE); + else + vty_out (vty, "router-id %s vrf %u%s", + inet_ntoa (zvrf->rid_user_assigned.u.prefix4), + zvrf->vrf_id, + VTY_NEWLINE); + } } DEFUN (router_id, @@ -195,6 +219,7 @@ DEFUN (router_id, "IP address to use for router-id\n") { struct prefix rid; + vrf_id_t vrf_id = VRF_DEFAULT; rid.u.prefix4.s_addr = inet_addr (argv[0]); if (!rid.u.prefix4.s_addr) @@ -203,11 +228,21 @@ DEFUN (router_id, rid.prefixlen = 32; rid.family = AF_INET; - router_id_set (&rid); + if (argc > 1) + VTY_GET_INTEGER ("VRF ID", vrf_id, argv[1]); + + router_id_set (&rid, vrf_id); return CMD_SUCCESS; } +ALIAS (router_id, + router_id_vrf_cmd, + "router-id A.B.C.D " VRF_CMD_STR, + "Manually set the router-id\n" + "IP address to use for router-id\n" + VRF_CMD_HELP_STR) + DEFUN (no_router_id, no_router_id_cmd, "no router-id", @@ -215,40 +250,58 @@ DEFUN (no_router_id, "Remove the manually configured router-id\n") { struct prefix rid; + vrf_id_t vrf_id = VRF_DEFAULT; rid.u.prefix4.s_addr = 0; rid.prefixlen = 0; rid.family = AF_INET; - router_id_set (&rid); + if (argc > 0) + VTY_GET_INTEGER ("VRF ID", vrf_id, argv[0]); + + router_id_set (&rid, vrf_id); return CMD_SUCCESS; } +ALIAS (no_router_id, + no_router_id_vrf_cmd, + "no router-id " VRF_CMD_STR, + NO_STR + "Remove the manually configured router-id\n" + VRF_CMD_HELP_STR) + static int 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 -router_id_init (void) +router_id_cmd_init (void) { install_element (CONFIG_NODE, &router_id_cmd); install_element (CONFIG_NODE, &no_router_id_cmd); + install_element (CONFIG_NODE, &router_id_vrf_cmd); + install_element (CONFIG_NODE, &no_router_id_vrf_cmd); +} + +void +router_id_init (struct zebra_vrf *zvrf) +{ + zvrf->rid_all_sorted_list = &zvrf->_rid_all_sorted_list; + zvrf->rid_lo_sorted_list = &zvrf->_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)); + memset (zvrf->rid_all_sorted_list, 0, sizeof (zvrf->_rid_all_sorted_list)); + memset (zvrf->rid_lo_sorted_list, 0, sizeof (zvrf->_rid_lo_sorted_list)); + memset (&zvrf->rid_user_assigned, 0, sizeof (zvrf->rid_user_assigned)); - rid_all_sorted_list.cmp = router_id_cmp; - rid_lo_sorted_list.cmp = router_id_cmp; + zvrf->rid_all_sorted_list->cmp = router_id_cmp; + zvrf->rid_lo_sorted_list->cmp = router_id_cmp; - rid_user_assigned.family = AF_INET; - rid_user_assigned.prefixlen = 32; + zvrf->rid_user_assigned.family = AF_INET; + zvrf->rid_user_assigned.prefixlen = 32; } diff --git a/zebra/router-id.h b/zebra/router-id.h index be12bf502..46d300eea 100644 --- a/zebra/router-id.h +++ b/zebra/router-id.h @@ -33,8 +33,9 @@ extern void router_id_add_address(struct connected *); extern void router_id_del_address(struct connected *); -extern void router_id_init(void); +extern void router_id_init(struct zebra_vrf *); +extern void router_id_cmd_init(void); extern void router_id_write(struct vty *); -extern void router_id_get(struct prefix *); +extern void router_id_get(struct prefix *, vrf_id_t); #endif diff --git a/zebra/rt.h b/zebra/rt.h index 8bfe5a429..8c1c476d6 100644 --- a/zebra/rt.h +++ b/zebra/rt.h @@ -27,18 +27,9 @@ #include "if.h" #include "zebra/rib.h" -extern int kernel_add_ipv4 (struct prefix *, struct rib *); -extern int kernel_delete_ipv4 (struct prefix *, struct rib *); +extern int kernel_route_rib (struct prefix *, struct rib *, struct rib *); extern int kernel_add_route (struct prefix_ipv4 *, struct in_addr *, int, int); extern int kernel_address_add_ipv4 (struct interface *, struct connected *); 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 */ - #endif /* _ZEBRA_RT_H */ diff --git a/zebra/rt_ioctl.c b/zebra/rt_ioctl.c deleted file mode 100644 index a5d588c7f..000000000 --- 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 f48df2bf9..fc6e373db 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -20,6 +20,7 @@ */ #include +#include /* Hack for GNU libc version 2. */ #ifndef MSG_TRUNC @@ -32,9 +33,12 @@ #include "prefix.h" #include "connected.h" #include "table.h" +#include "memory.h" #include "rib.h" #include "thread.h" #include "privs.h" +#include "vrf.h" +#include "nexthop.h" #include "zebra/zserv.h" #include "zebra/rt.h" @@ -42,15 +46,7 @@ #include "zebra/interface.h" #include "zebra/debug.h" -/* Socket interface to kernel */ -struct nlsock -{ - int sock; - int seq; - struct sockaddr_nl snl; - const char *name; -} netlink = { -1, 0, {0}, "netlink-listen"}, /* kernel messages */ - netlink_cmd = { -1, 0, {0}, "netlink-cmd"}; /* command channel */ +#include "rt_netlink.h" static const struct message nlmsg_str[] = { {RTM_NEWROUTE, "RTM_NEWROUTE"}, @@ -65,30 +61,21 @@ 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; extern u_int32_t nl_rcvbufsize; +static struct { + char *p; + size_t size; +} nl_rcvbuf; + /* Note: on netlink systems, there should be a 1-to-1 mapping between interface names and ifindex values. */ static void -set_ifindex(struct interface *ifp, unsigned int ifi_index) +set_ifindex(struct interface *ifp, ifindex_t ifi_index) { struct interface *oifp; @@ -112,6 +99,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) { @@ -128,8 +119,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, @@ -153,7 +152,7 @@ netlink_recvbuf (struct nlsock *nl, uint32_t newsize) /* Make socket for Linux netlink interface. */ static int -netlink_socket (struct nlsock *nl, unsigned long groups) +netlink_socket (struct nlsock *nl, unsigned long groups, vrf_id_t vrf_id) { int ret; struct sockaddr_nl snl; @@ -161,7 +160,13 @@ netlink_socket (struct nlsock *nl, unsigned long groups) int namelen; int save_errno; - sock = socket (AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (zserv_privs.change (ZPRIVS_RAISE)) + { + zlog (NULL, LOG_ERR, "Can't raise privileges"); + return -1; + } + + sock = vrf_socket (AF_NETLINK, SOCK_RAW, NETLINK_ROUTE, vrf_id); if (sock < 0) { zlog (NULL, LOG_ERR, "Can't open %s socket: %s", nl->name, @@ -174,12 +179,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)) @@ -271,8 +270,9 @@ netlink_request (int family, int type, struct nlsock *nl) /* Receive message from netlink interface and pass those information to the given function. */ static int -netlink_parse_info (int (*filter) (struct sockaddr_nl *, struct nlmsghdr *), - struct nlsock *nl) +netlink_parse_info (int (*filter) (struct sockaddr_nl *, struct nlmsghdr *, + vrf_id_t), + struct nlsock *nl, struct zebra_vrf *zvrf) { int status; int ret = 0; @@ -280,10 +280,17 @@ netlink_parse_info (int (*filter) (struct sockaddr_nl *, struct nlmsghdr *), while (1) { - char buf[4096]; - struct iovec iov = { buf, sizeof buf }; + struct iovec iov = { + .iov_base = nl_rcvbuf.p, + .iov_len = nl_rcvbuf.size, + }; 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); @@ -311,7 +318,8 @@ netlink_parse_info (int (*filter) (struct sockaddr_nl *, struct nlmsghdr *), return -1; } - for (h = (struct nlmsghdr *) buf; NLMSG_OK (h, (unsigned int) status); + for (h = (struct nlmsghdr *) nl_rcvbuf.p; + NLMSG_OK (h, (unsigned int) status); h = NLMSG_NEXT (h, status)) { /* Finish of reading. */ @@ -353,7 +361,7 @@ netlink_parse_info (int (*filter) (struct sockaddr_nl *, struct nlmsghdr *), } /* Deal with errors that occur because of races in link handling */ - if (nl == &netlink_cmd + if (nl == &zvrf->netlink_cmd && ((msg_type == RTM_DELROUTE && (-errnum == ENODEV || -errnum == ESRCH)) || (msg_type == RTM_NEWROUTE && -errnum == EEXIST))) @@ -380,16 +388,20 @@ 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 != &zvrf->netlink_cmd + && h->nlmsg_pid == zvrf->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", - netlink_cmd.name, nl->name); + zvrf->netlink_cmd.name, nl->name); continue; } - error = (*filter) (&snl, h); + error = (*filter) (&snl, h, zvrf->vrf_id); if (error < 0) { zlog (NULL, LOG_ERR, "%s filter function error", nl->name); @@ -400,7 +412,9 @@ netlink_parse_info (int (*filter) (struct sockaddr_nl *, struct nlmsghdr *), /* After error care. */ if (msg.msg_flags & MSG_TRUNC) { - zlog (NULL, LOG_ERR, "%s error: message truncated", nl->name); + zlog (NULL, LOG_ERR, "%s error: message truncated!", nl->name); + zlog (NULL, LOG_ERR, + "Must restart with larger --nl-bufsize value!"); continue; } if (status) @@ -426,17 +440,110 @@ netlink_parse_rtattr (struct rtattr **tb, int max, struct rtattr *rta, } } +/* Utility function to parse hardware link-layer address and update ifp */ +static void +netlink_interface_update_hw_addr (struct rtattr **tb, struct interface *ifp) +{ + int i; + + if (tb[IFLA_ADDRESS]) + { + int hw_addr_len; + + hw_addr_len = RTA_PAYLOAD (tb[IFLA_ADDRESS]); + + if (hw_addr_len > INTERFACE_HWADDR_MAX) + zlog_warn ("Hardware address is too large: %d", hw_addr_len); + else + { + ifp->hw_addr_len = hw_addr_len; + memcpy (ifp->hw_addr, RTA_DATA (tb[IFLA_ADDRESS]), hw_addr_len); + + for (i = 0; i < hw_addr_len; i++) + if (ifp->hw_addr[i] != 0) + break; + + if (i == hw_addr_len) + ifp->hw_addr_len = 0; + else + ifp->hw_addr_len = hw_addr_len; + } + } +} + +static enum zebra_link_type +netlink_to_zebra_link_type (unsigned int hwt) +{ + switch (hwt) + { + case ARPHRD_ETHER: return ZEBRA_LLT_ETHER; + case ARPHRD_EETHER: return ZEBRA_LLT_EETHER; + case ARPHRD_AX25: return ZEBRA_LLT_AX25; + case ARPHRD_PRONET: return ZEBRA_LLT_PRONET; + case ARPHRD_IEEE802: return ZEBRA_LLT_IEEE802; + case ARPHRD_ARCNET: return ZEBRA_LLT_ARCNET; + case ARPHRD_APPLETLK: return ZEBRA_LLT_APPLETLK; + case ARPHRD_DLCI: return ZEBRA_LLT_DLCI; + case ARPHRD_ATM: return ZEBRA_LLT_ATM; + case ARPHRD_METRICOM: return ZEBRA_LLT_METRICOM; + case ARPHRD_IEEE1394: return ZEBRA_LLT_IEEE1394; + case ARPHRD_EUI64: return ZEBRA_LLT_EUI64; + case ARPHRD_INFINIBAND: return ZEBRA_LLT_INFINIBAND; + case ARPHRD_SLIP: return ZEBRA_LLT_SLIP; + case ARPHRD_CSLIP: return ZEBRA_LLT_CSLIP; + case ARPHRD_SLIP6: return ZEBRA_LLT_SLIP6; + case ARPHRD_CSLIP6: return ZEBRA_LLT_CSLIP6; + case ARPHRD_RSRVD: return ZEBRA_LLT_RSRVD; + case ARPHRD_ADAPT: return ZEBRA_LLT_ADAPT; + case ARPHRD_ROSE: return ZEBRA_LLT_ROSE; + case ARPHRD_X25: return ZEBRA_LLT_X25; + case ARPHRD_PPP: return ZEBRA_LLT_PPP; + case ARPHRD_CISCO: return ZEBRA_LLT_CHDLC; + case ARPHRD_LAPB: return ZEBRA_LLT_LAPB; + case ARPHRD_RAWHDLC: return ZEBRA_LLT_RAWHDLC; + case ARPHRD_TUNNEL: return ZEBRA_LLT_IPIP; + case ARPHRD_TUNNEL6: return ZEBRA_LLT_IPIP6; + case ARPHRD_FRAD: return ZEBRA_LLT_FRAD; + case ARPHRD_SKIP: return ZEBRA_LLT_SKIP; + case ARPHRD_LOOPBACK: return ZEBRA_LLT_LOOPBACK; + case ARPHRD_LOCALTLK: return ZEBRA_LLT_LOCALTLK; + case ARPHRD_FDDI: return ZEBRA_LLT_FDDI; + case ARPHRD_SIT: return ZEBRA_LLT_SIT; + case ARPHRD_IPDDP: return ZEBRA_LLT_IPDDP; + case ARPHRD_IPGRE: return ZEBRA_LLT_IPGRE; + case ARPHRD_PIMREG: return ZEBRA_LLT_PIMREG; + case ARPHRD_HIPPI: return ZEBRA_LLT_HIPPI; + case ARPHRD_ECONET: return ZEBRA_LLT_ECONET; + case ARPHRD_IRDA: return ZEBRA_LLT_IRDA; + case ARPHRD_FCPP: return ZEBRA_LLT_FCPP; + case ARPHRD_FCAL: return ZEBRA_LLT_FCAL; + case ARPHRD_FCPL: return ZEBRA_LLT_FCPL; + case ARPHRD_FCFABRIC: return ZEBRA_LLT_FCFABRIC; + case ARPHRD_IEEE802_TR: return ZEBRA_LLT_IEEE802_TR; + case ARPHRD_IEEE80211: return ZEBRA_LLT_IEEE80211; + case ARPHRD_IEEE802154: return ZEBRA_LLT_IEEE802154; +#ifdef ARPHRD_IP6GRE + case ARPHRD_IP6GRE: return ZEBRA_LLT_IP6GRE; +#endif +#ifdef ARPHRD_IEEE802154_PHY + case ARPHRD_IEEE802154_PHY: return ZEBRA_LLT_IEEE802154_PHY; +#endif + + default: return ZEBRA_LLT_UNKNOWN; + } +} + /* Called from interface_lookup_netlink(). This function is only used during bootstrap. */ static int -netlink_interface (struct sockaddr_nl *snl, struct nlmsghdr *h) +netlink_interface (struct sockaddr_nl *snl, struct nlmsghdr *h, + vrf_id_t vrf_id) { int len; struct ifinfomsg *ifi; struct rtattr *tb[IFLA_MAX + 1]; struct interface *ifp; char *name; - int i; ifi = NLMSG_DATA (h); @@ -466,38 +573,15 @@ netlink_interface (struct sockaddr_nl *snl, struct nlmsghdr *h) name = (char *) RTA_DATA (tb[IFLA_IFNAME]); /* Add interface. */ - ifp = if_get_by_name (name); + ifp = if_get_by_name_vrf (name, vrf_id); 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; - - if (tb[IFLA_ADDRESS]) - { - int hw_addr_len; - - hw_addr_len = RTA_PAYLOAD (tb[IFLA_ADDRESS]); - - if (hw_addr_len > INTERFACE_HWADDR_MAX) - zlog_warn ("Hardware address is too large: %d", hw_addr_len); - else - { - ifp->hw_addr_len = hw_addr_len; - memcpy (ifp->hw_addr, RTA_DATA (tb[IFLA_ADDRESS]), hw_addr_len); - - for (i = 0; i < hw_addr_len; i++) - if (ifp->hw_addr[i] != 0) - break; - - if (i == hw_addr_len) - ifp->hw_addr_len = 0; - else - ifp->hw_addr_len = hw_addr_len; - } - } + ifp->ll_type = netlink_to_zebra_link_type (ifi->ifi_type); + netlink_interface_update_hw_addr (tb, ifp); if_add_update (ifp); @@ -506,7 +590,8 @@ netlink_interface (struct sockaddr_nl *snl, struct nlmsghdr *h) /* Lookup interface IPv4/IPv6 address. */ static int -netlink_interface_addr (struct sockaddr_nl *snl, struct nlmsghdr *h) +netlink_interface_addr (struct sockaddr_nl *snl, struct nlmsghdr *h, + vrf_id_t vrf_id) { int len; struct ifaddrmsg *ifa; @@ -536,19 +621,19 @@ netlink_interface_addr (struct sockaddr_nl *snl, struct nlmsghdr *h) memset (tb, 0, sizeof tb); netlink_parse_rtattr (tb, IFA_MAX, IFA_RTA (ifa), len); - ifp = if_lookup_by_index (ifa->ifa_index); + ifp = if_lookup_by_index_vrf (ifa->ifa_index, vrf_id); if (ifp == NULL) { - zlog_err ("netlink_interface_addr can't find interface by index %d", - ifa->ifa_index); + zlog_err ("netlink_interface_addr can't find interface by index %d vrf %u", + ifa->ifa_index, vrf_id); return -1; } if (IS_ZEBRA_DEBUG_KERNEL) /* remove this line to see initial ifcfg */ { char buf[BUFSIZ]; - zlog_debug ("netlink_interface_addr %s %s:", - lookup (nlmsg_str, h->nlmsg_type), ifp->name); + zlog_debug ("netlink_interface_addr %s %s vrf %u:", + lookup (nlmsg_str, h->nlmsg_type), ifp->name, vrf_id); if (tb[IFA_LOCAL]) zlog_debug (" IFA_LOCAL %s/%d", inet_ntop (ifa->ifa_family, RTA_DATA (tb[IFA_LOCAL]), @@ -641,7 +726,8 @@ netlink_interface_addr (struct sockaddr_nl *snl, struct nlmsghdr *h) /* Looking up routing table by netlink interface. */ static int -netlink_routing_table (struct sockaddr_nl *snl, struct nlmsghdr *h) +netlink_routing_table (struct sockaddr_nl *snl, struct nlmsghdr *h, + vrf_id_t vrf_id) { int len; struct rtmsg *rtm; @@ -652,7 +738,7 @@ netlink_routing_table (struct sockaddr_nl *snl, struct nlmsghdr *h) int index; int table; - int metric; + u_int32_t mtu = 0; void *dest; void *gate; @@ -693,7 +779,6 @@ netlink_routing_table (struct sockaddr_nl *snl, struct nlmsghdr *h) flags |= ZEBRA_FLAG_SELFROUTE; index = 0; - metric = 0; dest = NULL; gate = NULL; src = NULL; @@ -709,12 +794,20 @@ netlink_routing_table (struct sockaddr_nl *snl, struct nlmsghdr *h) if (tb[RTA_PREFSRC]) src = RTA_DATA (tb[RTA_PREFSRC]); - /* Multipath treatment is needed. */ if (tb[RTA_GATEWAY]) gate = RTA_DATA (tb[RTA_GATEWAY]); - if (tb[RTA_PRIORITY]) - metric = *(int *) RTA_DATA(tb[RTA_PRIORITY]); + if (tb[RTA_METRICS]) + { + struct rtattr *mxrta[RTAX_MAX+1]; + + memset (mxrta, 0, sizeof mxrta); + netlink_parse_rtattr (mxrta, RTAX_MAX, RTA_DATA(tb[RTA_METRICS]), + RTA_PAYLOAD(tb[RTA_METRICS])); + + if (mxrta[RTAX_MTU]) + mtu = *(u_int32_t *) RTA_DATA(mxrta[RTAX_MTU]); + } if (rtm->rtm_family == AF_INET) { @@ -723,7 +816,65 @@ netlink_routing_table (struct sockaddr_nl *snl, struct nlmsghdr *h) memcpy (&p.prefix, dest, 4); p.prefixlen = rtm->rtm_dst_len; - rib_add_ipv4 (ZEBRA_ROUTE_KERNEL, flags, &p, gate, src, index, table, metric, 0, SAFI_UNICAST); + if (!tb[RTA_MULTIPATH]) + rib_add_ipv4 (ZEBRA_ROUTE_KERNEL, flags, &p, gate, src, index, + vrf_id, table, 0, mtu, 0, SAFI_UNICAST); + else + { + /* This is a multipath route */ + + struct rib *rib; + struct rtnexthop *rtnh = + (struct rtnexthop *) RTA_DATA (tb[RTA_MULTIPATH]); + + len = RTA_PAYLOAD (tb[RTA_MULTIPATH]); + + rib = XCALLOC (MTYPE_RIB, sizeof (struct rib)); + rib->type = ZEBRA_ROUTE_KERNEL; + rib->distance = 0; + rib->flags = flags; + rib->metric = 0; + rib->mtu = mtu; + rib->vrf_id = vrf_id; + rib->table = table; + rib->nexthop_num = 0; + rib->uptime = time (NULL); + + for (;;) + { + if (len < (int) sizeof (*rtnh) || rtnh->rtnh_len > len) + break; + + index = rtnh->rtnh_ifindex; + gate = 0; + if (rtnh->rtnh_len > sizeof (*rtnh)) + { + memset (tb, 0, sizeof (tb)); + netlink_parse_rtattr (tb, RTA_MAX, RTNH_DATA (rtnh), + rtnh->rtnh_len - sizeof (*rtnh)); + if (tb[RTA_GATEWAY]) + gate = RTA_DATA (tb[RTA_GATEWAY]); + } + + if (gate) + { + if (index) + rib_nexthop_ipv4_ifindex_add (rib, gate, src, index); + else + rib_nexthop_ipv4_add (rib, gate, src); + } + else + rib_nexthop_ifindex_add (rib, index); + + len -= NLMSG_ALIGN(rtnh->rtnh_len); + rtnh = RTNH_NEXT(rtnh); + } + + if (rib->nexthop_num == 0) + XFREE (MTYPE_RIB, rib); + else + rib_add_ipv4_multipath (&p, rib, SAFI_UNICAST); + } } #ifdef HAVE_IPV6 if (rtm->rtm_family == AF_INET6) @@ -733,8 +884,8 @@ netlink_routing_table (struct sockaddr_nl *snl, struct nlmsghdr *h) memcpy (&p.prefix, dest, 16); p.prefixlen = rtm->rtm_dst_len; - rib_add_ipv6 (ZEBRA_ROUTE_KERNEL, flags, &p, gate, index, table, - metric, 0, SAFI_UNICAST); + rib_add_ipv6 (ZEBRA_ROUTE_KERNEL, flags, &p, gate, index, vrf_id, + table, 0, mtu, 0, SAFI_UNICAST); } #endif /* HAVE_IPV6 */ @@ -758,17 +909,19 @@ static const struct message rtproto_str[] = { /* Routing information change from the kernel. */ static int -netlink_route_change (struct sockaddr_nl *snl, struct nlmsghdr *h) +netlink_route_change (struct sockaddr_nl *snl, struct nlmsghdr *h, + vrf_id_t vrf_id) { int len; struct rtmsg *rtm; struct rtattr *tb[RTA_MAX + 1]; + u_char zebra_flags = 0; char anyaddr[16] = { 0 }; int index; int table; - int metric; + u_int32_t mtu = 0; void *dest; void *gate; @@ -779,18 +932,19 @@ netlink_route_change (struct sockaddr_nl *snl, struct nlmsghdr *h) if (!(h->nlmsg_type == RTM_NEWROUTE || h->nlmsg_type == RTM_DELROUTE)) { /* If this is not route add/delete message print warning. */ - zlog_warn ("Kernel message: %d\n", h->nlmsg_type); + zlog_warn ("Kernel message: %d vrf %u\n", h->nlmsg_type, vrf_id); return 0; } /* Connected route. */ if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug ("%s %s %s proto %s", + zlog_debug ("%s %s %s proto %s vrf %u", h->nlmsg_type == RTM_NEWROUTE ? "RTM_NEWROUTE" : "RTM_DELROUTE", rtm->rtm_family == AF_INET ? "ipv4" : "ipv6", rtm->rtm_type == RTN_UNICAST ? "unicast" : "multicast", - lookup (rtproto_str, rtm->rtm_protocol)); + lookup (rtproto_str, rtm->rtm_protocol), + vrf_id); if (rtm->rtm_type != RTN_UNICAST) { @@ -819,15 +973,16 @@ netlink_route_change (struct sockaddr_nl *snl, struct nlmsghdr *h) if (rtm->rtm_protocol == RTPROT_ZEBRA && h->nlmsg_type == RTM_NEWROUTE) return 0; + if (rtm->rtm_protocol == RTPROT_ZEBRA) + SET_FLAG(zebra_flags, ZEBRA_FLAG_SELFROUTE); if (rtm->rtm_src_len != 0) { - zlog_warn ("netlink_route_change(): no src len"); + zlog_warn ("netlink_route_change(): no src len, vrf %u", vrf_id); return 0; } index = 0; - metric = 0; dest = NULL; gate = NULL; src = NULL; @@ -846,8 +1001,20 @@ netlink_route_change (struct sockaddr_nl *snl, struct nlmsghdr *h) if (tb[RTA_PREFSRC]) src = RTA_DATA (tb[RTA_PREFSRC]); - if (h->nlmsg_type == RTM_NEWROUTE && tb[RTA_PRIORITY]) - metric = *(int *) RTA_DATA(tb[RTA_PRIORITY]); + if (h->nlmsg_type == RTM_NEWROUTE) + { + if (tb[RTA_METRICS]) + { + struct rtattr *mxrta[RTAX_MAX+1]; + + memset (mxrta, 0, sizeof mxrta); + netlink_parse_rtattr (mxrta, RTAX_MAX, RTA_DATA(tb[RTA_METRICS]), + RTA_PAYLOAD(tb[RTA_METRICS])); + + if (mxrta[RTAX_MTU]) + mtu = *(u_int32_t *) RTA_DATA(mxrta[RTAX_MTU]); + } + } if (rtm->rtm_family == AF_INET) { @@ -858,25 +1025,83 @@ netlink_route_change (struct sockaddr_nl *snl, struct nlmsghdr *h) if (IS_ZEBRA_DEBUG_KERNEL) { - if (h->nlmsg_type == RTM_NEWROUTE) - zlog_debug ("RTM_NEWROUTE %s/%d", - inet_ntoa (p.prefix), p.prefixlen); - else - zlog_debug ("RTM_DELROUTE %s/%d", - inet_ntoa (p.prefix), p.prefixlen); + char buf[PREFIX_STRLEN]; + zlog_debug ("%s %s vrf %u", + h->nlmsg_type == RTM_NEWROUTE ? "RTM_NEWROUTE" : "RTM_DELROUTE", + prefix2str (&p, buf, sizeof(buf)), vrf_id); } if (h->nlmsg_type == RTM_NEWROUTE) - rib_add_ipv4 (ZEBRA_ROUTE_KERNEL, 0, &p, gate, src, index, table, metric, 0, SAFI_UNICAST); + { + if (!tb[RTA_MULTIPATH]) + rib_add_ipv4 (ZEBRA_ROUTE_KERNEL, 0, &p, gate, src, index, vrf_id, + table, 0, mtu, 0, SAFI_UNICAST); + else + { + /* This is a multipath route */ + + struct rib *rib; + struct rtnexthop *rtnh = + (struct rtnexthop *) RTA_DATA (tb[RTA_MULTIPATH]); + + len = RTA_PAYLOAD (tb[RTA_MULTIPATH]); + + rib = XCALLOC (MTYPE_RIB, sizeof (struct rib)); + rib->type = ZEBRA_ROUTE_KERNEL; + rib->distance = 0; + rib->flags = 0; + rib->metric = 0; + rib->mtu = mtu; + rib->vrf_id = vrf_id; + rib->table = table; + rib->nexthop_num = 0; + rib->uptime = time (NULL); + + for (;;) + { + if (len < (int) sizeof (*rtnh) || rtnh->rtnh_len > len) + break; + + index = rtnh->rtnh_ifindex; + gate = 0; + if (rtnh->rtnh_len > sizeof (*rtnh)) + { + memset (tb, 0, sizeof (tb)); + netlink_parse_rtattr (tb, RTA_MAX, RTNH_DATA (rtnh), + rtnh->rtnh_len - sizeof (*rtnh)); + if (tb[RTA_GATEWAY]) + gate = RTA_DATA (tb[RTA_GATEWAY]); + } + + if (gate) + { + if (index) + rib_nexthop_ipv4_ifindex_add (rib, gate, src, index); + else + rib_nexthop_ipv4_add (rib, gate, src); + } + else + rib_nexthop_ifindex_add (rib, index); + + len -= NLMSG_ALIGN(rtnh->rtnh_len); + rtnh = RTNH_NEXT(rtnh); + } + + if (rib->nexthop_num == 0) + XFREE (MTYPE_RIB, rib); + else + rib_add_ipv4_multipath (&p, rib, SAFI_UNICAST); + } + } else - rib_delete_ipv4 (ZEBRA_ROUTE_KERNEL, 0, &p, gate, index, table, SAFI_UNICAST); + rib_delete_ipv4 (ZEBRA_ROUTE_KERNEL, zebra_flags, &p, gate, + index, vrf_id, SAFI_UNICAST); } #ifdef HAVE_IPV6 if (rtm->rtm_family == AF_INET6) { struct prefix_ipv6 p; - char buf[BUFSIZ]; p.family = AF_INET6; memcpy (&p.prefix, dest, 16); @@ -884,20 +1109,18 @@ netlink_route_change (struct sockaddr_nl *snl, struct nlmsghdr *h) if (IS_ZEBRA_DEBUG_KERNEL) { - if (h->nlmsg_type == RTM_NEWROUTE) - zlog_debug ("RTM_NEWROUTE %s/%d", - inet_ntop (AF_INET6, &p.prefix, buf, BUFSIZ), - p.prefixlen); - else - zlog_debug ("RTM_DELROUTE %s/%d", - inet_ntop (AF_INET6, &p.prefix, buf, BUFSIZ), - p.prefixlen); + char buf[PREFIX_STRLEN]; + zlog_debug ("%s %s vrf %u", + h->nlmsg_type == RTM_NEWROUTE ? "RTM_NEWROUTE" : "RTM_DELROUTE", + prefix2str (&p, buf, sizeof(buf)), vrf_id); } if (h->nlmsg_type == RTM_NEWROUTE) - rib_add_ipv6 (ZEBRA_ROUTE_KERNEL, 0, &p, gate, index, table, metric, 0, SAFI_UNICAST); + rib_add_ipv6 (ZEBRA_ROUTE_KERNEL, 0, &p, gate, index, vrf_id, table, + 0, mtu, 0, SAFI_UNICAST); else - rib_delete_ipv6 (ZEBRA_ROUTE_KERNEL, 0, &p, gate, index, table, SAFI_UNICAST); + rib_delete_ipv6 (ZEBRA_ROUTE_KERNEL, zebra_flags, &p, gate, index, vrf_id, + SAFI_UNICAST); } #endif /* HAVE_IPV6 */ @@ -905,7 +1128,8 @@ netlink_route_change (struct sockaddr_nl *snl, struct nlmsghdr *h) } static int -netlink_link_change (struct sockaddr_nl *snl, struct nlmsghdr *h) +netlink_link_change (struct sockaddr_nl *snl, struct nlmsghdr *h, + vrf_id_t vrf_id) { int len; struct ifinfomsg *ifi; @@ -918,8 +1142,8 @@ netlink_link_change (struct sockaddr_nl *snl, struct nlmsghdr *h) if (!(h->nlmsg_type == RTM_NEWLINK || h->nlmsg_type == RTM_DELLINK)) { /* If this is not link add/delete message so print warning. */ - zlog_warn ("netlink_link_change: wrong kernel message %d\n", - h->nlmsg_type); + zlog_warn ("netlink_link_change: wrong kernel message %d vrf %u\n", + h->nlmsg_type, vrf_id); return 0; } @@ -936,7 +1160,8 @@ netlink_link_change (struct sockaddr_nl *snl, struct nlmsghdr *h) if ((tb[IFLA_WIRELESS] != NULL) && (ifi->ifi_change == 0)) { if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug ("%s: ignoring IFLA_WIRELESS message", __func__); + zlog_debug ("%s: ignoring IFLA_WIRELESS message, vrf %u", __func__, + vrf_id); return 0; } #endif /* IFLA_WIRELESS */ @@ -948,17 +1173,19 @@ netlink_link_change (struct sockaddr_nl *snl, struct nlmsghdr *h) /* Add interface. */ if (h->nlmsg_type == RTM_NEWLINK) { - ifp = if_lookup_by_name (name); + ifp = if_lookup_by_name_vrf (name, vrf_id); if (ifp == NULL || !CHECK_FLAG (ifp->status, ZEBRA_INTERFACE_ACTIVE)) { if (ifp == NULL) - ifp = if_get_by_name (name); + ifp = if_get_by_name_vrf (name, vrf_id); 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); /* If new link is added. */ if_add_update (ifp); @@ -968,7 +1195,9 @@ 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); if (if_is_operative (ifp)) { @@ -990,12 +1219,12 @@ netlink_link_change (struct sockaddr_nl *snl, struct nlmsghdr *h) else { /* RTM_DELLINK. */ - ifp = if_lookup_by_name (name); + ifp = if_lookup_by_name_vrf (name, vrf_id); if (ifp == NULL) { - zlog (NULL, LOG_WARNING, "interface %s is deleted but can't find", - name); + zlog_warn ("interface %s vrf %u is deleted but can't find", + name, vrf_id); return 0; } @@ -1006,7 +1235,8 @@ netlink_link_change (struct sockaddr_nl *snl, struct nlmsghdr *h) } static int -netlink_information_fetch (struct sockaddr_nl *snl, struct nlmsghdr *h) +netlink_information_fetch (struct sockaddr_nl *snl, struct nlmsghdr *h, + vrf_id_t vrf_id) { /* JF: Ignore messages that aren't from the kernel */ if ( snl->nl_pid != 0 ) @@ -1018,25 +1248,26 @@ netlink_information_fetch (struct sockaddr_nl *snl, struct nlmsghdr *h) switch (h->nlmsg_type) { case RTM_NEWROUTE: - return netlink_route_change (snl, h); + return netlink_route_change (snl, h, vrf_id); break; case RTM_DELROUTE: - return netlink_route_change (snl, h); + return netlink_route_change (snl, h, vrf_id); break; case RTM_NEWLINK: - return netlink_link_change (snl, h); + return netlink_link_change (snl, h, vrf_id); break; case RTM_DELLINK: - return netlink_link_change (snl, h); + return netlink_link_change (snl, h, vrf_id); break; case RTM_NEWADDR: - return netlink_interface_addr (snl, h); + return netlink_interface_addr (snl, h, vrf_id); break; case RTM_DELADDR: - return netlink_interface_addr (snl, h); + return netlink_interface_addr (snl, h, vrf_id); break; default: - zlog_warn ("Unknown netlink nlmsg_type %d\n", h->nlmsg_type); + zlog_warn ("Unknown netlink nlmsg_type %d vrf %u\n", h->nlmsg_type, + vrf_id); break; } return 0; @@ -1044,32 +1275,32 @@ netlink_information_fetch (struct sockaddr_nl *snl, struct nlmsghdr *h) /* Interface lookup by netlink socket. */ int -interface_lookup_netlink (void) +interface_lookup_netlink (struct zebra_vrf *zvrf) { int ret; /* Get interface information. */ - ret = netlink_request (AF_PACKET, RTM_GETLINK, &netlink_cmd); + ret = netlink_request (AF_PACKET, RTM_GETLINK, &zvrf->netlink_cmd); if (ret < 0) return ret; - ret = netlink_parse_info (netlink_interface, &netlink_cmd); + ret = netlink_parse_info (netlink_interface, &zvrf->netlink_cmd, zvrf); if (ret < 0) return ret; /* Get IPv4 address of the interfaces. */ - ret = netlink_request (AF_INET, RTM_GETADDR, &netlink_cmd); + ret = netlink_request (AF_INET, RTM_GETADDR, &zvrf->netlink_cmd); if (ret < 0) return ret; - ret = netlink_parse_info (netlink_interface_addr, &netlink_cmd); + ret = netlink_parse_info (netlink_interface_addr, &zvrf->netlink_cmd, zvrf); if (ret < 0) return ret; #ifdef HAVE_IPV6 /* Get IPv6 address of the interfaces. */ - ret = netlink_request (AF_INET6, RTM_GETADDR, &netlink_cmd); + ret = netlink_request (AF_INET6, RTM_GETADDR, &zvrf->netlink_cmd); if (ret < 0) return ret; - ret = netlink_parse_info (netlink_interface_addr, &netlink_cmd); + ret = netlink_parse_info (netlink_interface_addr, &zvrf->netlink_cmd, zvrf); if (ret < 0) return ret; #endif /* HAVE_IPV6 */ @@ -1080,24 +1311,24 @@ interface_lookup_netlink (void) /* Routing table read function using netlink interface. Only called bootstrap time. */ int -netlink_route_read (void) +netlink_route_read (struct zebra_vrf *zvrf) { int ret; /* Get IPv4 routing table. */ - ret = netlink_request (AF_INET, RTM_GETROUTE, &netlink_cmd); + ret = netlink_request (AF_INET, RTM_GETROUTE, &zvrf->netlink_cmd); if (ret < 0) return ret; - ret = netlink_parse_info (netlink_routing_table, &netlink_cmd); + ret = netlink_parse_info (netlink_routing_table, &zvrf->netlink_cmd, zvrf); if (ret < 0) return ret; #ifdef HAVE_IPV6 /* Get IPv6 routing table. */ - ret = netlink_request (AF_INET6, RTM_GETROUTE, &netlink_cmd); + ret = netlink_request (AF_INET6, RTM_GETROUTE, &zvrf->netlink_cmd); if (ret < 0) return ret; - ret = netlink_parse_info (netlink_routing_table, &netlink_cmd); + ret = netlink_parse_info (netlink_routing_table, &zvrf->netlink_cmd, zvrf); if (ret < 0) return ret; #endif /* HAVE_IPV6 */ @@ -1107,10 +1338,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, size_t alen) { - int len; + size_t len; struct rtattr *rta; len = RTA_LENGTH (alen); @@ -1127,10 +1358,11 @@ addattr_l (struct nlmsghdr *n, int maxlen, int type, void *data, int alen) return 0; } -static int -rta_addattr_l (struct rtattr *rta, int maxlen, int type, void *data, int alen) +int +rta_addattr_l (struct rtattr *rta, size_t maxlen, int type, void *data, + size_t alen) { - int len; + size_t len; struct rtattr *subrta; len = RTA_LENGTH (alen); @@ -1149,10 +1381,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); @@ -1170,20 +1402,30 @@ addattr32 (struct nlmsghdr *n, int maxlen, int type, int data) } static int -netlink_talk_filter (struct sockaddr_nl *snl, struct nlmsghdr *h) +netlink_talk_filter (struct sockaddr_nl *snl, struct nlmsghdr *h, + vrf_id_t vrf_id) { - zlog_warn ("netlink_talk: ignoring message type 0x%04x", h->nlmsg_type); + zlog_warn ("netlink_talk: ignoring message type 0x%04x vrf %u", h->nlmsg_type, + vrf_id); return 0; } /* sendmsg() to netlink socket then recvmsg(). */ static int -netlink_talk (struct nlmsghdr *n, struct nlsock *nl) +netlink_talk (struct nlmsghdr *n, struct nlsock *nl, struct zebra_vrf *zvrf) { 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); @@ -1219,113 +1461,249 @@ netlink_talk (struct nlmsghdr *n, struct nlsock *nl) * Get reply from netlink socket. * The reply should either be an acknowlegement or an error. */ - return netlink_parse_info (netlink_talk_filter, nl); + return netlink_parse_info (netlink_talk_filter, nl, zvrf); } -/* Routing table change via netlink interface. */ -static int -netlink_route (int cmd, int family, void *dest, int length, void *gate, - int index, int zebra_flags, int table) +/* 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 nexthop *nexthop, + struct nlmsghdr *nlmsg, + struct rtmsg *rtmsg, + size_t req_size) { - int ret; - int bytelen; - struct sockaddr_nl snl; - int discard; - - struct - { - struct nlmsghdr n; - struct rtmsg r; - char buf[1024]; - } req; + 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); - memset (&req, 0, sizeof req); + 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); - bytelen = (family == AF_INET ? 4 : 16); + 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); - req.n.nlmsg_len = NLMSG_LENGTH (sizeof (struct rtmsg)); - req.n.nlmsg_flags = NLM_F_CREATE | NLM_F_REQUEST; - req.n.nlmsg_type = cmd; - req.r.rtm_family = family; - req.r.rtm_table = table; - req.r.rtm_dst_len = length; - req.r.rtm_protocol = RTPROT_ZEBRA; - req.r.rtm_scope = RT_SCOPE_UNIVERSE; + if (nexthop->src.ipv4.s_addr) + addattr_l (nlmsg, req_size, RTA_PREFSRC, + &nexthop->src.ipv4, bytelen); - if ((zebra_flags & ZEBRA_FLAG_BLACKHOLE) - || (zebra_flags & ZEBRA_FLAG_REJECT)) - discard = 1; - else - discard = 0; + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug("netlink_route_multipath() (%s): " + "nexthop via if %u", routedesc, nexthop->ifindex); + } - if (cmd == RTM_NEWROUTE) + if (nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX + || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME) { - if (discard) - { - if (zebra_flags & ZEBRA_FLAG_BLACKHOLE) - req.r.rtm_type = RTN_BLACKHOLE; - else if (zebra_flags & ZEBRA_FLAG_REJECT) - req.r.rtm_type = RTN_UNREACHABLE; - else - assert (RTN_BLACKHOLE != RTN_UNREACHABLE); /* false */ - } - else - req.r.rtm_type = RTN_UNICAST; + 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); } +} - if (dest) - addattr_l (&req.n, sizeof req, RTA_DST, dest, bytelen); +/* 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 (!discard) + 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) { - if (gate) - addattr_l (&req.n, sizeof req, RTA_GATEWAY, gate, bytelen); - if (index > 0) - addattr32 (&req.n, sizeof req, RTA_OIF, index); + 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; - /* Destination netlink address. */ - memset (&snl, 0, sizeof snl); - snl.nl_family = AF_NETLINK; + 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; - /* Talk to netlink socket. */ - ret = netlink_talk (&req.n, &netlink_cmd); - if (ret < 0) - return -1; + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug("netlink_route_multipath() (%s): " + "nexthop via if %u", routedesc, nexthop->ifindex); + } + else + { + rtnh->rtnh_ifindex = 0; + } +} - return 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, + struct zebra_vrf *zvrf) +{ + if (IS_ZEBRA_DEBUG_KERNEL) + { + char buf[PREFIX_STRLEN]; + zlog_debug ("netlink_route_multipath() (%s): %s %s vrf %u type %s", + routedesc, + lookup (nlmsg_str, cmd), + prefix2str (p, buf, sizeof(buf)), + zvrf->vrf_id, + 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, - int family) +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; + int family = PREFIX_FAMILY(p); + const char *routedesc; struct { struct nlmsghdr n; struct rtmsg r; - char buf[1024]; + char buf[NL_PKT_BUF_SIZE]; } req; - memset (&req, 0, sizeof req); + struct zebra_vrf *zvrf = vrf_info_lookup (rib->vrf_id); + + memset (&req, 0, sizeof req - NL_PKT_BUF_SIZE); bytelen = (family == AF_INET ? 4 : 16); req.n.nlmsg_len = NLMSG_LENGTH (sizeof (struct rtmsg)); - req.n.nlmsg_flags = NLM_F_CREATE | NLM_F_REQUEST; + req.n.nlmsg_flags = NLM_F_CREATE | NLM_F_REPLACE | NLM_F_REQUEST; req.n.nlmsg_type = cmd; req.r.rtm_family = family; req.r.rtm_table = rib->table; req.r.rtm_dst_len = p->prefixlen; req.r.rtm_protocol = RTPROT_ZEBRA; - req.r.rtm_scope = RT_SCOPE_UNIVERSE; + req.r.rtm_scope = RT_SCOPE_LINK; if ((rib->flags & ZEBRA_FLAG_BLACKHOLE) || (rib->flags & ZEBRA_FLAG_REJECT)) discard = 1; @@ -1350,164 +1728,76 @@ netlink_route_multipath (int cmd, struct prefix *p, struct rib *rib, addattr_l (&req.n, sizeof req, RTA_DST, &p->u.prefix, bytelen); /* Metric. */ - addattr32 (&req.n, sizeof req, RTA_PRIORITY, rib->metric); + addattr32 (&req.n, sizeof req, RTA_PRIORITY, NL_DEFAULT_ROUTE_METRIC); + + if (rib->mtu || rib->nexthop_mtu) + { + char buf[NL_PKT_BUF_SIZE]; + struct rtattr *rta = (void *) buf; + u_int32_t mtu = rib->mtu; + if (!mtu || (rib->nexthop_mtu && rib->nexthop_mtu < mtu)) + mtu = rib->nexthop_mtu; + rta->rta_type = RTA_METRICS; + rta->rta_len = RTA_LENGTH(0); + rta_addattr_l (rta, NL_PKT_BUF_SIZE, RTAX_MTU, &mtu, sizeof mtu); + addattr_l (&req.n, NL_PKT_BUF_SIZE, RTA_METRICS, RTA_DATA (rta), + RTA_PAYLOAD (rta)); + } 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)) + { + 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; + + if (nexthop->type != NEXTHOP_TYPE_IFINDEX && + nexthop->type != NEXTHOP_TYPE_IFNAME) + req.r.rtm_scope = RT_SCOPE_UNIVERSE; + + nexthop_num++; + } + + /* Singlepath case. */ + if (nexthop_num == 1 || MULTIPATH_NUM == 1) { - for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) + 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, zvrf); + _netlink_route_build_singlepath(routedesc, bytelen, + nexthop, &req.n, &req.r, + sizeof req); if (cmd == RTM_NEWROUTE) SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB); @@ -1519,7 +1809,7 @@ netlink_route_multipath (int cmd, struct prefix *p, struct rib *rib, } else { - char buf[1024]; + char buf[NL_PKT_BUF_SIZE]; struct rtattr *rta = (void *) buf; struct rtnexthop *rtnh; union g_addr *src = NULL; @@ -1529,168 +1819,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 (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, 4096, 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, 4096, 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, 4096, 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, 4096, 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, zvrf); + _netlink_route_build_multipath(routedesc, bytelen, + nexthop, rta, rtnh, &src); rtnh = RTNH_NEXT (rtnh); if (cmd == RTM_NEWROUTE) @@ -1701,7 +1849,7 @@ netlink_route_multipath (int cmd, struct prefix *p, struct rib *rib, addattr_l (&req.n, sizeof req, RTA_PREFSRC, &src->ipv4, bytelen); if (rta->rta_len > RTA_LENGTH (0)) - addattr_l (&req.n, 1024, RTA_MULTIPATH, RTA_DATA (rta), + addattr_l (&req.n, NL_PKT_BUF_SIZE, RTA_MULTIPATH, RTA_DATA (rta), RTA_PAYLOAD (rta)); } @@ -1720,44 +1868,23 @@ netlink_route_multipath (int cmd, struct prefix *p, struct rib *rib, snl.nl_family = AF_NETLINK; /* Talk to netlink socket. */ - return netlink_talk (&req.n, &netlink_cmd); -} - -int -kernel_add_ipv4 (struct prefix *p, struct rib *rib) -{ - return netlink_route_multipath (RTM_NEWROUTE, p, rib, AF_INET); + return netlink_talk (&req.n, &zvrf->netlink_cmd, zvrf); } int -kernel_delete_ipv4 (struct prefix *p, struct rib *rib) -{ - return netlink_route_multipath (RTM_DELROUTE, p, rib, AF_INET); -} - -#ifdef HAVE_IPV6 -int -kernel_add_ipv6 (struct prefix *p, struct rib *rib) +kernel_route_rib (struct prefix *p, struct rib *old, struct rib *new) { - return netlink_route_multipath (RTM_NEWROUTE, p, rib, AF_INET6); + if (!old && new) + return netlink_route_multipath (RTM_NEWROUTE, p, new); + if (old && !new) + return netlink_route_multipath (RTM_DELROUTE, p, old); + + /* Replace, can be done atomically if metric does not change; + * netlink uses [prefix, tos, priority] to identify prefix. + * Now metric is not sent to kernel, so we can just do atomic replace. */ + return netlink_route_multipath (RTM_NEWROUTE, p, new); } -int -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, @@ -1770,11 +1897,13 @@ netlink_address (int cmd, int family, struct interface *ifp, { struct nlmsghdr n; struct ifaddrmsg ifa; - char buf[1024]; + char buf[NL_PKT_BUF_SIZE]; } req; + struct zebra_vrf *zvrf = vrf_info_lookup (ifp->vrf_id); + p = ifc->address; - memset (&req, 0, sizeof req); + memset (&req, 0, sizeof req - NL_PKT_BUF_SIZE); bytelen = (family == AF_INET ? 4 : 16); @@ -1805,7 +1934,7 @@ netlink_address (int cmd, int family, struct interface *ifp, addattr_l (&req.n, sizeof req, IFA_LABEL, ifc->label, strlen (ifc->label) + 1); - return netlink_talk (&req.n, &netlink_cmd); + return netlink_talk (&req.n, &zvrf->netlink_cmd, zvrf); } int @@ -1827,8 +1956,10 @@ extern struct thread_master *master; static int kernel_read (struct thread *thread) { - netlink_parse_info (netlink_information_fetch, &netlink); - thread_add_read (zebrad.master, kernel_read, NULL, netlink.sock); + struct zebra_vrf *zvrf = (struct zebra_vrf *)THREAD_ARG (thread); + netlink_parse_info (netlink_information_fetch, &zvrf->netlink, zvrf); + zvrf->t_netlink = thread_add_read (zebrad.master, kernel_read, zvrf, + zvrf->netlink.sock); return 0; } @@ -1856,7 +1987,7 @@ static void netlink_install_filter (int sock, __u32 pid) }; struct sock_fprog prog = { - .len = sizeof(filter) / sizeof(filter[0]), + .len = array_size(filter), .filter = filter, }; @@ -1867,7 +1998,7 @@ static void netlink_install_filter (int sock, __u32 pid) /* Exported interface function. This function simply calls netlink_socket (). */ void -kernel_init (void) +kernel_init (struct zebra_vrf *zvrf) { unsigned long groups; @@ -1875,22 +2006,64 @@ kernel_init (void) #ifdef HAVE_IPV6 groups |= RTMGRP_IPV6_ROUTE | RTMGRP_IPV6_IFADDR; #endif /* HAVE_IPV6 */ - netlink_socket (&netlink, groups); - netlink_socket (&netlink_cmd, 0); + netlink_socket (&zvrf->netlink, groups, zvrf->vrf_id); + netlink_socket (&zvrf->netlink_cmd, 0, zvrf->vrf_id); /* Register kernel socket. */ - if (netlink.sock > 0) + if (zvrf->netlink.sock > 0) { + size_t bufsize = MAX(nl_rcvbufsize, 2 * sysconf(_SC_PAGESIZE)); + /* Only want non-blocking on the netlink event socket */ - if (fcntl (netlink.sock, F_SETFL, O_NONBLOCK) < 0) - zlog (NULL, LOG_ERR, "Can't set %s socket flags: %s", netlink.name, - safe_strerror (errno)); + if (fcntl (zvrf->netlink.sock, F_SETFL, O_NONBLOCK) < 0) + zlog_err ("Can't set %s socket flags: %s", zvrf->netlink.name, + safe_strerror (errno)); /* Set receive buffer size if it's set from command line */ if (nl_rcvbufsize) - netlink_recvbuf (&netlink, nl_rcvbufsize); + netlink_recvbuf (&zvrf->netlink, nl_rcvbufsize); + + nl_rcvbuf.p = XMALLOC (MTYPE_NETLINK_RCVBUF, bufsize); + nl_rcvbuf.size = bufsize; + + netlink_install_filter (zvrf->netlink.sock, zvrf->netlink_cmd.snl.nl_pid); + zvrf->t_netlink = thread_add_read (zebrad.master, kernel_read, zvrf, + zvrf->netlink.sock); + } +} + +void +kernel_terminate (struct zebra_vrf *zvrf) +{ + THREAD_READ_OFF (zvrf->t_netlink); + + if (zvrf->netlink.sock >= 0) + { + close (zvrf->netlink.sock); + zvrf->netlink.sock = -1; + } - netlink_install_filter (netlink.sock, netlink_cmd.snl.nl_pid); - thread_add_read (zebrad.master, kernel_read, NULL, netlink.sock); + if (zvrf->netlink_cmd.sock >= 0) + { + close (zvrf->netlink_cmd.sock); + zvrf->netlink_cmd.sock = -1; } } + +/* + * 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/rt_netlink.h b/zebra/rt_netlink.h new file mode 100644 index 000000000..9fc70010f --- /dev/null +++ b/zebra/rt_netlink.h @@ -0,0 +1,50 @@ +/* Header file exported by rt_netlink.c to zebra. + * Copyright (C) 1997, 98, 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. + */ + +#ifndef _ZEBRA_RT_NETLINK_H +#define _ZEBRA_RT_NETLINK_H + +#ifdef HAVE_NETLINK + +#define NL_PKT_BUF_SIZE 8192 +#define NL_DEFAULT_ROUTE_METRIC 20 + +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, size_t alen); + +extern int +rta_addattr_l (struct rtattr *rta, size_t maxlen, int type, void *data, size_t alen); + +extern const char * +nl_msg_type_to_str (uint16_t msg_type); + +extern const char * +nl_rtproto_to_str (u_char rtproto); + + +extern int interface_lookup_netlink (struct zebra_vrf *zvrf); +extern int netlink_route_read (struct zebra_vrf *zvrf); + +#endif /* HAVE_NETLINK */ + +#endif /* _ZEBRA_RT_NETLINK_H */ diff --git a/zebra/rt_socket.c b/zebra/rt_socket.c index 1b8ded7ee..9dd45823f 100644 --- a/zebra/rt_socket.c +++ b/zebra/rt_socket.c @@ -41,6 +41,7 @@ extern int rtm_write (int message, union sockunion *dest, union sockunion *mask, union sockunion *gate, unsigned int index, int zebra_flags, int metric); +#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN /* Adjust netmask socket length. Return value is a adjusted sin_len value. */ static int @@ -63,23 +64,25 @@ sin_masklen (struct in_addr mask) len--; return len; } +#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */ /* Interface between zebra message and rtm message. */ static int -kernel_rtm_ipv4 (int cmd, struct prefix *p, struct rib *rib, int family) +kernel_rtm_ipv4 (int cmd, struct prefix *p, struct rib *rib) { 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; + ifindex_t ifindex = 0; int gate = 0; int error; - char prefix_buf[INET_ADDRSTRLEN]; + char prefix_buf[PREFIX_STRLEN]; if (IS_ZEBRA_DEBUG_RIB) - inet_ntop (AF_INET, &p->u.prefix, prefix_buf, INET_ADDRSTRLEN); + prefix2str (p, prefix_buf, sizeof(prefix_buf)); memset (&sin_dest, 0, sizeof (struct sockaddr_in)); sin_dest.sin_family = AF_INET; #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN @@ -96,8 +99,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 +118,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) @@ -170,9 +160,9 @@ kernel_rtm_ipv4 (int cmd, struct prefix *p, struct rib *rib, int family) { if (!gate) { - 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); + zlog_debug ("%s: %s: attention! gate not found for rib %p", + __func__, prefix_buf, rib); + rib_dump (p, rib); } else inet_ntop (AF_INET, &sin_gate.sin_addr, gate_buf, INET_ADDRSTRLEN); @@ -184,8 +174,8 @@ kernel_rtm_ipv4 (int cmd, struct prefix *p, struct rib *rib, int family) case ZEBRA_ERR_NOERROR: nexthop_num++; if (IS_ZEBRA_DEBUG_RIB) - zlog_debug ("%s: %s/%d: successfully did NH %s", - __func__, prefix_buf, p->prefixlen, gate_buf); + zlog_debug ("%s: %s: successfully did NH %s", + __func__, prefix_buf, gate_buf); if (cmd == RTM_ADD) SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB); break; @@ -207,11 +197,9 @@ kernel_rtm_ipv4 (int cmd, struct prefix *p, struct rib *rib, int family) case ZEBRA_ERR_RTNOEXIST: case ZEBRA_ERR_RTUNREACH: default: - /* This point is reachable regardless of debugging mode. */ - if (!IS_ZEBRA_DEBUG_RIB) - inet_ntop (AF_INET, &p->u.prefix, prefix_buf, INET_ADDRSTRLEN); - zlog_err ("%s: %s/%d: rtm_write() unexpectedly returned %d for command %s", - __func__, prefix_buf, p->prefixlen, error, lookup (rtm_type_str, cmd)); + zlog_err ("%s: %s: rtm_write() unexpectedly returned %d for command %s", + __func__, prefix2str(p, prefix_buf, sizeof(prefix_buf)), + error, lookup (rtm_type_str, cmd)); break; } } /* if (cmd and flags make sense) */ @@ -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) @@ -228,36 +216,9 @@ kernel_rtm_ipv4 (int cmd, struct prefix *p, struct rib *rib, int family) return 0; /*XXX*/ } -int -kernel_add_ipv4 (struct prefix *p, struct rib *rib) -{ - int route; - - if (zserv_privs.change(ZPRIVS_RAISE)) - zlog (NULL, LOG_ERR, "Can't raise privileges"); - route = kernel_rtm_ipv4 (RTM_ADD, p, rib, AF_INET); - if (zserv_privs.change(ZPRIVS_LOWER)) - zlog (NULL, LOG_ERR, "Can't lower privileges"); - - return route; -} - -int -kernel_delete_ipv4 (struct prefix *p, struct rib *rib) -{ - int route; - - if (zserv_privs.change(ZPRIVS_RAISE)) - zlog (NULL, LOG_ERR, "Can't raise privileges"); - route = kernel_rtm_ipv4 (RTM_DELETE, p, rib, AF_INET); - if (zserv_privs.change(ZPRIVS_LOWER)) - zlog (NULL, LOG_ERR, "Can't lower privileges"); - - return route; -} - #ifdef HAVE_IPV6 +#ifdef SIN6_LEN /* Calculate sin6_len value for netmask socket value. */ static int sin6_masklen (struct in6_addr mask) @@ -266,13 +227,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); @@ -285,78 +241,18 @@ sin6_masklen (struct in6_addr mask) return len; } - -/* Interface between zebra message and rtm message. */ -static int -kernel_rtm_ipv6 (int message, struct prefix_ipv6 *dest, - struct in6_addr *gate, int index, int flags) -{ - struct sockaddr_in6 *mask; - struct sockaddr_in6 sin_dest, sin_mask, sin_gate; - - memset (&sin_dest, 0, sizeof (struct sockaddr_in6)); - sin_dest.sin6_family = AF_INET6; -#ifdef SIN6_LEN - sin_dest.sin6_len = sizeof (struct sockaddr_in6); #endif /* SIN6_LEN */ - memset (&sin_mask, 0, sizeof (struct sockaddr_in6)); - - memset (&sin_gate, 0, sizeof (struct sockaddr_in6)); - sin_gate.sin6_family = AF_INET6; -#ifdef SIN6_LEN - sin_gate.sin6_len = sizeof (struct sockaddr_in6); -#endif /* SIN6_LEN */ - - sin_dest.sin6_addr = dest->prefix; - - if (gate) - memcpy (&sin_gate.sin6_addr, gate, sizeof (struct in6_addr)); - - /* Under kame set interface index to link local address. */ -#ifdef KAME - -#define SET_IN6_LINKLOCAL_IFINDEX(a, i) \ - do { \ - (a).s6_addr[2] = ((i) >> 8) & 0xff; \ - (a).s6_addr[3] = (i) & 0xff; \ - } while (0) - - if (gate && IN6_IS_ADDR_LINKLOCAL(gate)) - SET_IN6_LINKLOCAL_IFINDEX (sin_gate.sin6_addr, index); -#endif /* KAME */ - - if (gate && dest->prefixlen == 128) - mask = NULL; - else - { - masklen2ip6 (dest->prefixlen, &sin_mask.sin6_addr); - sin_mask.sin6_family = AF_INET6; -#ifdef SIN6_LEN - sin_mask.sin6_len = sin6_masklen (sin_mask.sin6_addr); -#endif /* SIN6_LEN */ - mask = &sin_mask; - } - - return rtm_write (message, - (union sockunion *) &sin_dest, - (union sockunion *) mask, - gate ? (union sockunion *)&sin_gate : NULL, - index, - flags, - 0); -} - /* Interface between zebra message and rtm message. */ static int -kernel_rtm_ipv6_multipath (int cmd, struct prefix *p, struct rib *rib, - int family) +kernel_rtm_ipv6 (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; + ifindex_t ifindex = 0; int gate = 0; int error; @@ -376,8 +272,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 +287,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); @@ -459,9 +340,11 @@ kernel_rtm_ipv6_multipath (int cmd, struct prefix *p, struct rib *rib, #if 0 if (error) { - zlog_info ("kernel_rtm_ipv6_multipath(): nexthop %d add error=%d.", + zlog_info ("kernel_rtm_ipv6(): nexthop %d add error=%d.", nexthop_num, error); } +#else + (void)error; #endif nexthop_num++; @@ -471,54 +354,44 @@ kernel_rtm_ipv6_multipath (int cmd, struct prefix *p, struct rib *rib, if (nexthop_num == 0) { if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug ("kernel_rtm_ipv6_multipath(): No useful nexthop."); + zlog_debug ("kernel_rtm_ipv6(): No useful nexthop."); return 0; } return 0; /*XXX*/ } -int -kernel_add_ipv6 (struct prefix *p, struct rib *rib) -{ - int route; - - if (zserv_privs.change(ZPRIVS_RAISE)) - zlog (NULL, LOG_ERR, "Can't raise privileges"); - route = kernel_rtm_ipv6_multipath (RTM_ADD, p, rib, AF_INET6); - if (zserv_privs.change(ZPRIVS_LOWER)) - zlog (NULL, LOG_ERR, "Can't lower privileges"); +#endif - return route; +static int +kernel_rtm (int cmd, struct prefix *p, struct rib *rib) +{ + switch (PREFIX_FAMILY(p)) + { + case AF_INET: + return kernel_rtm_ipv4 (cmd, p, rib); + case AF_INET6: + return kernel_rtm_ipv6 (cmd, p, rib); + } + return 0; } int -kernel_delete_ipv6 (struct prefix *p, struct rib *rib) +kernel_route_rib (struct prefix *p, struct rib *old, struct rib *new) { - int route; + int route = 0; if (zserv_privs.change(ZPRIVS_RAISE)) zlog (NULL, LOG_ERR, "Can't raise privileges"); - route = kernel_rtm_ipv6_multipath (RTM_DELETE, p, rib, AF_INET6); - if (zserv_privs.change(ZPRIVS_LOWER)) - zlog (NULL, LOG_ERR, "Can't lower privileges"); - return route; -} + if (old) + route |= kernel_rtm (RTM_DELETE, p, old); -/* 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 (new) + route |= kernel_rtm (RTM_ADD, p, new); - 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 ae5c5a1cc..2f62714d8 100644 --- a/zebra/rtadv.c +++ b/zebra/rtadv.c @@ -31,6 +31,7 @@ #include "linklist.h" #include "command.h" #include "privs.h" +#include "vrf.h" #include "zebra/interface.h" #include "zebra/rtadv.h" @@ -40,7 +41,7 @@ extern struct zebra_privs_t zserv_privs; -#if defined (HAVE_IPV6) && defined (RTADV) +#if defined (HAVE_IPV6) && defined (HAVE_RTADV) #ifdef OPEN_BSD #include @@ -62,34 +63,14 @@ extern struct zebra_t zebrad; enum rtadv_event {RTADV_START, RTADV_STOP, RTADV_TIMER, RTADV_TIMER_MSEC, RTADV_READ}; -static void rtadv_event (enum rtadv_event, int); +static void rtadv_event (struct zebra_vrf *, 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 -{ - int sock; - - int adv_if_count; - int adv_msec_if_count; - - struct thread *ra_read; - struct thread *ra_timer; -}; - -struct rtadv *rtadv = NULL; - -static struct rtadv * -rtadv_new (void) -{ - return XCALLOC (MTYPE_TMP, sizeof (struct rtadv)); -} static int rtadv_recv_packet (int sock, u_char *buf, int buflen, - struct sockaddr_in6 *from, unsigned int *ifindex, + struct sockaddr_in6 *from, ifindex_t *ifindex, int *hoplimit) { int ret; @@ -313,24 +294,6 @@ rtadv_send_packet (int sock, struct interface *ifp) } /* Hardware address. */ -#ifdef HAVE_STRUCT_SOCKADDR_DL - sdl = &ifp->sdl; - if (sdl != NULL && sdl->sdl_alen != 0) - { - buf[len++] = ND_OPT_SOURCE_LINKADDR; - - /* Option length should be rounded up to next octet if - the link address does not end on an octet boundary. */ - buf[len++] = (sdl->sdl_alen + 9) >> 3; - - memcpy (buf + len, LLADDR (sdl), sdl->sdl_alen); - len += sdl->sdl_alen; - - /* Pad option to end on an octet boundary. */ - memset (buf + len, 0, -(sdl->sdl_alen + 2) & 0x7); - len += -(sdl->sdl_alen + 2) & 0x7; - } -#else if (ifp->hw_addr_len != 0) { buf[len++] = ND_OPT_SOURCE_LINKADDR; @@ -346,7 +309,6 @@ rtadv_send_packet (int sock, struct interface *ifp) memset (buf + len, 0, -(ifp->hw_addr_len + 2) & 0x7); len += -(ifp->hw_addr_len + 2) & 0x7; } -#endif /* HAVE_STRUCT_SOCKADDR_DL */ /* MTU */ if (zif->rtadv.AdvLinkMTU) @@ -389,24 +351,25 @@ rtadv_send_packet (int sock, struct interface *ifp) static int rtadv_timer (struct thread *thread) { + struct zebra_vrf *zvrf = THREAD_ARG (thread); struct listnode *node, *nnode; struct interface *ifp; struct zebra_if *zif; int period; - rtadv->ra_timer = NULL; - if (rtadv->adv_msec_if_count == 0) + zvrf->rtadv.ra_timer = NULL; + if (zvrf->rtadv.adv_msec_if_count == 0) { period = 1000; /* 1 s */ - rtadv_event (RTADV_TIMER, 1 /* 1 s */); + rtadv_event (zvrf, RTADV_TIMER, 1 /* 1 s */); } else { period = 10; /* 10 ms */ - rtadv_event (RTADV_TIMER_MSEC, 10 /* 10 ms */); + rtadv_event (zvrf, RTADV_TIMER_MSEC, 10 /* 10 ms */); } - for (ALL_LIST_ELEMENTS (iflist, node, nnode, ifp)) + for (ALL_LIST_ELEMENTS (vrf_iflist (zvrf->vrf_id), node, nnode, ifp)) { if (if_is_loopback (ifp) || ! if_is_operative (ifp)) continue; @@ -421,7 +384,7 @@ rtadv_timer (struct thread *thread) /* FIXME: using MaxRtrAdvInterval each time isn't what section 6.2.4 of RFC4861 tells to do. */ zif->rtadv.AdvIntervalTimer = zif->rtadv.MaxRtrAdvInterval; - rtadv_send_packet (rtadv->sock, ifp); + rtadv_send_packet (zvrf->rtadv.sock, ifp); } } } @@ -431,9 +394,11 @@ rtadv_timer (struct thread *thread) static void rtadv_process_solicit (struct interface *ifp) { - zlog_info ("Router solicitation received on %s", ifp->name); + struct zebra_vrf *zvrf = vrf_info_lookup (ifp->vrf_id); - rtadv_send_packet (rtadv->sock, ifp); + zlog_info ("Router solicitation received on %s vrf %u", ifp->name, zvrf->vrf_id); + + rtadv_send_packet (zvrf->rtadv.sock, ifp); } static void @@ -443,17 +408,18 @@ rtadv_process_advert (void) } static void -rtadv_process_packet (u_char *buf, unsigned int len, unsigned int ifindex, int hoplimit) +rtadv_process_packet (u_char *buf, unsigned int len, ifindex_t ifindex, + int hoplimit, vrf_id_t vrf_id) { struct icmp6_hdr *icmph; struct interface *ifp; struct zebra_if *zif; /* Interface search. */ - ifp = if_lookup_by_index (ifindex); + ifp = if_lookup_by_index_vrf (ifindex, vrf_id); if (ifp == NULL) { - zlog_warn ("Unknown interface index: %d", ifindex); + zlog_warn ("Unknown interface index: %d, vrf %u", ifindex, vrf_id); return; } @@ -506,16 +472,17 @@ rtadv_read (struct thread *thread) int len; u_char buf[RTADV_MSG_SIZE]; struct sockaddr_in6 from; - unsigned int ifindex = 0; + ifindex_t ifindex = 0; int hoplimit = -1; + struct zebra_vrf *zvrf = THREAD_ARG (thread); sock = THREAD_FD (thread); - rtadv->ra_read = NULL; + zvrf->rtadv.ra_read = NULL; /* Register myself. */ - rtadv_event (RTADV_READ, sock); + rtadv_event (zvrf, RTADV_READ, sock); - len = rtadv_recv_packet (sock, buf, BUFSIZ, &from, &ifindex, &hoplimit); + len = rtadv_recv_packet (sock, buf, sizeof (buf), &from, &ifindex, &hoplimit); if (len < 0) { @@ -523,13 +490,13 @@ rtadv_read (struct thread *thread) return len; } - rtadv_process_packet (buf, (unsigned)len, ifindex, hoplimit); + rtadv_process_packet (buf, (unsigned)len, ifindex, hoplimit, zvrf->vrf_id); return 0; } static int -rtadv_make_socket (void) +rtadv_make_socket (vrf_id_t vrf_id) { int sock; int ret; @@ -539,7 +506,7 @@ rtadv_make_socket (void) zlog_err ("rtadv_make_socket: could not raise privs, %s", safe_strerror (errno) ); - sock = socket (AF_INET6, SOCK_RAW, IPPROTO_ICMPV6); + sock = vrf_socket (AF_INET6, SOCK_RAW, IPPROTO_ICMPV6, vrf_id); if ( zserv_privs.change (ZPRIVS_LOWER) ) zlog_err ("rtadv_make_socket: could not lower privs, %s", @@ -548,23 +515,41 @@ rtadv_make_socket (void) /* When we can't make ICMPV6 socket simply back. Router advertisement feature will not be supported. */ if (sock < 0) - return -1; + { + close (sock); + return -1; + } ret = setsockopt_ipv6_pktinfo (sock, 1); if (ret < 0) - return ret; + { + close (sock); + return ret; + } ret = setsockopt_ipv6_multicast_loop (sock, 0); if (ret < 0) - return ret; + { + close (sock); + return ret; + } ret = setsockopt_ipv6_unicast_hops (sock, 255); if (ret < 0) - return ret; + { + close (sock); + return ret; + } ret = setsockopt_ipv6_multicast_hops (sock, 255); if (ret < 0) - return ret; + { + close (sock); + return ret; + } ret = setsockopt_ipv6_hoplimit (sock, 1); if (ret < 0) - return ret; + { + close (sock); + return ret; + } ICMP6_FILTER_SETBLOCKALL(&filter); ICMP6_FILTER_SETPASS (ND_ROUTER_SOLICIT, &filter); @@ -580,7 +565,7 @@ rtadv_make_socket (void) return sock; } - + static struct rtadv_prefix * rtadv_prefix_new (void) { @@ -661,9 +646,11 @@ DEFUN (ipv6_nd_suppress_ra, { struct interface *ifp; struct zebra_if *zif; + struct zebra_vrf *zvrf; ifp = vty->index; zif = ifp->info; + zvrf = vrf_info_lookup (ifp->vrf_id); if (if_is_loopback (ifp)) { @@ -675,12 +662,12 @@ DEFUN (ipv6_nd_suppress_ra, { zif->rtadv.AdvSendAdvertisements = 0; zif->rtadv.AdvIntervalTimer = 0; - rtadv->adv_if_count--; + zvrf->rtadv.adv_if_count--; - if_leave_all_router (rtadv->sock, ifp); + if_leave_all_router (zvrf->rtadv.sock, ifp); - if (rtadv->adv_if_count == 0) - rtadv_event (RTADV_STOP, 0); + if (zvrf->rtadv.adv_if_count == 0) + rtadv_event (zvrf, RTADV_STOP, 0); } return CMD_SUCCESS; @@ -696,9 +683,11 @@ DEFUN (no_ipv6_nd_suppress_ra, { struct interface *ifp; struct zebra_if *zif; + struct zebra_vrf *zvrf; ifp = vty->index; zif = ifp->info; + zvrf = vrf_info_lookup (ifp->vrf_id); if (if_is_loopback (ifp)) { @@ -710,12 +699,12 @@ DEFUN (no_ipv6_nd_suppress_ra, { zif->rtadv.AdvSendAdvertisements = 1; zif->rtadv.AdvIntervalTimer = 0; - rtadv->adv_if_count++; + zvrf->rtadv.adv_if_count++; - if_join_all_router (rtadv->sock, ifp); + if_join_all_router (zvrf->rtadv.sock, ifp); - if (rtadv->adv_if_count == 1) - rtadv_event (RTADV_START, rtadv->sock); + if (zvrf->rtadv.adv_if_count == 1) + rtadv_event (zvrf, RTADV_START, zvrf->rtadv.sock); } return CMD_SUCCESS; @@ -732,6 +721,7 @@ DEFUN (ipv6_nd_ra_interval_msec, unsigned interval; struct interface *ifp = (struct interface *) vty->index; struct zebra_if *zif = ifp->info; + struct zebra_vrf *zvrf = vrf_info_lookup (ifp->vrf_id); VTY_GET_INTEGER_RANGE ("router advertisement interval", interval, argv[0], 70, 1800000); if ((zif->rtadv.AdvDefaultLifetime != -1 && interval > (unsigned)zif->rtadv.AdvDefaultLifetime * 1000)) @@ -741,10 +731,10 @@ DEFUN (ipv6_nd_ra_interval_msec, } if (zif->rtadv.MaxRtrAdvInterval % 1000) - rtadv->adv_msec_if_count--; + zvrf->rtadv.adv_msec_if_count--; if (interval % 1000) - rtadv->adv_msec_if_count++; + zvrf->rtadv.adv_msec_if_count++; zif->rtadv.MaxRtrAdvInterval = interval; zif->rtadv.MinRtrAdvInterval = 0.33 * interval; @@ -764,6 +754,7 @@ DEFUN (ipv6_nd_ra_interval, unsigned interval; struct interface *ifp = (struct interface *) vty->index; struct zebra_if *zif = ifp->info; + struct zebra_vrf *zvrf = vrf_info_lookup (ifp->vrf_id); VTY_GET_INTEGER_RANGE ("router advertisement interval", interval, argv[0], 1, 1800); if ((zif->rtadv.AdvDefaultLifetime != -1 && interval > (unsigned)zif->rtadv.AdvDefaultLifetime)) @@ -773,7 +764,7 @@ DEFUN (ipv6_nd_ra_interval, } if (zif->rtadv.MaxRtrAdvInterval % 1000) - rtadv->adv_msec_if_count--; + zvrf->rtadv.adv_msec_if_count--; /* convert to milliseconds */ interval = interval * 1000; @@ -795,12 +786,14 @@ DEFUN (no_ipv6_nd_ra_interval, { struct interface *ifp; struct zebra_if *zif; + struct zebra_vrf *zvrf; ifp = (struct interface *) vty->index; zif = ifp->info; + zvrf = vrf_info_lookup (ifp->vrf_id); if (zif->rtadv.MaxRtrAdvInterval % 1000) - rtadv->adv_msec_if_count--; + zvrf->rtadv.adv_msec_if_count--; zif->rtadv.MaxRtrAdvInterval = RTADV_MAX_RTR_ADV_INTERVAL; zif->rtadv.MinRtrAdvInterval = RTADV_MIN_RTR_ADV_INTERVAL; @@ -1201,7 +1194,8 @@ DEFUN (ipv6_nd_prefix, if (argc > 1) { - if ((isdigit(argv[1][0])) || strncmp (argv[1], "i", 1) == 0) + if ((isdigit((unsigned char)argv[1][0])) + || strncmp (argv[1], "i", 1) == 0) { if ( strncmp (argv[1], "i", 1) == 0) rp.AdvValidLifetime = UINT32_MAX; @@ -1483,7 +1477,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" @@ -1536,20 +1530,15 @@ rtadv_config_write (struct vty *vty, struct interface *ifp) struct zebra_if *zif; struct listnode *node; struct rtadv_prefix *rprefix; - u_char buf[INET6_ADDRSTRLEN]; + char buf[PREFIX_STRLEN]; int interval; - if (! rtadv) - return; - zif = ifp->info; if (! if_is_loopback (ifp)) { if (zif->rtadv.AdvSendAdvertisements) vty_out (vty, " no ipv6 nd suppress-ra%s", VTY_NEWLINE); - else - vty_out (vty, " ipv6 nd suppress-ra%s", VTY_NEWLINE); } @@ -1600,10 +1589,8 @@ rtadv_config_write (struct vty *vty, struct interface *ifp) for (ALL_LIST_ELEMENTS_RO (zif->rtadv.AdvPrefixList, node, rprefix)) { - vty_out (vty, " ipv6 nd prefix %s/%d", - inet_ntop (AF_INET6, &rprefix->prefix.prefix, - (char *) buf, INET6_ADDRSTRLEN), - rprefix->prefix.prefixlen); + vty_out (vty, " ipv6 nd prefix %s", + prefix2str (&rprefix->prefix, buf, sizeof(buf))); if ((rprefix->AdvValidLifetime != RTADV_VALID_LIFETIME) || (rprefix->AdvPreferredLifetime != RTADV_PREFERRED_LIFETIME)) { @@ -1628,16 +1615,18 @@ rtadv_config_write (struct vty *vty, struct interface *ifp) static void -rtadv_event (enum rtadv_event event, int val) +rtadv_event (struct zebra_vrf *zvrf, enum rtadv_event event, int val) { + struct rtadv *rtadv = &zvrf->rtadv; + switch (event) { case RTADV_START: if (! rtadv->ra_read) - rtadv->ra_read = thread_add_read (zebrad.master, rtadv_read, NULL, val); + rtadv->ra_read = thread_add_read (zebrad.master, rtadv_read, zvrf, val); if (! rtadv->ra_timer) rtadv->ra_timer = thread_add_event (zebrad.master, rtadv_timer, - NULL, 0); + zvrf, 0); break; case RTADV_STOP: if (rtadv->ra_timer) @@ -1653,17 +1642,17 @@ rtadv_event (enum rtadv_event event, int val) break; case RTADV_TIMER: if (! rtadv->ra_timer) - rtadv->ra_timer = thread_add_timer (zebrad.master, rtadv_timer, NULL, + rtadv->ra_timer = thread_add_timer (zebrad.master, rtadv_timer, zvrf, val); break; case RTADV_TIMER_MSEC: if (! rtadv->ra_timer) rtadv->ra_timer = thread_add_timer_msec (zebrad.master, rtadv_timer, - NULL, val); + zvrf, val); break; case RTADV_READ: if (! rtadv->ra_read) - rtadv->ra_read = thread_add_read (zebrad.master, rtadv_read, NULL, val); + rtadv->ra_read = thread_add_read (zebrad.master, rtadv_read, zvrf, val); break; default: break; @@ -1672,17 +1661,29 @@ rtadv_event (enum rtadv_event event, int val) } void -rtadv_init (void) +rtadv_init (struct zebra_vrf *zvrf) { - int sock; + zvrf->rtadv.sock = rtadv_make_socket (zvrf->vrf_id); +} - sock = rtadv_make_socket (); - if (sock < 0) - return; +void +rtadv_terminate (struct zebra_vrf *zvrf) +{ + rtadv_event (zvrf, RTADV_STOP, 0); + + if (zvrf->rtadv.sock >= 0) + { + close (zvrf->rtadv.sock); + zvrf->rtadv.sock = -1; + } - rtadv = rtadv_new (); - rtadv->sock = sock; + zvrf->rtadv.adv_if_count = 0; + zvrf->rtadv.adv_msec_if_count = 0; +} +void +rtadv_cmd_init (void) +{ install_element (INTERFACE_NODE, &ipv6_nd_suppress_ra_cmd); install_element (INTERFACE_NODE, &no_ipv6_nd_suppress_ra_cmd); install_element (INTERFACE_NODE, &ipv6_nd_ra_interval_cmd); @@ -1777,8 +1778,18 @@ if_leave_all_router (int sock, struct interface *ifp) #else void -rtadv_init (void) +rtadv_init (struct zebra_vrf *zvrf) +{ + /* Empty.*/; +} +void +rtadv_terminate (struct zebra_vrf *zvrf) +{ + /* Empty.*/; +} +void +rtadv_cmd_init (void) { /* Empty.*/; } -#endif /* RTADV && HAVE_IPV6 */ +#endif /* HAVE_RTADV && HAVE_IPV6 */ diff --git a/zebra/rtadv.h b/zebra/rtadv.h index 564a4c66d..160814b20 100644 --- a/zebra/rtadv.h +++ b/zebra/rtadv.h @@ -26,6 +26,9 @@ #include "vty.h" #include "zebra/interface.h" +/* NB: RTADV is defined in zebra/interface.h above */ +#if defined (HAVE_RTADV) + /* Router advertisement prefix. */ struct rtadv_prefix { @@ -56,7 +59,6 @@ struct rtadv_prefix }; extern void rtadv_config_write (struct vty *, struct interface *); -extern void rtadv_init (void); /* RFC4584 Extension to Sockets API for Mobile IPv6 */ @@ -96,4 +98,10 @@ struct nd_opt_homeagent_info { /* Home Agent info */ extern const char *rtadv_pref_strs[]; +#endif /* HAVE_RTADV */ + +extern void rtadv_init (struct zebra_vrf *); +extern void rtadv_terminate (struct zebra_vrf *); +extern void rtadv_cmd_init (void); + #endif /* _ZEBRA_RTADV_H */ diff --git a/zebra/rtread_getmsg.c b/zebra/rtread_getmsg.c index 81bf0de64..e1ec67095 100644 --- a/zebra/rtread_getmsg.c +++ b/zebra/rtread_getmsg.c @@ -25,6 +25,8 @@ #include "prefix.h" #include "log.h" #include "if.h" +#include "vrf.h" +#include "vty.h" #include "zebra/rib.h" #include "zebra/zserv.h" @@ -90,11 +92,12 @@ handle_route_entry (mib2_ipRouteEntry_t *routeEntry) gateway.s_addr = routeEntry->ipRouteNextHop; rib_add_ipv4 (ZEBRA_ROUTE_KERNEL, zebra_flags, &prefix, - &gateway, NULL, 0, 0, 0, 0, SAFI_UNICAST); + &gateway, NULL, 0, VRF_DEFAULT, RT_TABLE_MAIN, + 0, 0, 0, SAFI_UNICAST); } void -route_read (void) +route_read (struct zebra_vrf *zvrf) { char storage[RT_BUFSIZ]; @@ -109,6 +112,10 @@ route_read (void) struct strbuf msgdata; int flags, dev, retval, process; + if (zvrf->vrf_id != VRF_DEFAULT) { + return; + } + if ((dev = open (_PATH_GETMSG_ROUTE, O_RDWR)) == -1) { zlog_warn ("can't open %s: %s", _PATH_GETMSG_ROUTE, safe_strerror (errno)); @@ -150,13 +157,13 @@ route_read (void) /* This is normal loop termination */ if (retval == 0 && - msgdata.len >= sizeof (struct T_optmgmt_ack) && + (size_t)msgdata.len >= sizeof (struct T_optmgmt_ack) && TLIack->PRIM_type == T_OPTMGMT_ACK && TLIack->MGMT_flags == T_SUCCESS && MIB2hdr->len == 0) break; - if (msgdata.len >= sizeof (struct T_error_ack) && + if ((size_t)msgdata.len >= sizeof (struct T_error_ack) && TLIerr->PRIM_type == T_ERROR_ACK) { zlog_warn ("getmsg(ctl) returned T_ERROR_ACK: %s", safe_strerror ((TLIerr->TLI_error == TSYSERR) @@ -168,7 +175,7 @@ route_read (void) like what GateD does in this instance, but not critical yet. */ if (retval != MOREDATA || - msgdata.len < sizeof (struct T_optmgmt_ack) || + (size_t)msgdata.len < sizeof (struct T_optmgmt_ack) || TLIack->PRIM_type != T_OPTMGMT_ACK || TLIack->MGMT_flags != T_SUCCESS) { errno = ENOMSG; diff --git a/zebra/rtread_netlink.c b/zebra/rtread_netlink.c index 066e84433..1f658646e 100644 --- a/zebra/rtread_netlink.c +++ b/zebra/rtread_netlink.c @@ -23,10 +23,9 @@ #include #include "zebra/zserv.h" +#include "rt_netlink.h" -extern void netlink_route_read (void); - -void route_read (void) +void route_read (struct zebra_vrf *zvrf) { - netlink_route_read (); + netlink_route_read (zvrf); } diff --git a/zebra/rtread_proc.c b/zebra/rtread_proc.c deleted file mode 100644 index 07e8491ad..000000000 --- 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/rtread_sysctl.c b/zebra/rtread_sysctl.c index b8f5bde70..385e15069 100644 --- a/zebra/rtread_sysctl.c +++ b/zebra/rtread_sysctl.c @@ -24,6 +24,7 @@ #include "memory.h" #include "log.h" +#include "vrf.h" #include "zebra/zserv.h" #include "zebra/rt.h" @@ -31,7 +32,7 @@ /* Kernel routing table read up by sysctl function. */ void -route_read (void) +route_read (struct zebra_vrf *zvrf) { caddr_t buf, end, ref; size_t bufsiz; @@ -47,7 +48,10 @@ route_read (void) NET_RT_DUMP, 0 }; - + + if (zvrf->vrf_id != VRF_DEFAULT) + return; + /* Get buffer size. */ if (sysctl (mib, MIBSIZ, NULL, &bufsiz, NULL, 0) < 0) { diff --git a/zebra/test_main.c b/zebra/test_main.c index 70a1a3a65..09f53adfb 100644 --- a/zebra/test_main.c +++ b/zebra/test_main.c @@ -29,6 +29,7 @@ #include "log.h" #include "privs.h" #include "sigevent.h" +#include "vrf.h" #include "zebra/rib.h" #include "zebra/zserv.h" @@ -103,8 +104,8 @@ usage (char *progname, int status) exit (status); } - -static unsigned int test_ifindex = 0; + +static ifindex_t test_ifindex = 0; /* testrib commands */ DEFUN (test_interface_state, @@ -149,7 +150,7 @@ test_cmd_init (void) { install_element (INTERFACE_NODE, &test_interface_state_cmd); } - + /* SIGHUP handler. */ static void sighup (void) @@ -195,7 +196,72 @@ struct quagga_signal_t zebra_signals[] = .handler = &sigint, }, }; - + +/* Callback upon creating a new VRF. */ +static int +zebra_vrf_new (vrf_id_t vrf_id, void **info) +{ + struct zebra_vrf *zvrf = *info; + + if (! zvrf) + { + zvrf = zebra_vrf_alloc (vrf_id); + *info = (void *)zvrf; + } + + return 0; +} + +/* Callback upon enabling a VRF. */ +static int +zebra_vrf_enable (vrf_id_t vrf_id, void **info) +{ + struct zebra_vrf *zvrf = (struct zebra_vrf *) (*info); + + assert (zvrf); + + kernel_init (zvrf); + route_read (zvrf); + + return 0; +} + +/* Callback upon disabling a VRF. */ +static int +zebra_vrf_disable (vrf_id_t vrf_id, void **info) +{ + struct zebra_vrf *zvrf = (struct zebra_vrf *) (*info); + struct listnode *list_node; + struct interface *ifp; + + assert (zvrf); + + rib_close_table (zvrf->table[AFI_IP][SAFI_UNICAST]); + rib_close_table (zvrf->table[AFI_IP6][SAFI_UNICAST]); + + for (ALL_LIST_ELEMENTS_RO (vrf_iflist (vrf_id), list_node, ifp)) + { + int operative = if_is_operative (ifp); + UNSET_FLAG (ifp->flags, IFF_UP); + if (operative) + if_down (ifp); + } + + kernel_terminate (zvrf); + + return 0; +} + +/* Zebra VRF initialization. */ +static void +zebra_vrf_init (void) +{ + vrf_add_hook (VRF_NEW_HOOK, zebra_vrf_new); + vrf_add_hook (VRF_ENABLE_HOOK, zebra_vrf_enable); + vrf_add_hook (VRF_DISABLE_HOOK, zebra_vrf_disable); + vrf_init (); +} + /* Main startup routine. */ int main (int argc, char **argv) @@ -280,11 +346,10 @@ main (int argc, char **argv) zebrad.master = thread_master_create (); /* Vty related initialize. */ - signal_init (zebrad.master, Q_SIGC(zebra_signals), zebra_signals); + signal_init (zebrad.master, array_size(zebra_signals), zebra_signals); cmd_init (1); vty_init (zebrad.master); memory_init (); - if_init(); zebra_debug_init (); zebra_if_init (); test_cmd_init (); @@ -294,13 +359,9 @@ main (int argc, char **argv) access_list_init (); /* Make kernel routing socket. */ - kernel_init (); - route_read (); + zebra_vrf_init (); 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 75ba0ccd7..0df7dc24f 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 000000000..22fc6cac9 --- /dev/null +++ b/zebra/zebra_fpm.c @@ -0,0 +1,1756 @@ +/* + * 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; + +/* + * Message format to be used to communicate with the FPM. + */ +typedef enum +{ + ZFPM_MSG_FORMAT_NONE, + ZFPM_MSG_FORMAT_NETLINK, + ZFPM_MSG_FORMAT_PROTOBUF, +} zfpm_msg_format_e; +/* + * Globals. + */ +typedef struct zfpm_glob_t_ +{ + + /* + * True if the FPM module has been enabled. + */ + int enabled; + + /* + * Message format to be used to communicate with the fpm. + */ + zfpm_msg_format_e message_format; + + struct thread_master *master; + + zfpm_state_t state; + + in_addr_t fpm_server; + /* + * 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->zvrf->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, fpm_msg_type_e *msg_type) +{ + size_t len; + int cmd; + len = 0; + + *msg_type = FPM_MSG_TYPE_NONE; + + switch (zfpm_g->message_format) { + + case ZFPM_MSG_FORMAT_PROTOBUF: +#ifdef HAVE_PROTOBUF + len = zfpm_protobuf_encode_route (dest, rib, (uint8_t *) in_buf, + in_buf_len); + *msg_type = FPM_MSG_TYPE_PROTOBUF; +#endif + break; + + case ZFPM_MSG_FORMAT_NETLINK: +#ifdef HAVE_NETLINK + *msg_type = FPM_MSG_TYPE_NETLINK; + cmd = rib ? RTM_NEWROUTE : RTM_DELROUTE; + len = zfpm_netlink_encode_route (cmd, dest, rib, in_buf, in_buf_len); + assert(fpm_msg_align(len) == len); + *msg_type = FPM_MSG_TYPE_NETLINK; +#endif /* HAVE_NETLINK */ + break; + + default: + break; + } + + return len; + +} + +/* + * zfpm_route_for_update + * + * Returns the rib that is to be sent to the FPM for a given dest. + */ +struct rib * +zfpm_route_for_update (rib_dest_t *dest) +{ + struct rib *rib; + + RIB_DEST_FOREACH_ROUTE (dest, rib) + { + if (!CHECK_FLAG (rib->status, RIB_ENTRY_SELECTED_FIB)) + 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; + fpm_msg_type_e msg_type; + + 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; + + 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, + &msg_type); + + assert (data_len); + if (data_len) + { + hdr->msg_type = msg_type; + 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 */ + if (!zfpm_g->fpm_server) + serv.sin_addr.s_addr = htonl (INADDR_LOOPBACK); + else + serv.sin_addr.s_addr = (zfpm_g->fpm_server); + + /* + * 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[PREFIX_STRLEN]; + + /* + * 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 triggering update to FPM - Reason: %s", + prefix2str (&rn->p, buf, sizeof(buf)), 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; +} + +/* + * update fpm connection information + */ +DEFUN ( fpm_remote_ip, + fpm_remote_ip_cmd, + "fpm connection ip A.B.C.D port <1-65535>", + "fpm connection remote ip and port\n" + "Remote fpm server ip A.B.C.D\n" + "Enter ip ") +{ + + in_addr_t fpm_server; + uint32_t port_no; + + fpm_server = inet_addr (argv[0]); + if (fpm_server == INADDR_NONE) + return CMD_ERR_INCOMPLETE; + + port_no = atoi (argv[1]); + if (port_no < TCP_MIN_PORT || port_no > TCP_MAX_PORT) + return CMD_ERR_INCOMPLETE; + + zfpm_g->fpm_server = fpm_server; + zfpm_g->fpm_port = port_no; + + + return CMD_SUCCESS; +} + +DEFUN ( no_fpm_remote_ip, + no_fpm_remote_ip_cmd, + "no fpm connection ip A.B.C.D port <1-65535>", + "fpm connection remote ip and port\n" + "Connection\n" + "Remote fpm server ip A.B.C.D\n" + "Enter ip ") +{ + if (zfpm_g->fpm_server != inet_addr (argv[0]) || + zfpm_g->fpm_port != atoi (argv[1])) + return CMD_ERR_NO_MATCH; + + zfpm_g->fpm_server = FPM_DEFAULT_IP; + zfpm_g->fpm_port = FPM_DEFAULT_PORT; + + return CMD_SUCCESS; +} + + +/* + * zfpm_init_message_format + */ +static inline void +zfpm_init_message_format (const char *format) +{ + int have_netlink, have_protobuf; + + have_netlink = have_protobuf = 0; + +#ifdef HAVE_NETLINK + have_netlink = 1; +#endif + +#ifdef HAVE_PROTOBUF + have_protobuf = 1; +#endif + + zfpm_g->message_format = ZFPM_MSG_FORMAT_NONE; + + if (!format) + { + if (have_netlink) + { + zfpm_g->message_format = ZFPM_MSG_FORMAT_NETLINK; + } + else if (have_protobuf) + { + zfpm_g->message_format = ZFPM_MSG_FORMAT_PROTOBUF; + } + return; + } + + if (!strcmp ("netlink", format)) + { + if (!have_netlink) + { + zlog_err ("FPM netlink message format is not available"); + return; + } + zfpm_g->message_format = ZFPM_MSG_FORMAT_NETLINK; + return; + } + + if (!strcmp ("protobuf", format)) + { + if (!have_protobuf) + { + zlog_err ("FPM protobuf message format is not available"); + return; + } + zfpm_g->message_format = ZFPM_MSG_FORMAT_PROTOBUF; + return; + } + + zlog_warn ("Unknown fpm format '%s'", format); +} + +/** + * fpm_remote_srv_write + * + * Module to write remote fpm connection + * + * Returns ZERO on success. + */ + +int fpm_remote_srv_write (struct vty *vty ) +{ + struct in_addr in; + + in.s_addr = zfpm_g->fpm_server; + + if (zfpm_g->fpm_server != FPM_DEFAULT_IP || + zfpm_g->fpm_port != FPM_DEFAULT_PORT) + vty_out (vty,"fpm connection ip %s port %d%s", inet_ntoa (in),zfpm_g->fpm_port,VTY_NEWLINE); + + return 0; +} + + +/** + * 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 + * @param[in] format to use to talk to the FPM. Can be 'netink' or 'protobuf'. + * + * Returns TRUE on success. + */ +int +zfpm_init (struct thread_master *master, int enable, uint16_t port, + const char *format) +{ + 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; + + 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); + install_element (CONFIG_NODE, &fpm_remote_ip_cmd); + install_element (CONFIG_NODE, &no_fpm_remote_ip_cmd); + + zfpm_init_message_format(format); + + /* + * Disable FPM interface if no suitable format is available. + */ + if (zfpm_g->message_format == ZFPM_MSG_FORMAT_NONE) + enable = 0; + + zfpm_g->enabled = enable; + + if (!enable) { + return 1; + } + + if (!zfpm_g->fpm_server) + zfpm_g->fpm_server = FPM_DEFAULT_IP; + + 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 000000000..ecd23c768 --- /dev/null +++ b/zebra/zebra_fpm.h @@ -0,0 +1,35 @@ +/* + * 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, + const char *message_format); +extern void zfpm_trigger_update (struct route_node *rn, const char *reason); + +#endif /* _ZEBRA_FPM_H */ diff --git a/zebra/zebra_fpm_dt.c b/zebra/zebra_fpm_dt.c new file mode 100644 index 000000000..bd171c89b --- /dev/null +++ b/zebra/zebra_fpm_dt.c @@ -0,0 +1,275 @@ +/* + * zebra_fpm_dt.c + * + * @copyright Copyright (C) 2016 Sproute Networks, Inc. + * + * @author Avneesh Sachdev + * + * 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. + */ + +/* + * Developer tests for the zebra code that interfaces with the + * forwarding plane manager. + * + * The functions here are built into developer builds of zebra (when + * DEV_BUILD is defined), and can be called via the 'invoke' cli + * command. + * + * For example: + * + * # invoke zebra function zfpm_dt_benchmark_protobuf_encode 100000 + * + */ + +#include +#include "log.h" +#include "vrf.h" + +#include "zebra/rib.h" + +#include "zebra_fpm_private.h" + +#include "qpb/qpb_allocator.h" +#include "qpb/linear_allocator.h" + +#include "qpb/qpb.h" +#include "fpm/fpm.pb-c.h" + +/* + * Externs. + */ +extern int zfpm_dt_benchmark_netlink_encode (int argc, const char **argv); +extern int zfpm_dt_benchmark_protobuf_encode (int argc, const char **argv); +extern int zfpm_dt_benchmark_protobuf_decode (int argc, const char **argv); + +/* + * zfpm_dt_find_route + * + * Selects a suitable rib destination for fpm interface tests. + */ +static int +zfpm_dt_find_route (rib_dest_t **dest_p, struct rib **rib_p) +{ + struct route_node *rnode; + route_table_iter_t iter; + struct route_table *table; + rib_dest_t *dest; + struct rib *rib; + int ret; + + table = zebra_vrf_table (AFI_IP, SAFI_UNICAST, VRF_DEFAULT); + if (!table) + return 0; + + route_table_iter_init(&iter, table); + while ((rnode = route_table_iter_next(&iter))) + { + dest = rib_dest_from_rnode (rnode); + + if (!dest) + continue; + + rib = zfpm_route_for_update(dest); + if (!rib) + continue; + + if (rib->nexthop_active_num <= 0) + continue; + + *dest_p = dest; + *rib_p = rib; + ret = 1; + goto done; + } + + ret = 0; + + done: + route_table_iter_cleanup(&iter); + return ret; +} +#ifdef HAVE_NETLINK + +/* + * zfpm_dt_benchmark_netlink_encode + */ +int +zfpm_dt_benchmark_netlink_encode (int argc, const char **argv) +{ + int times, i, len; + rib_dest_t *dest; + struct rib *rib; + char buf[4096]; + + times = 100000; + if (argc > 0) { + times = atoi(argv[0]); + } + + if (!zfpm_dt_find_route(&dest, &rib)) { + return 1; + } + + for (i = 0; i < times; i++) { + len = zfpm_netlink_encode_route(RTM_NEWROUTE, dest, rib, buf, sizeof(buf)); + if (len <= 0) { + return 2; + } + } + return 0; +} + +#endif /* HAVE_NETLINK */ + +#ifdef HAVE_PROTOBUF + +/* + * zfpm_dt_benchmark_protobuf_encode + */ +int +zfpm_dt_benchmark_protobuf_encode (int argc, const char **argv) +{ + int times, i, len; + rib_dest_t *dest; + struct rib *rib; + uint8_t buf[4096]; + + times = 100000; + if (argc > 0) { + times = atoi(argv[0]); + } + + if (!zfpm_dt_find_route(&dest, &rib)) { + return 1; + } + + for (i = 0; i < times; i++) { + len = zfpm_protobuf_encode_route(dest, rib, buf, sizeof(buf)); + if (len <= 0) { + return 2; + } + } + return 0; +} + +/* + * zfpm_dt_log_fpm_message + */ +static void +zfpm_dt_log_fpm_message (Fpm__Message *msg) +{ + Fpm__AddRoute *add_route; + Fpm__Nexthop *nexthop; + struct prefix prefix; + u_char family, nh_family; + uint if_index; + char *if_name; + size_t i; + char buf[INET6_ADDRSTRLEN]; + union g_addr nh_addr; + + if (msg->type != FPM__MESSAGE__TYPE__ADD_ROUTE) + return; + + zfpm_debug ("Add route message"); + add_route = msg->add_route; + + if (!qpb_address_family_get (add_route->address_family, &family)) + return; + + if (!qpb_l3_prefix_get (add_route->key->prefix, family, &prefix)) + return; + + zfpm_debug ("Vrf id: %d, Prefix: %s/%d, Metric: %d", add_route->vrf_id, + inet_ntop (family, &prefix.u.prefix, buf, sizeof (buf)), + prefix.prefixlen, add_route->metric); + + /* + * Go over nexthops. + */ + for (i = 0; i < add_route->n_nexthops; i++) + { + nexthop = add_route->nexthops[i]; + if (!qpb_if_identifier_get (nexthop->if_id, &if_index, &if_name)) + continue; + + if (nexthop->address) + qpb_l3_address_get (nexthop->address, &nh_family, &nh_addr); + + zfpm_debug ("Nexthop - if_index: %d (%s), gateway: %s, ", if_index, + if_name ? if_name : "name not specified", + nexthop->address ? inet_ntoa (nh_addr.ipv4) : "None"); + } +} + +/* + * zfpm_dt_benchmark_protobuf_decode + */ +int +zfpm_dt_benchmark_protobuf_decode (int argc, const char **argv) +{ + int times, i, len; + rib_dest_t *dest; + struct rib *rib; + uint8_t msg_buf[4096]; + QPB_DECLARE_STACK_ALLOCATOR (allocator, 8192); + Fpm__Message *fpm_msg; + + QPB_INIT_STACK_ALLOCATOR (allocator); + + times = 100000; + if (argc > 0) + times = atoi(argv[0]); + + if (!zfpm_dt_find_route (&dest, &rib)) + return 1; + + /* + * Encode the route into the message buffer once only. + */ + len = zfpm_protobuf_encode_route (dest, rib, msg_buf, sizeof (msg_buf)); + if (len <= 0) + return 2; + + // Decode once, and display the decoded message + fpm_msg = fpm__message__unpack(&allocator, len, msg_buf); + + if (fpm_msg) + { + zfpm_dt_log_fpm_message(fpm_msg); + QPB_RESET_STACK_ALLOCATOR (allocator); + } + + /* + * Decode encoded message the specified number of times. + */ + for (i = 0; i < times; i++) + { + fpm_msg = fpm__message__unpack (&allocator, len, msg_buf); + + if (!fpm_msg) + return 3; + + // fpm__message__free_unpacked(msg, NULL); + QPB_RESET_STACK_ALLOCATOR (allocator); + } + return 0; +} + +#endif /* HAVE_PROTOBUF */ diff --git a/zebra/zebra_fpm_netlink.c b/zebra/zebra_fpm_netlink.c new file mode 100644 index 000000000..175d351a1 --- /dev/null +++ b/zebra/zebra_fpm_netlink.c @@ -0,0 +1,495 @@ +/* + * 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 "nexthop.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 + */ + netlink_nh_info_t nhs[MULTIPATH_NUM]; + 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)->vrf_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) + goto skip; + + if (!rib) + { + zlog_err("netlink_route_info_fill RTM_ADDROUTE called without rib info"); + return 0; + } + + 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 (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) +{ + size_t 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 000000000..1c4fd4c22 --- /dev/null +++ b/zebra/zebra_fpm_private.h @@ -0,0 +1,61 @@ +/* + * 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); + +extern int +zfpm_protobuf_encode_route (rib_dest_t *dest, struct rib *rib, + uint8_t *in_buf, size_t in_buf_len); + +extern struct rib *zfpm_route_for_update (rib_dest_t *dest); +#endif /* _ZEBRA_FPM_PRIVATE_H */ diff --git a/zebra/zebra_fpm_protobuf.c b/zebra/zebra_fpm_protobuf.c new file mode 100644 index 000000000..beef310b1 --- /dev/null +++ b/zebra/zebra_fpm_protobuf.c @@ -0,0 +1,311 @@ +/* + * zebra_fpm_protobuf.c + * + * @copyright Copyright (C) 2016 Sproute Networks, Inc. + * + * @author Avneesh Sachdev + * + * 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 "log.h" +#include "rib.h" + +#include "qpb/qpb.pb-c.h" +#include "qpb/qpb.h" +#include "qpb/qpb_allocator.h" +#include "qpb/linear_allocator.h" +#include "fpm/fpm_pb.h" + +#include "zebra_fpm_private.h" + +/* + * create_delete_route_message + */ +static Fpm__DeleteRoute * +create_delete_route_message (qpb_allocator_t *allocator, rib_dest_t *dest, + struct rib *rib) +{ + Fpm__DeleteRoute *msg; + + msg = QPB_ALLOC(allocator, typeof(*msg)); + if (!msg) { + assert(0); + return NULL; + } + + fpm__delete_route__init(msg); + msg->vrf_id = rib_dest_vrf(dest)->vrf_id; + + qpb_address_family_set(&msg->address_family, rib_dest_af(dest)); + + /* + * XXX Hardcode subaddress family for now. + */ + msg->sub_address_family = QPB__SUB_ADDRESS_FAMILY__UNICAST; + msg->key = fpm_route_key_create (allocator, rib_dest_prefix(dest)); + if (!msg->key) { + assert(0); + return NULL; + } + + return msg; +} + +/* + * add_nexthop + */ +static inline int +add_nexthop (qpb_allocator_t *allocator, Fpm__AddRoute *msg, rib_dest_t *dest, + struct nexthop *nexthop) +{ + uint32_t if_index; + union g_addr *gateway, *src; + + gateway = src = NULL; + + if_index = nexthop->ifindex; + + if (nexthop->type == NEXTHOP_TYPE_IPV4 + || nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX) + { + gateway = &nexthop->gate; + if (nexthop->src.ipv4.s_addr) + src = &nexthop->src; + } + + if (nexthop->type == NEXTHOP_TYPE_IPV6 + || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME + || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX) + { + gateway = &nexthop->gate; + } + + if (nexthop->type == NEXTHOP_TYPE_IFINDEX + || nexthop->type == NEXTHOP_TYPE_IFNAME) + { + if (nexthop->src.ipv4.s_addr) + src = &nexthop->src; + } + + if (!gateway && if_index == 0) + return 0; + + /* + * We have a valid nexthop. + */ + { + Fpm__Nexthop *pb_nh; + pb_nh = QPB_ALLOC(allocator, typeof(*pb_nh)); + if (!pb_nh) { + assert(0); + return 0; + } + + fpm__nexthop__init(pb_nh); + + if (if_index != 0) { + pb_nh->if_id = qpb_if_identifier_create (allocator, if_index); + } + + if (gateway) { + pb_nh->address = qpb_l3_address_create (allocator, gateway, + rib_dest_af(dest)); + } + + msg->nexthops[msg->n_nexthops++] = pb_nh; + } + + // TODO: Use src. + + return 1; +} + +/* + * create_add_route_message + */ +static Fpm__AddRoute * +create_add_route_message (qpb_allocator_t *allocator, rib_dest_t *dest, + struct rib *rib) +{ + Fpm__AddRoute *msg; + int discard; + struct nexthop *nexthop, *tnexthop; + int recursing; + uint num_nhs, u; + struct nexthop *nexthops[MAX (MULTIPATH_NUM, 64)]; + + msg = QPB_ALLOC(allocator, typeof(*msg)); + if (!msg) { + assert(0); + return NULL; + } + + fpm__add_route__init(msg); + + msg->vrf_id = rib_dest_vrf(dest)->vrf_id; + + qpb_address_family_set (&msg->address_family, rib_dest_af(dest)); + + /* + * XXX Hardcode subaddress family for now. + */ + msg->sub_address_family = QPB__SUB_ADDRESS_FAMILY__UNICAST; + msg->key = fpm_route_key_create (allocator, rib_dest_prefix(dest)); + qpb_protocol_set (&msg->protocol, rib->type); + + if ((rib->flags & ZEBRA_FLAG_BLACKHOLE) || (rib->flags & ZEBRA_FLAG_REJECT)) + discard = 1; + else + discard = 0; + + if (discard) + { + if (rib->flags & ZEBRA_FLAG_BLACKHOLE) { + msg->route_type = FPM__ROUTE_TYPE__BLACKHOLE; + } else if (rib->flags & ZEBRA_FLAG_REJECT) { + msg->route_type = FPM__ROUTE_TYPE__UNREACHABLE; + } else { + assert (0); + } + return msg; + } + else { + msg->route_type = FPM__ROUTE_TYPE__NORMAL; + } + + msg->metric = rib->metric; + + /* + * Figure out the set of nexthops to be added to the message. + */ + num_nhs = 0; + for (ALL_NEXTHOPS_RO (rib->nexthop, nexthop, tnexthop, recursing)) + { + if (MULTIPATH_NUM != 0 && num_nhs >= MULTIPATH_NUM) + break; + + if (num_nhs >= ZEBRA_NUM_OF(nexthops)) + break; + + if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) + continue; + + if (!CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE)) + continue; + + nexthops[num_nhs] = nexthop; + num_nhs++; + } + + if (!num_nhs) { + zfpm_debug ("netlink_encode_route(): No useful nexthop."); + assert(0); + return NULL; + } + + /* + * And add them to the message. + */ + if (!(msg->nexthops = qpb_alloc_ptr_array(allocator, num_nhs))) { + assert(0); + return NULL; + } + + msg->n_nexthops = 0; + for (u = 0; u < num_nhs; u++) { + if (!add_nexthop(allocator, msg, dest, nexthops[u])) { + assert(0); + return NULL; + } + } + + assert(msg->n_nexthops == num_nhs); + + return msg; +} + +/* + * create_route_message + */ +static Fpm__Message * +create_route_message (qpb_allocator_t *allocator, rib_dest_t *dest, + struct rib *rib) +{ + Fpm__Message *msg; + + msg = QPB_ALLOC(allocator, typeof(*msg)); + if (!msg) { + assert(0); + return NULL; + } + + fpm__message__init(msg); + + if (!rib) { + msg->type = FPM__MESSAGE__TYPE__DELETE_ROUTE; + msg->delete_route = create_delete_route_message(allocator, dest, rib); + if (!msg->delete_route) { + assert(0); + return NULL; + } + return msg; + } + + msg->type = FPM__MESSAGE__TYPE__ADD_ROUTE; + msg->add_route = create_add_route_message(allocator, dest, rib); + if (!msg->add_route) { + assert(0); + return NULL; + } + + return msg; +} + +/* + * zfpm_protobuf_encode_route + * + * Create a protobuf 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_protobuf_encode_route (rib_dest_t *dest, struct rib *rib, + uint8_t *in_buf, size_t in_buf_len) +{ + Fpm__Message *msg; + QPB_DECLARE_STACK_ALLOCATOR (allocator, 4096); + size_t len; + + QPB_INIT_STACK_ALLOCATOR (allocator); + + msg = create_route_message(&allocator, dest, rib); + if (!msg) { + assert(0); + return 0; + } + + len = fpm__message__pack(msg, (uint8_t *) in_buf); + assert(len <= in_buf_len); + + QPB_RESET_STACK_ALLOCATOR (allocator); + return len; +} diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index f7f4d0a21..9ca02904e 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -34,12 +34,16 @@ #include "workqueue.h" #include "prefix.h" #include "routemap.h" +#include "vrf.h" +#include "nexthop.h" #include "zebra/rib.h" #include "zebra/rt.h" #include "zebra/zserv.h" #include "zebra/redistribute.h" #include "zebra/debug.h" +#include "zebra/zebra_fpm.h" +#include "zebra/zebra_rnh.h" /* Default rtm_table for all clients */ extern struct zebra_t zebrad; @@ -68,107 +72,58 @@ static const struct [ZEBRA_ROUTE_ISIS] = {ZEBRA_ROUTE_ISIS, 115}, [ZEBRA_ROUTE_BGP] = {ZEBRA_ROUTE_BGP, 20 /* IBGP is 200. */}, [ZEBRA_ROUTE_BABEL] = {ZEBRA_ROUTE_BABEL, 95}, + [ZEBRA_ROUTE_NHRP] = {ZEBRA_ROUTE_NHRP, 10}, /* no entry/default: 150 */ }; - -/* Vector for routing table. */ -static vector vrf_vector; -/* Allocate new VRF. */ -static struct vrf * -vrf_alloc (const char *name) -{ - struct vrf *vrf; - - vrf = XCALLOC (MTYPE_VRF, sizeof (struct vrf)); - - /* Put name. */ - if (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->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->stable[AFI_IP][SAFI_MULTICAST] = route_table_init (); - vrf->stable[AFI_IP6][SAFI_MULTICAST] = route_table_init (); - - - return vrf; -} +/* RPF lookup behaviour */ +static enum multicast_mode ipv4_multicast_mode = MCAST_NO_CONFIG; -/* Lookup VRF by identifier. */ -struct vrf * -vrf_lookup (u_int32_t id) -{ - return vector_lookup (vrf_vector, id); -} - -/* Initialize VRF. */ -static void -vrf_init (void) +static void __attribute__((format (printf, 4, 5))) +_rnode_zlog(const char *_func, struct route_node *rn, int priority, + const char *msgfmt, ...) { - struct vrf *default_table; - - /* Allocate VRF vector. */ - vrf_vector = vector_init (1); + char prefix[PREFIX_STRLEN], buf[256]; + char msgbuf[512]; + va_list ap; - /* Allocate default main table. */ - default_table = vrf_alloc ("Default-IP-Routing-Table"); - - /* Default table index must be 0. */ - vector_set_index (vrf_vector, 0, default_table); -} + va_start(ap, msgfmt); + vsnprintf(msgbuf, sizeof(msgbuf), msgfmt, ap); + va_end(ap); -/* Lookup route table. */ -struct route_table * -vrf_table (afi_t afi, safi_t safi, u_int32_t id) -{ - struct vrf *vrf; + if (rn) + { + rib_table_info_t *info = rn->table->info; - vrf = vrf_lookup (id); - if (! vrf) - return NULL; + snprintf(buf, sizeof(buf), "%s%s vrf %u", + prefix2str(&rn->p, prefix, sizeof(prefix)), + info->safi == SAFI_MULTICAST ? " (MRIB)" : "", + info->zvrf->vrf_id); + } + else + { + snprintf(buf, sizeof(buf), "{(route_node *) NULL}"); + } - return vrf->table[afi][safi]; + zlog (NULL, priority, "%s: %s: %s", _func, buf, msgbuf); } -/* Lookup static route table. */ -struct route_table * -vrf_static_table (afi_t afi, safi_t safi, u_int32_t id) -{ - struct vrf *vrf; - - vrf = vrf_lookup (id); - if (! vrf) - return NULL; +#define rnode_debug(node, ...) \ + _rnode_zlog(__func__, node, LOG_DEBUG, __VA_ARGS__) +#define rnode_info(node, ...) \ + _rnode_zlog(__func__, node, LOG_INFO, __VA_ARGS__) - return vrf->stable[afi][safi]; -} - -/* Add nexthop to the end of the list. */ -static void -nexthop_add (struct rib *rib, struct nexthop *nexthop) +/* Add nexthop to the end of a rib node's nexthop list */ +void +rib_nexthop_add (struct rib *rib, struct nexthop *nexthop) { - struct nexthop *last; - - for (last = rib->nexthop; last && last->next; last = last->next) - ; - if (last) - last->next = nexthop; - else - rib->nexthop = nexthop; - nexthop->prev = last; - + nexthop_add(&rib->nexthop, nexthop); rib->nexthop_num++; } /* Delete specified nexthop from the list. */ static void -nexthop_delete (struct rib *rib, struct nexthop *nexthop) +rib_nexthop_delete (struct rib *rib, struct nexthop *nexthop) { if (nexthop->next) nexthop->next->prev = nexthop->prev; @@ -179,139 +134,146 @@ nexthop_delete (struct rib *rib, struct nexthop *nexthop) rib->nexthop_num--; } -/* Free nexthop. */ -static void -nexthop_free (struct nexthop *nexthop) -{ - if (nexthop->ifname) - XFREE (0, nexthop->ifname); - XFREE (MTYPE_NEXTHOP, nexthop); -} - struct nexthop * -nexthop_ifindex_add (struct rib *rib, unsigned int ifindex) +rib_nexthop_ifindex_add (struct rib *rib, ifindex_t ifindex) { struct nexthop *nexthop; - nexthop = XCALLOC (MTYPE_NEXTHOP, sizeof (struct nexthop)); + nexthop = nexthop_new (); nexthop->type = NEXTHOP_TYPE_IFINDEX; nexthop->ifindex = ifindex; - nexthop_add (rib, nexthop); + rib_nexthop_add (rib, nexthop); return nexthop; } struct nexthop * -nexthop_ifname_add (struct rib *rib, char *ifname) +rib_nexthop_ifname_add (struct rib *rib, char *ifname) { struct nexthop *nexthop; - nexthop = XCALLOC (MTYPE_NEXTHOP, sizeof (struct nexthop)); + nexthop = nexthop_new (); nexthop->type = NEXTHOP_TYPE_IFNAME; - nexthop->ifname = XSTRDUP (0, ifname); + nexthop->ifname = XSTRDUP (MTYPE_TMP, ifname); - nexthop_add (rib, nexthop); + rib_nexthop_add (rib, nexthop); return nexthop; } struct nexthop * -nexthop_ipv4_add (struct rib *rib, struct in_addr *ipv4, struct in_addr *src) +rib_nexthop_ipv4_add (struct rib *rib, struct in_addr *ipv4, struct in_addr *src) { struct nexthop *nexthop; - nexthop = XCALLOC (MTYPE_NEXTHOP, sizeof (struct nexthop)); + nexthop = nexthop_new (); nexthop->type = NEXTHOP_TYPE_IPV4; nexthop->gate.ipv4 = *ipv4; if (src) nexthop->src.ipv4 = *src; - nexthop_add (rib, nexthop); + rib_nexthop_add (rib, nexthop); return nexthop; } -static struct nexthop * -nexthop_ipv4_ifindex_add (struct rib *rib, struct in_addr *ipv4, - struct in_addr *src, unsigned int ifindex) +struct nexthop * +rib_nexthop_ipv4_ifindex_add (struct rib *rib, struct in_addr *ipv4, + struct in_addr *src, ifindex_t ifindex) { struct nexthop *nexthop; - nexthop = XCALLOC (MTYPE_NEXTHOP, sizeof (struct nexthop)); + nexthop = nexthop_new (); nexthop->type = NEXTHOP_TYPE_IPV4_IFINDEX; nexthop->gate.ipv4 = *ipv4; if (src) nexthop->src.ipv4 = *src; nexthop->ifindex = ifindex; - nexthop_add (rib, nexthop); + rib_nexthop_add (rib, nexthop); return nexthop; } -#ifdef HAVE_IPV6 struct nexthop * -nexthop_ipv6_add (struct rib *rib, struct in6_addr *ipv6) +rib_nexthop_ipv6_add (struct rib *rib, struct in6_addr *ipv6) { struct nexthop *nexthop; - nexthop = XCALLOC (MTYPE_NEXTHOP, sizeof (struct nexthop)); + nexthop = nexthop_new (); nexthop->type = NEXTHOP_TYPE_IPV6; nexthop->gate.ipv6 = *ipv6; - nexthop_add (rib, nexthop); + rib_nexthop_add (rib, nexthop); return nexthop; } static struct nexthop * -nexthop_ipv6_ifname_add (struct rib *rib, struct in6_addr *ipv6, - char *ifname) +rib_nexthop_ipv6_ifname_add (struct rib *rib, struct in6_addr *ipv6, + char *ifname) { struct nexthop *nexthop; - nexthop = XCALLOC (MTYPE_NEXTHOP, sizeof (struct nexthop)); + nexthop = nexthop_new (); nexthop->type = NEXTHOP_TYPE_IPV6_IFNAME; nexthop->gate.ipv6 = *ipv6; - nexthop->ifname = XSTRDUP (0, ifname); + nexthop->ifname = XSTRDUP (MTYPE_TMP, ifname); - nexthop_add (rib, nexthop); + rib_nexthop_add (rib, nexthop); return nexthop; } -static struct nexthop * -nexthop_ipv6_ifindex_add (struct rib *rib, struct in6_addr *ipv6, - unsigned int ifindex) +struct nexthop * +rib_nexthop_ipv6_ifindex_add (struct rib *rib, struct in6_addr *ipv6, + ifindex_t ifindex) { struct nexthop *nexthop; - nexthop = XCALLOC (MTYPE_NEXTHOP, sizeof (struct nexthop)); + nexthop = nexthop_new (); nexthop->type = NEXTHOP_TYPE_IPV6_IFINDEX; nexthop->gate.ipv6 = *ipv6; nexthop->ifindex = ifindex; - nexthop_add (rib, nexthop); + rib_nexthop_add (rib, nexthop); return nexthop; } -#endif /* HAVE_IPV6 */ struct nexthop * -nexthop_blackhole_add (struct rib *rib) +rib_nexthop_blackhole_add (struct rib *rib) { struct nexthop *nexthop; - nexthop = XCALLOC (MTYPE_NEXTHOP, sizeof (struct nexthop)); + nexthop = nexthop_new (); nexthop->type = NEXTHOP_TYPE_BLACKHOLE; SET_FLAG (rib->flags, ZEBRA_FLAG_BLACKHOLE); - nexthop_add (rib, nexthop); + rib_nexthop_add (rib, nexthop); 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 +284,20 @@ 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; + rib->nexthop_mtu = 0; + } /* Make lookup prefix. */ memset (&p, 0, sizeof (struct prefix_ipv4)); @@ -337,7 +306,7 @@ nexthop_active_ipv4 (struct rib *rib, struct nexthop *nexthop, int set, p.prefix = nexthop->gate.ipv4; /* Lookup table. */ - table = vrf_table (AFI_IP, SAFI_UNICAST, 0); + table = zebra_vrf_table (AFI_IP, SAFI_UNICAST, rib->vrf_id); if (! table) return 0; @@ -351,11 +320,11 @@ 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; - if (CHECK_FLAG (match->flags, ZEBRA_FLAG_SELECTED)) + if (CHECK_FLAG (match->status, RIB_ENTRY_SELECTED_FIB)) break; } @@ -372,6 +341,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 +358,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,18 +366,39 @@ 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; + 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. Therefore, the resolved route + * should have the original gateway as nexthop as it + * is directly connected. */ 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->type = NEXTHOP_TYPE_IPV4_IFINDEX; + resolved_hop->gate.ipv4 = nexthop->gate.ipv4; + resolved_hop->ifindex = newhop->ifindex; + } + + nexthop_add(&nexthop->resolved, resolved_hop); } - return 1; + resolved = 1; } - return 0; + if (resolved && set) + rib->nexthop_mtu = match->mtu; + return resolved; } else { @@ -412,7 +409,6 @@ nexthop_active_ipv4 (struct rib *rib, struct nexthop *nexthop, int set, return 0; } -#ifdef HAVE_IPV6 /* If force flag is not set, do not modify falgs at all for uninstall the route from FIB. */ static int @@ -423,13 +419,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)); @@ -438,7 +440,7 @@ nexthop_active_ipv6 (struct rib *rib, struct nexthop *nexthop, int set, p.prefix = nexthop->gate.ipv6; /* Lookup table. */ - table = vrf_table (AFI_IP6, SAFI_UNICAST, 0); + table = zebra_vrf_table (AFI_IP6, SAFI_UNICAST, rib->vrf_id); if (! table) return 0; @@ -452,11 +454,11 @@ 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; - if (CHECK_FLAG (match->flags, ZEBRA_FLAG_SELECTED)) + if (CHECK_FLAG (match->status, RIB_ENTRY_SELECTED_FIB)) break; } @@ -473,6 +475,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 +493,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,20 +501,39 @@ 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); } - return 1; + resolved = 1; } - return 0; + return resolved; } else { @@ -515,46 +543,40 @@ nexthop_active_ipv6 (struct rib *rib, struct nexthop *nexthop, int set, } return 0; } -#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, vrf_id_t vrf_id) { - 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 = zebra_vrf_table (AFI_IP, safi, vrf_id); 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; - if (CHECK_FLAG (match->flags, ZEBRA_FLAG_SELECTED)) + if (CHECK_FLAG (match->status, RIB_ENTRY_SELECTED_FIB)) break; } /* 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,31 +586,119 @@ 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_lookup_ipv4 (struct prefix_ipv4 *p) +rib_match_ipv4_multicast (struct in_addr addr, struct route_node **rn_out, + vrf_id_t vrf_id) +{ + 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, + vrf_id); + case MCAST_URIB_ONLY: + return rib_match_ipv4_safi (addr, SAFI_UNICAST, skip_bgp, rn_out, + vrf_id); + case MCAST_NO_CONFIG: + case MCAST_MIX_MRIB_FIRST: + rib = mrib = rib_match_ipv4_safi (addr, SAFI_MULTICAST, skip_bgp, &m_rn, + vrf_id); + if (!mrib) + rib = urib = rib_match_ipv4_safi (addr, SAFI_UNICAST, skip_bgp, &u_rn, + vrf_id); + break; + case MCAST_MIX_DISTANCE: + mrib = rib_match_ipv4_safi (addr, SAFI_MULTICAST, skip_bgp, &m_rn, + vrf_id); + urib = rib_match_ipv4_safi (addr, SAFI_UNICAST, skip_bgp, &u_rn, + vrf_id); + 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, + vrf_id); + urib = rib_match_ipv4_safi (addr, SAFI_UNICAST, skip_bgp, &u_rn, + vrf_id); + 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 vrf %u: found %s, using %s", + __func__, buf, vrf_id, + 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, vrf_id_t vrf_id) { 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); + table = zebra_vrf_table (AFI_IP, SAFI_UNICAST, vrf_id); if (! table) return 0; @@ -601,11 +711,11 @@ 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; - if (CHECK_FLAG (match->flags, ZEBRA_FLAG_SELECTED)) + if (CHECK_FLAG (match->status, RIB_ENTRY_SELECTED_FIB)) break; } @@ -615,7 +725,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; @@ -635,15 +745,18 @@ rib_lookup_ipv4 (struct prefix_ipv4 *p) * 3: no matches found */ int -rib_lookup_ipv4_route (struct prefix_ipv4 *p, union sockunion * qgate) +rib_lookup_ipv4_route (struct prefix_ipv4 *p, union sockunion * qgate, + vrf_id_t vrf_id) { 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); + table = zebra_vrf_table (AFI_IP, SAFI_UNICAST, vrf_id); if (! table) return ZEBRA_RIB_LOOKUP_ERROR; @@ -658,11 +771,11 @@ 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; - if (CHECK_FLAG (match->flags, ZEBRA_FLAG_SELECTED)) + if (CHECK_FLAG (match->status, RIB_ENTRY_SELECTED_FIB)) break; } @@ -674,42 +787,41 @@ 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 == qgate->sin.sin_addr.s_addr || - nexthop->rgate.ipv4.s_addr == qgate->sin.sin_addr.s_addr) - 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, &qgate->sin.sin_addr.s_addr, 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; } -#ifdef HAVE_IPV6 struct rib * -rib_match_ipv6 (struct in6_addr *addr) +rib_match_ipv6 (struct in6_addr *addr, vrf_id_t vrf_id) { struct prefix_ipv6 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_IP6, SAFI_UNICAST, 0); + table = zebra_vrf_table (AFI_IP6, SAFI_UNICAST, vrf_id); if (! table) return 0; @@ -725,11 +837,11 @@ 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; - if (CHECK_FLAG (match->flags, ZEBRA_FLAG_SELECTED)) + if (CHECK_FLAG (match->status, RIB_ENTRY_SELECTED_FIB)) break; } @@ -751,7 +863,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; @@ -760,7 +872,6 @@ rib_match_ipv6 (struct in6_addr *addr) } return NULL; } -#endif /* HAVE_IPV6 */ #define RIB_SYSTEM_ROUTE(R) \ ((R)->type == ZEBRA_ROUTE_KERNEL || (R)->type == ZEBRA_ROUTE_CONNECT) @@ -779,6 +890,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]; @@ -789,7 +901,7 @@ nexthop_active_check (struct route_node *rn, struct rib *rib, switch (nexthop->type) { case NEXTHOP_TYPE_IFINDEX: - ifp = if_lookup_by_index (nexthop->ifindex); + ifp = if_lookup_by_index_vrf (nexthop->ifindex, rib->vrf_id); if (ifp && if_is_operative(ifp)) SET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE); else @@ -798,7 +910,7 @@ nexthop_active_check (struct route_node *rn, struct rib *rib, case NEXTHOP_TYPE_IPV6_IFNAME: family = AFI_IP6; case NEXTHOP_TYPE_IFNAME: - ifp = if_lookup_by_name (nexthop->ifname); + ifp = if_lookup_by_name_vrf (nexthop->ifname, rib->vrf_id); if (ifp && if_is_operative(ifp)) { if (set) @@ -820,7 +932,6 @@ nexthop_active_check (struct route_node *rn, struct rib *rib, else UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE); break; -#ifdef HAVE_IPV6 case NEXTHOP_TYPE_IPV6: family = AFI_IP6; if (nexthop_active_ipv6 (rib, nexthop, set, rn)) @@ -832,7 +943,7 @@ nexthop_active_check (struct route_node *rn, struct rib *rib, family = AFI_IP6; if (IN6_IS_ADDR_LINKLOCAL (&nexthop->gate.ipv6)) { - ifp = if_lookup_by_index (nexthop->ifindex); + ifp = if_lookup_by_index_vrf (nexthop->ifindex, rib->vrf_id); if (ifp && if_is_operative(ifp)) SET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE); else @@ -846,7 +957,6 @@ nexthop_active_check (struct route_node *rn, struct rib *rib, UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE); } break; -#endif /* HAVE_IPV6 */ case NEXTHOP_TYPE_BLACKHOLE: SET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE); break; @@ -856,11 +966,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]) @@ -868,7 +989,8 @@ nexthop_active_check (struct route_node *rn, struct rib *rib, if (!rmap && proto_rm[family][ZEBRA_ROUTE_MAX]) rmap = route_map_lookup_by_name (proto_rm[family][ZEBRA_ROUTE_MAX]); if (rmap) { - ret = route_map_apply(rmap, &rn->p, RMAP_ZEBRA, nexthop); + struct nexthop_vrfid nh_vrf = {nexthop, rib->vrf_id}; + ret = route_map_apply(rmap, &rn->p, RMAP_ZEBRA, &nh_vrf); } if (ret == RMAP_DENYMATCH) @@ -879,7 +1001,7 @@ nexthop_active_check (struct route_node *rn, struct rib *rib, /* Iterate over all nexthops of the given RIB entry and refresh their * ACTIVE flag. rib->nexthop_active_num is updated accordingly. If any * nexthop is found to toggle the ACTIVE flag, the whole rib structure - * is flagged with ZEBRA_FLAG_CHANGED. The 4th 'set' argument is + * is flagged with RIB_ENTRY_CHANGED. The 4th 'set' argument is * transparently passed to nexthop_active_check(). * * Return value is the new number of active nexthops. @@ -889,10 +1011,10 @@ static int nexthop_active_update (struct route_node *rn, struct rib *rib, int set) { struct nexthop *nexthop; - unsigned int prev_active, prev_index, new_active; - + unsigned int prev_active, new_active; + ifindex_t prev_index; + rib->nexthop_active_num = 0; - UNSET_FLAG (rib->flags, ZEBRA_FLAG_CHANGED); for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) { @@ -902,61 +1024,52 @@ nexthop_active_update (struct route_node *rn, struct rib *rib, int set) rib->nexthop_active_num++; if (prev_active != new_active || prev_index != nexthop->ifindex) - SET_FLAG (rib->flags, ZEBRA_FLAG_CHANGED); + SET_FLAG (rib->status, RIB_ENTRY_CHANGED); } return rib->nexthop_active_num; } - -static void -rib_install_kernel (struct route_node *rn, struct rib *rib) + +static int +rib_update_kernel (struct route_node *rn, struct rib *old, struct rib *new) { int ret = 0; - struct nexthop *nexthop; - - switch (PREFIX_FAMILY (&rn->p)) - { - case AF_INET: - ret = kernel_add_ipv4 (&rn->p, rib); - break; -#ifdef HAVE_IPV6 - case AF_INET6: - ret = kernel_add_ipv6 (&rn->p, rib); - break; -#endif /* HAVE_IPV6 */ + struct nexthop *nexthop, *tnexthop; + rib_table_info_t *info = rn->table->info; + int recursing; + + if (info->safi != SAFI_UNICAST) + { + if (new) + for (ALL_NEXTHOPS_RO(new->nexthop, nexthop, tnexthop, recursing)) + SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB); + if (old) + for (ALL_NEXTHOPS_RO(old->nexthop, nexthop, tnexthop, recursing)) + UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB); + return 0; } + /* + * Make sure we update the FPM any time we send new information to + * the kernel. + */ + zfpm_trigger_update (rn, "updating in kernel"); + + ret = kernel_route_rib (&rn->p, old, new); + /* This condition is never met, if we are using rt_socket.c */ - if (ret < 0) + if (ret < 0 && new) { - for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) - UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB); + for (ALL_NEXTHOPS_RO(new->nexthop, nexthop, tnexthop, recursing)) + UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB); } -} - -/* Uninstall the route from kernel. */ -static int -rib_uninstall_kernel (struct route_node *rn, struct rib *rib) -{ - int ret = 0; - struct nexthop *nexthop; - - switch (PREFIX_FAMILY (&rn->p)) + else if (old && old != new) { - case AF_INET: - ret = kernel_delete_ipv4 (&rn->p, rib); - break; -#ifdef HAVE_IPV6 - case AF_INET6: - ret = kernel_delete_ipv6 (&rn->p, rib); - break; -#endif /* HAVE_IPV6 */ + for (ALL_NEXTHOPS_RO(old->nexthop, nexthop, tnexthop, recursing)) + UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB); } - for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) - UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB); - return ret; } @@ -964,66 +1077,160 @@ rib_uninstall_kernel (struct route_node *rn, struct rib *rib) static void rib_uninstall (struct route_node *rn, struct rib *rib) { - if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED)) + rib_table_info_t *info = rn->table->info; + + if (CHECK_FLAG (rib->status, RIB_ENTRY_SELECTED_FIB)) { + 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); + rib_update_kernel (rn, rib, NULL); UNSET_FLAG (rib->flags, ZEBRA_FLAG_SELECTED); } } 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; +} + +/* Check if 'alternate' RIB entry is better than 'current'. */ +static struct rib * +rib_choose_best (struct rib *current, struct rib *alternate) +{ + if (current == NULL) + return alternate; + + /* filter route selection in following order: + * - connected beats other types + * - lower distance beats higher + * - lower metric beats higher for equal distance + * - last, hence oldest, route wins tie break. + */ + + /* Connected routes. Pick the last connected + * route of the set of lowest metric connected routes. + */ + if (alternate->type == ZEBRA_ROUTE_CONNECT) + { + if (current->type != ZEBRA_ROUTE_CONNECT + || alternate->metric <= current->metric) + return alternate; + + return current; + } + + if (current->type == ZEBRA_ROUTE_CONNECT) + return current; + + /* higher distance loses */ + if (alternate->distance < current->distance) + return alternate; + if (current->distance < alternate->distance) + return current; + + /* metric tie-breaks equal distance */ + if (alternate->metric <= current->metric) + return alternate; + + return current; +} + /* Core function for processing routing information base. */ static void rib_process (struct route_node *rn) { struct rib *rib; struct rib *next; - struct rib *fib = NULL; - struct rib *select = NULL; - struct rib *del = NULL; + struct rib *old_selected = NULL; + struct rib *new_selected = NULL; + struct rib *old_fib = NULL; + struct rib *new_fib = 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 (rn, rib) { - /* The next pointer is saved, because current pointer - * may be passed to rib_unlink() in the middle of iteration. - */ - next = rib->next; - + UNSET_FLAG (rib->status, RIB_ENTRY_CHANGED); + /* Currently installed rib. */ if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED)) { - assert (fib == NULL); - fib = rib; + assert (old_selected == NULL); + old_selected = rib; } - - /* Unlock removed routes, so they'll be freed, bar the FIB entry, - * which we need to do do further work with below. - */ - if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED)) + if (CHECK_FLAG (rib->status, RIB_ENTRY_SELECTED_FIB)) { - 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); - } - else - del = rib; - - continue; + assert (old_fib == NULL); + old_fib = rib; } + + /* Skip deleted entries from selection */ + if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED)) + continue; /* Skip unreachable nexthop. */ if (! nexthop_active_update (rn, rib, 0)) @@ -1033,144 +1240,109 @@ rib_process (struct route_node *rn) if (rib->distance == DISTANCE_INFINITY) continue; - /* Newly selected rib, the common case. */ - if (!select) - { - select = rib; - continue; - } - - /* filter route selection in following order: - * - connected beats other types - * - lower distance beats higher - * - lower metric beats higher for equal distance - * - last, hence oldest, route wins tie break. - */ - - /* Connected routes. Pick the last connected - * route of the set of lowest metric connected routes. - */ - if (rib->type == ZEBRA_ROUTE_CONNECT) - { - if (select->type != ZEBRA_ROUTE_CONNECT - || rib->metric <= select->metric) - select = rib; - continue; - } - else if (select->type == ZEBRA_ROUTE_CONNECT) - continue; - - /* higher distance loses */ - if (rib->distance > select->distance) - continue; - - /* lower wins */ - if (rib->distance < select->distance) - { - select = rib; - continue; - } - - /* metric tie-breaks equal distance */ - if (rib->metric <= select->metric) - select = rib; - } /* for (rib = rn->info; rib; rib = next) */ + if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_FIB_OVERRIDE)) + new_fib = rib_choose_best(new_fib, rib); + else + new_selected = rib_choose_best(new_selected, rib); + } /* RNODE_FOREACH_RIB_SAFE */ + + /* If no FIB override route, use the selected route also for FIB */ + if (new_fib == NULL) + new_fib = new_selected; /* After the cycle is finished, the following pointers will be set: - * select --- the winner RIB entry, if any was found, otherwise NULL - * fib --- the SELECTED RIB entry, if any, otherwise NULL - * del --- equal to fib, if fib is queued for deletion, NULL otherwise - * rib --- NULL + * old_selected --- RIB entry currently having SELECTED + * new_selected --- RIB entry that is newly SELECTED + * old_fib --- RIB entry currently in kernel FIB + * new_fib --- RIB entry that is newly to be in kernel FIB + * + * new_selected will get SELECTED flag, and is going to be redistributed + * the zclients. new_fib (which can be new_selected) will be installed in kernel. */ - /* Same RIB entry is selected. Update FIB and finish. */ - if (select && select == fib) + /* Set real nexthops. */ + if (new_fib) + nexthop_active_update (rn, new_fib, 1); + if (new_selected && new_selected != new_fib) + nexthop_active_update (rn, new_selected, 1); + + /* Update kernel if FIB entry has changed */ + if (old_fib != new_fib + || (new_fib && CHECK_FLAG (new_fib->status, RIB_ENTRY_CHANGED))) + { + if (old_fib && old_fib != new_fib) + { + if (! RIB_SYSTEM_ROUTE (old_fib) && (! new_fib || RIB_SYSTEM_ROUTE (new_fib))) + rib_update_kernel (rn, old_fib, NULL); + UNSET_FLAG (old_fib->status, RIB_ENTRY_SELECTED_FIB); + } + + if (new_fib) + { + /* Install new or replace existing FIB entry */ + SET_FLAG (new_fib->status, RIB_ENTRY_SELECTED_FIB); + if (! RIB_SYSTEM_ROUTE (new_fib)) + rib_update_kernel (rn, old_fib, new_fib); + } + + if (info->safi == SAFI_UNICAST) + zfpm_trigger_update (rn, "updating existing route"); + } + else if (old_fib == new_fib && new_fib && ! RIB_SYSTEM_ROUTE (new_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); - if (CHECK_FLAG (select->flags, ZEBRA_FLAG_CHANGED)) - { - redistribute_delete (&rn->p, select); - if (! RIB_SYSTEM_ROUTE (select)) - rib_uninstall_kernel (rn, select); + /* Housekeeping code to deal with race conditions in kernel with + * linux netlink reporting interface up before IPv4 or IPv6 protocol + * is ready to add routes. This makes sure routes are IN the kernel. + */ + for (ALL_NEXTHOPS_RO(new_fib->nexthop, nexthop, tnexthop, recursing)) + if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB)) + { + installed = 1; + break; + } + if (! installed) + rib_update_kernel (rn, NULL, new_fib); + } - /* Set real nexthop. */ - nexthop_active_update (rn, select, 1); - - if (! RIB_SYSTEM_ROUTE (select)) - rib_install_kernel (rn, select); - redistribute_add (&rn->p, select); + /* Redistribute SELECTED entry */ + if (old_selected != new_selected + || (new_selected && CHECK_FLAG (new_selected->status, RIB_ENTRY_CHANGED))) + { + if (old_selected) + { + if (! new_selected) + redistribute_delete (&rn->p, old_selected); + if (old_selected != new_selected) + UNSET_FLAG (old_selected->flags, ZEBRA_FLAG_SELECTED); } - else if (! RIB_SYSTEM_ROUTE (select)) + + if (new_selected) { - /* Housekeeping code to deal with - race conditions in kernel with linux - netlink reporting interface up before IPv4 or IPv6 protocol - is ready to add routes. - This makes sure the routes are IN the kernel. - */ - - for (nexthop = select->nexthop; nexthop; nexthop = nexthop->next) - if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB)) - { - installed = 1; - break; - } - if (! installed) - rib_install_kernel (rn, select); + /* Install new or replace existing redistributed entry */ + SET_FLAG (new_selected->flags, ZEBRA_FLAG_SELECTED); + redistribute_add (&rn->p, new_selected); } - goto end; - } - - /* At this point we either haven't found the best RIB entry or it is - * different from what we currently intend to flag with SELECTED. In both - * cases, if a RIB block is present in FIB, it should be withdrawn. - */ - if (fib) - { - if (IS_ZEBRA_DEBUG_RIB) - zlog_debug ("%s: %s/%d: Removing existing route, fib %p", __func__, - buf, rn->p.prefixlen, fib); - redistribute_delete (&rn->p, fib); - if (! RIB_SYSTEM_ROUTE (fib)) - rib_uninstall_kernel (rn, fib); - UNSET_FLAG (fib->flags, ZEBRA_FLAG_SELECTED); - - /* Set real nexthop. */ - nexthop_active_update (rn, fib, 1); - } - - /* Regardless of some RIB entry being SELECTED or not before, now we can - * tell, that if a new winner exists, FIB is still not updated with this - * data, but ready to be. - */ - if (select) - { - if (IS_ZEBRA_DEBUG_RIB) - zlog_debug ("%s: %s/%d: Adding route, select %p", __func__, buf, - rn->p.prefixlen, select); - /* Set real nexthop. */ - nexthop_active_update (rn, select, 1); - - if (! RIB_SYSTEM_ROUTE (select)) - rib_install_kernel (rn, select); - SET_FLAG (select->flags, ZEBRA_FLAG_SELECTED); - redistribute_add (&rn->p, select); - } + } - /* FIB route was removed, should be deleted */ - if (del) + /* Remove all RIB entries queued for removal */ + RNODE_FOREACH_RIB_SAFE (rn, rib, next) { - if (IS_ZEBRA_DEBUG_RIB) - zlog_debug ("%s: %s/%d: Deleting fib %p, rn %p", __func__, buf, - rn->p.prefixlen, del, rn); - rib_unlink (rn, del); + if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED)) + { + if (IS_ZEBRA_DEBUG_RIB) + rnode_debug (rn, "rn %p, removing rib %p", + (void *)rn, (void *)rib); + rib_unlink (rn, rib); + } } -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", (void *)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 +1361,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 { @@ -1204,6 +1377,18 @@ process_subq (struct list * subq, u_char qindex) return 1; } +/* + * All meta queues have been processed. Trigger next-hop evaluation. + */ +static void +meta_queue_process_complete (struct work_queue *dummy) +{ + zebra_evaluate_rnh_table(0, AF_INET); +#ifdef HAVE_IPV6 + zebra_evaluate_rnh_table(0, AF_INET6); +#endif /* HAVE_IPV6 */ +} + /* Dispatch the meta queue by picking, processing and unlocking the next RN from * a non-empty sub-queue with lowest priority. wq is equal to zebra->ribq and data * is pointed to the meta queue structure. @@ -1223,7 +1408,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, @@ -1237,6 +1424,7 @@ static const u_char meta_queue_map[ZEBRA_ROUTE_MAX] = { [ZEBRA_ROUTE_BGP] = 3, [ZEBRA_ROUTE_HSLS] = 4, [ZEBRA_ROUTE_BABEL] = 2, + [ZEBRA_ROUTE_NHRP] = 2, }; /* Look into the RN and queue it into one or more priority queues, @@ -1246,32 +1434,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", + (void *)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", + (void *)rn, qindex); } } @@ -1279,14 +1464,26 @@ 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) { - - if (IS_ZEBRA_DEBUG_RIB_Q) + assert (zebra && rn); + + /* Pointless to queue a route_node with no RIB entries to add or remove */ + if (!rnode_to_ribs (rn)) { - char buf[INET6_ADDRSTRLEN]; + zlog_debug ("%s: called for route_node (%p, %d) with no ribs", + __func__, (void *)rn, rn->lock); + zlog_backtrace(LOG_DEBUG); + return; + } + + if (IS_ZEBRA_DEBUG_RIB_Q) + rnode_info (rn, "work queue added"); + + assert (zebra); - zlog_info ("%s: %s/%d: work queue added", __func__, - inet_ntop (rn->p.family, &rn->p.u.prefix, buf, INET6_ADDRSTRLEN), - rn->p.prefixlen); + if (zebra->ribq == NULL) + { + zlog_err ("%s: work_queue does not exist!", __func__); + return; } /* @@ -1301,6 +1498,11 @@ rib_queue_add (struct zebra_t *zebra, struct route_node *rn) work_queue_add (zebra->ribq, zebra->mq); rib_meta_queue_add (zebra->mq, rn); + + if (IS_ZEBRA_DEBUG_RIB_Q) + rnode_debug (rn, "rn %p queued", (void *)rn); + + return; } /* Create new meta queue. @@ -1328,6 +1530,8 @@ meta_queue_new (void) static void rib_queue_init (struct zebra_t *zebra) { + assert (zebra); + if (! (zebra->ribq = work_queue_new (zebra->master, "route_node processing"))) { @@ -1338,12 +1542,17 @@ rib_queue_init (struct zebra_t *zebra) /* fill in the work queue spec */ zebra->ribq->spec.workfunc = &meta_queue_process; zebra->ribq->spec.errorfunc = NULL; + zebra->ribq->spec.completion_func = &meta_queue_process_complete; /* XXX: TODO: These should be runtime configurable via vty */ zebra->ribq->spec.max_retries = 3; zebra->ribq->spec.hold = rib_process_hold_time; if (!(zebra->mq = meta_queue_new ())) + { zlog_err ("%s: could not initialise meta queue!", __func__); + return; + } + return; } /* RIB updates are processed via a queue of pointers to route_nodes. @@ -1352,7 +1561,7 @@ rib_queue_init (struct zebra_t *zebra) * as a route_node will not be requeued, if already queued. * * RIBs are submitted via rib_addnode or rib_delnode which set minimal - * state, or static_install_ipv{4,6} (when an existing RIB is updated) + * state, or static_install_route (when an existing RIB is updated) * and then submit route_node to queue for best-path selection later. * Order of add/delete state changes are preserved for any given RIB. * @@ -1368,17 +1577,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. * @@ -1389,31 +1597,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", (void *)rn, (void *)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); } @@ -1426,32 +1635,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", (void *)rn, (void *)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", (void *)rn, (void *)rib); + + dest = rib_dest_from_rnode (rn); if (rib->next) rib->next->prev = rib->prev; @@ -1460,38 +1671,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", (void *)rn, (void *)rib); SET_FLAG (rib->status, RIB_ENTRY_REMOVED); rib_queue_add (&zebrad, rn); } @@ -1499,8 +1692,8 @@ rib_delnode (struct route_node *rn, struct rib *rib) int rib_add_ipv4 (int type, int flags, struct prefix_ipv4 *p, struct in_addr *gate, struct in_addr *src, - unsigned int ifindex, u_int32_t vrf_id, - u_int32_t metric, u_char distance, safi_t safi) + ifindex_t ifindex, vrf_id_t vrf_id, int table_id, + u_int32_t metric, u_int32_t mtu, u_char distance, safi_t safi) { struct rib *rib; struct rib *same = NULL; @@ -1509,7 +1702,7 @@ rib_add_ipv4 (int type, int flags, struct prefix_ipv4 *p, struct nexthop *nexthop; /* Lookup table. */ - table = vrf_table (AFI_IP, safi, 0); + table = zebra_vrf_table (AFI_IP, safi, vrf_id); if (! table) return 0; @@ -1519,7 +1712,7 @@ rib_add_ipv4 (int type, int flags, struct prefix_ipv4 *p, /* Set default distance by route type. */ if (distance == 0) { - if ((unsigned)type >= sizeof(route_info) / sizeof(route_info[0])) + if ((unsigned)type >= array_size(route_info)) distance = 150; else distance = route_info[type].distance; @@ -1534,7 +1727,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; @@ -1563,7 +1756,9 @@ rib_add_ipv4 (int type, int flags, struct prefix_ipv4 *p, rib->distance = distance; rib->flags = flags; rib->metric = metric; - rib->table = vrf_id; + rib->mtu = mtu; + rib->vrf_id = vrf_id; + rib->table = table_id; rib->nexthop_num = 0; rib->uptime = time (NULL); @@ -1571,12 +1766,12 @@ rib_add_ipv4 (int type, int flags, struct prefix_ipv4 *p, if (gate) { if (ifindex) - nexthop_ipv4_ifindex_add (rib, gate, src, ifindex); + rib_nexthop_ipv4_ifindex_add (rib, gate, src, ifindex); else - nexthop_ipv4_add (rib, gate, src); + rib_nexthop_ipv4_add (rib, gate, src); } else - nexthop_ifindex_add (rib, ifindex); + rib_nexthop_ifindex_add (rib, ifindex); /* If this route is kernel route, set FIB flag to the route. */ if (type == ZEBRA_ROUTE_KERNEL || type == ZEBRA_ROUTE_CONNECT) @@ -1585,14 +1780,16 @@ rib_add_ipv4 (int type, int flags, struct prefix_ipv4 *p, /* Link new rib to node.*/ if (IS_ZEBRA_DEBUG_RIB) - zlog_debug ("%s: calling rib_addnode (%p, %p)", __func__, rn, rib); + zlog_debug ("%s: calling rib_addnode (%p, %p)", + __func__, (void *)rn, (void *)rib); rib_addnode (rn, rib); /* Free implicit route.*/ if (same) { if (IS_ZEBRA_DEBUG_RIB) - zlog_debug ("%s: calling rib_delnode (%p, %p)", __func__, rn, rib); + zlog_debug ("%s: calling rib_delnode (%p, %p)", + __func__, (void *)rn, (void *)rib); rib_delnode (rn, same); } @@ -1605,13 +1802,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[PREFIX_STRLEN]; + 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); + zlog_debug ("%s: dumping RIB entry %p for %s vrf %u", func, (void *)rib, + prefix2str(p, straddr, sizeof(straddr)), rib->vrf_id); zlog_debug ( "%s: refcnt == %lu, uptime == %lu, type == %u, table == %d", @@ -1638,21 +1838,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); } @@ -1668,21 +1868,21 @@ void rib_lookup_and_dump (struct prefix_ipv4 * p) char prefix_buf[INET_ADDRSTRLEN]; /* Lookup table. */ - table = vrf_table (AFI_IP, SAFI_UNICAST, 0); + table = zebra_vrf_table (AFI_IP, SAFI_UNICAST, VRF_DEFAULT); if (! table) { - zlog_err ("%s: vrf_table() returned NULL", __func__); + zlog_err ("%s: zebra_vrf_table() returned NULL", __func__); return; } - inet_ntop (AF_INET, &p->prefix.s_addr, prefix_buf, INET_ADDRSTRLEN); /* Scan the RIB table for exactly matching RIB entry. */ rn = route_node_lookup (table, (struct prefix *) p); /* No route for this prefix. */ if (! rn) { - zlog_debug ("%s: lookup failed for %s/%d", __func__, prefix_buf, p->prefixlen); + zlog_debug ("%s: lookup failed for %s", __func__, + prefix2str((struct prefix*) p, prefix_buf, sizeof(prefix_buf))); return; } @@ -1690,71 +1890,19 @@ 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 ( "%s: rn %p, rib %p: %s, %s", __func__, - rn, - rib, + (void *)rn, + (void *)rib, (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); - } -} - -/* Check if requested address assignment will fail due to another - * route being installed by zebra in FIB already. Take necessary - * actions, if needed: remove such a route from FIB and deSELECT - * corresponding RIB entry. Then put affected RN into RIBQ head. - */ -void rib_lookup_and_pushup (struct prefix_ipv4 * p) -{ - struct route_table *table; - struct route_node *rn; - struct rib *rib; - unsigned changed = 0; - - if (NULL == (table = vrf_table (AFI_IP, SAFI_UNICAST, 0))) - { - zlog_err ("%s: vrf_table() returned NULL", __func__); - return; - } - - /* No matches would be the simplest case. */ - if (NULL == (rn = route_node_lookup (table, (struct prefix *) p))) - return; - - /* Unlock node. */ - route_unlock_node (rn); - - /* Check all RIB entries. In case any changes have to be done, requeue - * the RN into RIBQ head. If the routing message about the new connected - * route (generated by the IP address we are going to assign very soon) - * comes before the RIBQ is processed, the new RIB entry will join - * 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) - { - if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED) && - ! RIB_SYSTEM_ROUTE (rib)) - { - changed = 1; - if (IS_ZEBRA_DEBUG_RIB) - { - 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_uninstall (rn, rib); - } + rib_dump (p, rib); } - if (changed) - rib_queue_add (&zebrad, rn); } int @@ -1764,9 +1912,10 @@ rib_add_ipv4_multipath (struct prefix_ipv4 *p, struct rib *rib, safi_t safi) struct route_node *rn; struct rib *same; struct nexthop *nexthop; + int ret = 0; /* Lookup table. */ - table = vrf_table (AFI_IP, safi, 0); + table = zebra_vrf_table (AFI_IP, safi, rib->vrf_id); if (! table) return 0; @@ -1789,7 +1938,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; @@ -1806,11 +1955,12 @@ rib_add_ipv4_multipath (struct prefix_ipv4 *p, struct rib *rib, safi_t safi) /* Link new rib to node.*/ rib_addnode (rn, rib); + ret = 1; if (IS_ZEBRA_DEBUG_RIB) { zlog_debug ("%s: called rib_addnode (%p, %p) on new RIB entry", - __func__, rn, rib); - rib_dump (__func__, p, rib); + __func__, (void *)rn, (void *)rib); + rib_dump (p, rib); } /* Free implicit route.*/ @@ -1819,44 +1969,53 @@ rib_add_ipv4_multipath (struct prefix_ipv4 *p, struct rib *rib, safi_t safi) if (IS_ZEBRA_DEBUG_RIB) { zlog_debug ("%s: calling rib_delnode (%p, %p) on existing RIB entry", - __func__, rn, same); - rib_dump (__func__, p, same); + __func__, (void *)rn, (void *)same); + rib_dump (p, same); } rib_delnode (rn, same); + ret = -1; } route_unlock_node (rn); - return 0; + return ret; } /* XXX factor with rib_delete_ipv6 */ int rib_delete_ipv4 (int type, int flags, struct prefix_ipv4 *p, - struct in_addr *gate, unsigned int ifindex, u_int32_t vrf_id, safi_t safi) + struct in_addr *gate, ifindex_t ifindex, + vrf_id_t vrf_id, safi_t safi) { struct route_table *table; struct route_node *rn; struct rib *rib; struct rib *fib = NULL; struct rib *same = NULL; - struct nexthop *nexthop; - char buf1[INET_ADDRSTRLEN]; + struct nexthop *nexthop, *tnexthop; + int recursing; + char buf1[PREFIX_STRLEN]; char buf2[INET_ADDRSTRLEN]; /* Lookup table. */ - table = vrf_table (AFI_IP, safi, 0); + table = zebra_vrf_table (AFI_IP, safi, vrf_id); if (! table) return 0; /* 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 vrf %u via %s ifindex %d", + prefix2str (p, buf1, sizeof(buf1)), vrf_id, + inet_ntoa (*gate), + ifindex); + else + zlog_debug ("rib_delete_ipv4(): route delete %s vrf %u ifindex %d", + prefix2str (p, buf1, sizeof(buf1)), vrf_id, + ifindex); + } /* Lookup route node. */ rn = route_node_lookup (table, (struct prefix *) p); @@ -1865,27 +2024,25 @@ rib_delete_ipv4 (int type, int flags, struct prefix_ipv4 *p, if (IS_ZEBRA_DEBUG_KERNEL) { if (gate) - zlog_debug ("route %s/%d via %s ifindex %d doesn't exist in rib", - inet_ntop (AF_INET, &p->prefix, buf1, INET_ADDRSTRLEN), - p->prefixlen, + zlog_debug ("route %s vrf %u via %s ifindex %d doesn't exist in rib", + prefix2str (p, buf1, sizeof(buf1)), vrf_id, inet_ntop (AF_INET, gate, buf2, INET_ADDRSTRLEN), ifindex); else - zlog_debug ("route %s/%d ifindex %d doesn't exist in rib", - inet_ntop (AF_INET, &p->prefix, buf1, INET_ADDRSTRLEN), - p->prefixlen, + zlog_debug ("route %s vrf %u ifindex %d doesn't exist in rib", + prefix2str (p, buf1, sizeof(buf1)), vrf_id, ifindex); } return ZEBRA_ERR_RTNOEXIST; } /* 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; - if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED)) + if (CHECK_FLAG (rib->status, RIB_ENTRY_SELECTED_FIB)) fib = rib; if (rib->type != type) @@ -1906,43 +2063,54 @@ 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) { - if (fib && type == ZEBRA_ROUTE_KERNEL) - { - /* Unset flags. */ - for (nexthop = fib->nexthop; nexthop; nexthop = nexthop->next) - UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB); - - UNSET_FLAG (fib->flags, ZEBRA_FLAG_SELECTED); - } + if (fib && type == ZEBRA_ROUTE_KERNEL && + CHECK_FLAG(flags, ZEBRA_FLAG_SELFROUTE)) + { + if (IS_ZEBRA_DEBUG_KERNEL) + { + zlog_debug ("Zebra route %s/%d was deleted by others from kernel", + inet_ntop (AF_INET, &p->prefix, buf1, INET_ADDRSTRLEN), + p->prefixlen); + } + /* This means someone else, other than Zebra, has deleted + * a Zebra router from the kernel. We will add it back */ + rib_update_kernel(rn, NULL, fib); + } else { if (IS_ZEBRA_DEBUG_KERNEL) { if (gate) - zlog_debug ("route %s/%d via %s ifindex %d type %d doesn't exist in rib", - inet_ntop (AF_INET, &p->prefix, buf1, INET_ADDRSTRLEN), - p->prefixlen, + zlog_debug ("route %s vrf %u via %s ifindex %d type %d " + "doesn't exist in rib", + prefix2str (p, buf1, sizeof(buf1)), vrf_id, inet_ntop (AF_INET, gate, buf2, INET_ADDRSTRLEN), ifindex, type); else - zlog_debug ("route %s/%d ifindex %d type %d doesn't exist in rib", - inet_ntop (AF_INET, &p->prefix, buf1, INET_ADDRSTRLEN), - p->prefixlen, + zlog_debug ("route %s vrf %u ifindex %d type %d doesn't exist in rib", + prefix2str (p, buf1, sizeof(buf1)), vrf_id, ifindex, type); } @@ -1957,23 +2125,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_route (afi_t afi, safi_t safi, struct prefix *p, struct static_route *si) { struct rib *rib; struct route_node *rn; struct route_table *table; /* Lookup table. */ - table = vrf_table (AFI_IP, SAFI_UNICAST, 0); + table = zebra_vrf_table (afi, safi, si->vrf_id); 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; @@ -1984,20 +2152,33 @@ static_install_ipv4 (struct prefix *p, struct static_ipv4 *si) if (rib) { + /* if tag value changed , update old value in RIB */ + if (rib->tag != si->tag) + rib->tag = si->tag; + /* Same distance static route is there. Update it with new nexthop. */ route_unlock_node (rn); switch (si->type) { - case STATIC_IPV4_GATEWAY: - nexthop_ipv4_add (rib, &si->gate.ipv4, NULL); - break; - case STATIC_IPV4_IFNAME: - nexthop_ifname_add (rib, si->gate.ifname); - break; - case STATIC_IPV4_BLACKHOLE: - nexthop_blackhole_add (rib); - break; + case STATIC_IPV4_GATEWAY: + rib_nexthop_ipv4_add (rib, &si->addr.ipv4, NULL); + break; + case STATIC_IPV4_IFNAME: + rib_nexthop_ifname_add (rib, si->ifname); + break; + case STATIC_IPV4_BLACKHOLE: + rib_nexthop_blackhole_add (rib); + break; + case STATIC_IPV6_GATEWAY: + rib_nexthop_ipv6_add (rib, &si->addr.ipv6); + break; + case STATIC_IPV6_IFNAME: + rib_nexthop_ifname_add (rib, si->ifname); + break; + case STATIC_IPV6_GATEWAY_IFNAME: + rib_nexthop_ipv6_ifname_add (rib, &si->addr.ipv6, si->ifname); + break; } rib_queue_add (&zebrad, rn); } @@ -2009,19 +2190,31 @@ static_install_ipv4 (struct prefix *p, struct static_ipv4 *si) rib->type = ZEBRA_ROUTE_STATIC; rib->distance = si->distance; rib->metric = 0; + rib->vrf_id = si->vrf_id; + rib->table = zebrad.rtm_table_default; rib->nexthop_num = 0; + rib->tag = si->tag; switch (si->type) { - case STATIC_IPV4_GATEWAY: - nexthop_ipv4_add (rib, &si->gate.ipv4, NULL); - break; - case STATIC_IPV4_IFNAME: - nexthop_ifname_add (rib, si->gate.ifname); - break; - case STATIC_IPV4_BLACKHOLE: - nexthop_blackhole_add (rib); - break; + case STATIC_IPV4_GATEWAY: + rib_nexthop_ipv4_add (rib, &si->addr.ipv4, NULL); + break; + case STATIC_IPV4_IFNAME: + rib_nexthop_ifname_add (rib, si->ifname); + break; + case STATIC_IPV4_BLACKHOLE: + rib_nexthop_blackhole_add (rib); + break; + case STATIC_IPV6_GATEWAY: + rib_nexthop_ipv6_add (rib, &si->addr.ipv6); + break; + case STATIC_IPV6_IFNAME: + rib_nexthop_ifname_add (rib, si->ifname); + break; + case STATIC_IPV6_GATEWAY_IFNAME: + rib_nexthop_ipv6_ifname_add (rib, &si->addr.ipv6, si->ifname); + break; } /* Save the flags of this static routes (reject, blackhole) */ @@ -2033,25 +2226,38 @@ static_install_ipv4 (struct prefix *p, struct static_ipv4 *si) } static int -static_ipv4_nexthop_same (struct nexthop *nexthop, struct static_ipv4 *si) +static_nexthop_same (struct nexthop *nexthop, struct static_route *si) { if (nexthop->type == NEXTHOP_TYPE_IPV4 && si->type == STATIC_IPV4_GATEWAY - && IPV4_ADDR_SAME (&nexthop->gate.ipv4, &si->gate.ipv4)) + && IPV4_ADDR_SAME (&nexthop->gate.ipv4, &si->addr.ipv4)) return 1; if (nexthop->type == NEXTHOP_TYPE_IFNAME && si->type == STATIC_IPV4_IFNAME - && strcmp (nexthop->ifname, si->gate.ifname) == 0) + && strcmp (nexthop->ifname, si->ifname) == 0) return 1; if (nexthop->type == NEXTHOP_TYPE_BLACKHOLE && si->type == STATIC_IPV4_BLACKHOLE) return 1; + if (nexthop->type == NEXTHOP_TYPE_IPV6 + && si->type == STATIC_IPV6_GATEWAY + && IPV6_ADDR_SAME (&nexthop->gate.ipv6, &si->addr.ipv6)) + return 1; + if (nexthop->type == NEXTHOP_TYPE_IFNAME + && si->type == STATIC_IPV6_IFNAME + && strcmp (nexthop->ifname, si->ifname) == 0) + return 1; + if (nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME + && si->type == STATIC_IPV6_GATEWAY_IFNAME + && IPV6_ADDR_SAME (&nexthop->gate.ipv6, &si->addr.ipv6) + && strcmp (nexthop->ifname, si->ifname) == 0) + return 1; return 0; } /* Uninstall static route from RIB. */ static void -static_uninstall_ipv4 (struct prefix *p, struct static_ipv4 *si) +static_uninstall_route (afi_t afi, safi_t safi, struct prefix *p, struct static_route *si) { struct route_node *rn; struct rib *rib; @@ -2059,7 +2265,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 = zebra_vrf_table (afi, safi, si->vrf_id); if (! table) return; @@ -2068,12 +2274,13 @@ 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; - if (rib->type == ZEBRA_ROUTE_STATIC && rib->distance == si->distance) + if (rib->type == ZEBRA_ROUTE_STATIC && rib->distance == si->distance && + rib->tag == si->tag) break; } @@ -2085,7 +2292,7 @@ static_uninstall_ipv4 (struct prefix *p, struct static_ipv4 *si) /* Lookup nexthop. */ for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) - if (static_ipv4_nexthop_same (nexthop, si)) + if (static_nexthop_same (nexthop, si)) break; /* Can't find nexthop. */ @@ -2102,7 +2309,7 @@ static_uninstall_ipv4 (struct prefix *p, struct static_ipv4 *si) { if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB)) rib_uninstall (rn, rib); - nexthop_delete (rib, nexthop); + rib_nexthop_delete (rib, nexthop); nexthop_free (nexthop); rib_queue_add (&zebrad, rn); } @@ -2110,21 +2317,20 @@ 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, route_tag_t tag, + u_char distance, vrf_id_t vrf_id) { u_char type = 0; struct route_node *rn; - struct static_ipv4 *si; - struct static_ipv4 *pp; - struct static_ipv4 *cp; - struct static_ipv4 *update = NULL; - struct route_table *stable; + struct static_route *si; + struct static_route *pp; + struct static_route *cp; + struct static_route *update = NULL; + struct zebra_vrf *zvrf = vrf_info_get (vrf_id); + struct route_table *stable = zvrf->stable[AFI_IP][safi]; - /* Lookup table. */ - stable = vrf_static_table (AFI_IP, SAFI_UNICAST, vrf_id); if (! stable) return -1; @@ -2143,10 +2349,11 @@ static_add_ipv4 (struct prefix *p, struct in_addr *gate, const char *ifname, for (si = rn->info; si; si = si->next) { if (type == si->type - && (! gate || IPV4_ADDR_SAME (gate, &si->gate.ipv4)) - && (! ifname || strcmp (ifname, si->gate.ifname) == 0)) + && (! gate || IPV4_ADDR_SAME (gate, &si->addr.ipv4)) + && (! ifname || strcmp (ifname, si->ifname) == 0)) { - if (distance == si->distance) + if (distance == si->distance && + tag == si->tag) { route_unlock_node (rn); return 0; @@ -2156,21 +2363,23 @@ static_add_ipv4 (struct prefix *p, struct in_addr *gate, const char *ifname, } } - /* Distance changed. */ + /* Distance or tag changed. */ if (update) - static_delete_ipv4 (p, gate, ifname, update->distance, vrf_id); + static_delete_ipv4_safi (safi, p, gate, ifname, update->tag, update->distance, vrf_id); /* Make new static route structure. */ - si = XCALLOC (MTYPE_STATIC_IPV4, sizeof (struct static_ipv4)); + si = XCALLOC (MTYPE_STATIC_ROUTE, sizeof (struct static_route)); si->type = type; si->distance = distance; + si->tag = tag; si->flags = flags; + si->vrf_id = vrf_id; if (gate) - si->gate.ipv4 = *gate; + si->addr.ipv4 = *gate; if (ifname) - si->gate.ifname = XSTRDUP (0, ifname); + si->ifname = XSTRDUP (MTYPE_TMP, ifname); /* Add new static route information to the tree with sort by distance value and gateway address. */ @@ -2182,9 +2391,9 @@ static_add_ipv4 (struct prefix *p, struct in_addr *gate, const char *ifname, continue; if (si->type == STATIC_IPV4_GATEWAY && cp->type == STATIC_IPV4_GATEWAY) { - if (ntohl (si->gate.ipv4.s_addr) < ntohl (cp->gate.ipv4.s_addr)) + if (ntohl (si->addr.ipv4.s_addr) < ntohl (cp->addr.ipv4.s_addr)) break; - if (ntohl (si->gate.ipv4.s_addr) > ntohl (cp->gate.ipv4.s_addr)) + if (ntohl (si->addr.ipv4.s_addr) > ntohl (cp->addr.ipv4.s_addr)) continue; } } @@ -2200,23 +2409,23 @@ 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_route (AFI_IP, 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, route_tag_t tag, u_char distance, + vrf_id_t vrf_id) { u_char type = 0; struct route_node *rn; - struct static_ipv4 *si; + struct static_route *si; struct route_table *stable; /* Lookup table. */ - stable = vrf_static_table (AFI_IP, SAFI_UNICAST, vrf_id); + stable = zebra_vrf_static_table (AFI_IP, safi, vrf_id); if (! stable) return -1; @@ -2236,8 +2445,9 @@ static_delete_ipv4 (struct prefix *p, struct in_addr *gate, const char *ifname, /* Find same static route is the tree */ for (si = rn->info; si; si = si->next) if (type == si->type - && (! gate || IPV4_ADDR_SAME (gate, &si->gate.ipv4)) - && (! ifname || strcmp (ifname, si->gate.ifname) == 0)) + && (! gate || IPV4_ADDR_SAME (gate, &si->addr.ipv4)) + && (! ifname || strcmp (ifname, si->ifname) == 0) + && (! tag || (tag == si->tag))) break; /* Can't find static route. */ @@ -2248,7 +2458,7 @@ static_delete_ipv4 (struct prefix *p, struct in_addr *gate, const char *ifname, } /* Install into rib. */ - static_uninstall_ipv4 (p, si); + static_uninstall_route (AFI_IP, safi, p, si); /* Unlink static route from linked list. */ if (si->prev) @@ -2261,41 +2471,19 @@ static_delete_ipv4 (struct prefix *p, struct in_addr *gate, const char *ifname, /* Free static route configuration. */ if (ifname) - XFREE (0, si->gate.ifname); - XFREE (MTYPE_STATIC_IPV4, si); + XFREE (0, si->ifname); + XFREE (MTYPE_STATIC_ROUTE, si); route_unlock_node (rn); 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, - u_int32_t metric, u_char distance, safi_t safi) + struct in6_addr *gate, ifindex_t ifindex, + vrf_id_t vrf_id, int table_id, + u_int32_t metric, u_int32_t mtu, u_char distance, safi_t safi) { struct rib *rib; struct rib *same = NULL; @@ -2304,7 +2492,7 @@ rib_add_ipv6 (int type, int flags, struct prefix_ipv6 *p, struct nexthop *nexthop; /* Lookup table. */ - table = vrf_table (AFI_IP6, safi, 0); + table = zebra_vrf_table (AFI_IP6, safi, vrf_id); if (! table) return 0; @@ -2318,16 +2506,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; @@ -2355,7 +2539,9 @@ rib_add_ipv6 (int type, int flags, struct prefix_ipv6 *p, rib->distance = distance; rib->flags = flags; rib->metric = metric; - rib->table = vrf_id; + rib->mtu = mtu; + rib->vrf_id = vrf_id; + rib->table = table_id; rib->nexthop_num = 0; rib->uptime = time (NULL); @@ -2363,12 +2549,12 @@ rib_add_ipv6 (int type, int flags, struct prefix_ipv6 *p, if (gate) { if (ifindex) - nexthop_ipv6_ifindex_add (rib, gate, ifindex); + rib_nexthop_ipv6_ifindex_add (rib, gate, ifindex); else - nexthop_ipv6_add (rib, gate); + rib_nexthop_ipv6_add (rib, gate); } else - nexthop_ifindex_add (rib, ifindex); + rib_nexthop_ifindex_add (rib, ifindex); /* If this route is kernel route, set FIB flag to the route. */ if (type == ZEBRA_ROUTE_KERNEL || type == ZEBRA_ROUTE_CONNECT) @@ -2377,68 +2563,162 @@ 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__, (void *)rn, (void *)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__, (void *)rn, (void *)same); + rib_dump (p, same); + } rib_delnode (rn, same); + } route_unlock_node (rn); return 0; } -/* XXX factor with rib_delete_ipv6 */ int -rib_delete_ipv6 (int type, int flags, struct prefix_ipv6 *p, - struct in6_addr *gate, unsigned int ifindex, u_int32_t vrf_id, safi_t safi) +rib_add_ipv6_multipath (struct prefix_ipv6 *p, struct rib *rib, safi_t safi) { struct route_table *table; struct route_node *rn; - struct rib *rib; - struct rib *fib = NULL; struct rib *same = NULL; struct nexthop *nexthop; - char buf1[INET6_ADDRSTRLEN]; - char buf2[INET6_ADDRSTRLEN]; + int ret = 0; - /* Apply mask. */ - apply_mask_ipv6 (p); + if (!rib) + return 0; /* why are we getting called with NULL rib */ /* Lookup table. */ - table = vrf_table (AFI_IP6, safi, 0); + table = zebra_vrf_table (AFI_IP6, safi, rib->vrf_id); + if (! table) return 0; - - /* Lookup route node. */ - rn = route_node_lookup (table, (struct prefix *) p); - if (! rn) - { - if (IS_ZEBRA_DEBUG_KERNEL) - { - if (gate) - zlog_debug ("route %s/%d via %s ifindex %d doesn't exist in rib", - inet_ntop (AF_INET6, &p->prefix, buf1, INET6_ADDRSTRLEN), - p->prefixlen, - inet_ntop (AF_INET6, gate, buf2, INET6_ADDRSTRLEN), - ifindex); - else - zlog_debug ("route %s/%d ifindex %d doesn't exist in rib", - inet_ntop (AF_INET6, &p->prefix, buf1, INET6_ADDRSTRLEN), - p->prefixlen, - ifindex); - } - return ZEBRA_ERR_RTNOEXIST; - } - /* Lookup same type route. */ - for (rib = rn->info; rib; rib = rib->next) + /* Make sure mask is applied. */ + apply_mask_ipv6 (p); + + /* Set default distance by route type. */ + if (rib->distance == 0) { - if (CHECK_FLAG(rib->status, RIB_ENTRY_REMOVED)) - continue; + rib->distance = route_info[rib->type].distance; - if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED)) - fib = rib; + /* iBGP distance is 200. */ + if (rib->type == ZEBRA_ROUTE_BGP + && CHECK_FLAG (rib->flags, ZEBRA_FLAG_IBGP)) + rib->distance = 200; + } - if (rib->type != type) + /* Lookup route node.*/ + rn = route_node_get (table, (struct prefix *) p); + + /* If same type of route are installed, treat it as a implicit + withdraw. */ + RNODE_FOREACH_RIB (rn, same) { + if (CHECK_FLAG (same->status, RIB_ENTRY_REMOVED)) { + continue; + } + if (same->type != rib->type) { + continue; + } + + if (same->table != rib->table) { + continue; + } + if (same->type != ZEBRA_ROUTE_CONNECT) { + break; + } + } + + /* If this route is kernel route, set FIB flag to the route. */ + if (rib->type == ZEBRA_ROUTE_KERNEL || rib->type == ZEBRA_ROUTE_CONNECT) { + for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) { + SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB); + } + } + + /* Link new rib to node.*/ + rib_addnode (rn, rib); + ret = 1; + /* 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 ((struct prefix *)p, same); + } + rib_delnode (rn, same); + ret = -1; + } + + route_unlock_node (rn); + return ret; +} + +/* XXX factor with rib_delete_ipv6 */ +int +rib_delete_ipv6 (int type, int flags, struct prefix_ipv6 *p, + struct in6_addr *gate, ifindex_t ifindex, + vrf_id_t vrf_id, safi_t safi) +{ + struct route_table *table; + struct route_node *rn; + struct rib *rib; + struct rib *fib = NULL; + struct rib *same = NULL; + struct nexthop *nexthop, *tnexthop; + int recursing; + char buf1[PREFIX_STRLEN]; + char buf2[INET6_ADDRSTRLEN]; + + /* Apply mask. */ + apply_mask_ipv6 (p); + + /* Lookup table. */ + table = zebra_vrf_table (AFI_IP6, safi, vrf_id); + if (! table) + return 0; + + /* Lookup route node. */ + rn = route_node_lookup (table, (struct prefix *) p); + if (! rn) + { + if (IS_ZEBRA_DEBUG_KERNEL) + { + if (gate) + zlog_debug ("route %s vrf %u via %s ifindex %d doesn't exist in rib", + prefix2str (p, buf1, sizeof(buf1)), vrf_id, + inet_ntop (AF_INET6, gate, buf2, INET6_ADDRSTRLEN), + ifindex); + else + zlog_debug ("route %s vrf %u ifindex %d doesn't exist in rib", + prefix2str (p, buf1, sizeof(buf1)), vrf_id, + ifindex); + } + return ZEBRA_ERR_RTNOEXIST; + } + + /* Lookup same type route. */ + RNODE_FOREACH_RIB (rn, rib) + { + if (CHECK_FLAG(rib->status, RIB_ENTRY_REMOVED)) + continue; + + if (CHECK_FLAG (rib->status, RIB_ENTRY_SELECTED_FIB)) + fib = rib; + + if (rib->type != type) continue; if (rib->type == ZEBRA_ROUTE_CONNECT && (nexthop = rib->nexthop) && nexthop->type == NEXTHOP_TYPE_IFINDEX) @@ -2456,43 +2736,55 @@ 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 kernel. */ if (! same) { - if (fib && type == ZEBRA_ROUTE_KERNEL) - { - /* Unset flags. */ - for (nexthop = fib->nexthop; nexthop; nexthop = nexthop->next) - UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB); - - UNSET_FLAG (fib->flags, ZEBRA_FLAG_SELECTED); - } + if (fib && type == ZEBRA_ROUTE_KERNEL && + CHECK_FLAG(flags, ZEBRA_FLAG_SELFROUTE)) + { + if (IS_ZEBRA_DEBUG_KERNEL) + { + zlog_debug ("Zebra route %s/%d was deleted by others from kernel", + inet_ntop (AF_INET, &p->prefix, buf1, INET_ADDRSTRLEN), + p->prefixlen); + } + /* This means someone else, other than Zebra, has deleted a Zebra + * route from the kernel. We will add it back */ + rib_update_kernel(rn, NULL, fib); + } else { if (IS_ZEBRA_DEBUG_KERNEL) { if (gate) - zlog_debug ("route %s/%d via %s ifindex %d type %d doesn't exist in rib", - inet_ntop (AF_INET6, &p->prefix, buf1, INET6_ADDRSTRLEN), - p->prefixlen, + zlog_debug ("route %s vrf %u via %s ifindex %d type %d " + "doesn't exist in rib", + prefix2str (p, buf1, sizeof(buf1)), vrf_id, inet_ntop (AF_INET6, gate, buf2, INET6_ADDRSTRLEN), ifindex, type); else - zlog_debug ("route %s/%d ifindex %d type %d doesn't exist in rib", - inet_ntop (AF_INET6, &p->prefix, buf1, INET6_ADDRSTRLEN), - p->prefixlen, + zlog_debug ("route %s vrf %u ifindex %d type %d doesn't exist in rib", + prefix2str (p, buf1, sizeof(buf1)), vrf_id, ifindex, type); } @@ -2507,177 +2799,21 @@ 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) -{ - struct rib *rib; - struct route_table *table; - struct route_node *rn; - - /* Lookup table. */ - table = vrf_table (AFI_IP6, SAFI_UNICAST, 0); - if (! table) - return; - - /* Lookup existing route */ - rn = route_node_get (table, p); - for (rib = rn->info; rib; rib = rib->next) - { - if (CHECK_FLAG(rib->status, RIB_ENTRY_REMOVED)) - continue; - - if (rib->type == ZEBRA_ROUTE_STATIC && rib->distance == si->distance) - break; - } - - if (rib) - { - /* Same distance static route is there. Update it with new - nexthop. */ - route_unlock_node (rn); - - switch (si->type) - { - case STATIC_IPV6_GATEWAY: - nexthop_ipv6_add (rib, &si->ipv6); - break; - case STATIC_IPV6_IFNAME: - nexthop_ifname_add (rib, si->ifname); - break; - case STATIC_IPV6_GATEWAY_IFNAME: - nexthop_ipv6_ifname_add (rib, &si->ipv6, si->ifname); - break; - } - rib_queue_add (&zebrad, rn); - } - else - { - /* This is new static route. */ - rib = XCALLOC (MTYPE_RIB, sizeof (struct rib)); - - rib->type = ZEBRA_ROUTE_STATIC; - rib->distance = si->distance; - rib->metric = 0; - rib->nexthop_num = 0; - - switch (si->type) - { - case STATIC_IPV6_GATEWAY: - nexthop_ipv6_add (rib, &si->ipv6); - break; - case STATIC_IPV6_IFNAME: - nexthop_ifname_add (rib, si->ifname); - break; - case STATIC_IPV6_GATEWAY_IFNAME: - nexthop_ipv6_ifname_add (rib, &si->ipv6, si->ifname); - break; - } - - /* Save the flags of this static routes (reject, blackhole) */ - rib->flags = si->flags; - - /* Link this rib to the tree. */ - rib_addnode (rn, rib); - } -} - -static int -static_ipv6_nexthop_same (struct nexthop *nexthop, struct static_ipv6 *si) -{ - if (nexthop->type == NEXTHOP_TYPE_IPV6 - && si->type == STATIC_IPV6_GATEWAY - && IPV6_ADDR_SAME (&nexthop->gate.ipv6, &si->ipv6)) - return 1; - if (nexthop->type == NEXTHOP_TYPE_IFNAME - && si->type == STATIC_IPV6_IFNAME - && strcmp (nexthop->ifname, si->ifname) == 0) - return 1; - if (nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME - && si->type == STATIC_IPV6_GATEWAY_IFNAME - && IPV6_ADDR_SAME (&nexthop->gate.ipv6, &si->ipv6) - && strcmp (nexthop->ifname, si->ifname) == 0) - return 1; - return 0; -} - -static void -static_uninstall_ipv6 (struct prefix *p, struct static_ipv6 *si) -{ - struct route_table *table; - struct route_node *rn; - struct rib *rib; - struct nexthop *nexthop; - - /* Lookup table. */ - table = vrf_table (AFI_IP6, SAFI_UNICAST, 0); - if (! table) - return; - - /* Lookup existing route with type and distance. */ - rn = route_node_lookup (table, (struct prefix *) p); - if (! rn) - return; - - for (rib = rn->info; rib; rib = rib->next) - { - if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED)) - continue; - - if (rib->type == ZEBRA_ROUTE_STATIC && rib->distance == si->distance) - break; - } - - if (! rib) - { - route_unlock_node (rn); - return; - } - - /* Lookup nexthop. */ - for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) - if (static_ipv6_nexthop_same (nexthop, si)) - break; - - /* Can't find nexthop. */ - if (! nexthop) - { - route_unlock_node (rn); - return; - } - - /* Check nexthop. */ - if (rib->nexthop_num == 1) - { - rib_delnode (rn, rib); - } - else - { - if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB)) - rib_uninstall (rn, rib); - nexthop_delete (rib, nexthop); - nexthop_free (nexthop); - rib_queue_add (&zebrad, rn); - } - /* Unlock node. */ - route_unlock_node (rn); -} /* Add static route into static route configuration. */ int static_add_ipv6 (struct prefix *p, u_char type, struct in6_addr *gate, - const char *ifname, u_char flags, u_char distance, - u_int32_t vrf_id) + const char *ifname, u_char flags, route_tag_t tag, + u_char distance, vrf_id_t vrf_id) { struct route_node *rn; - struct static_ipv6 *si; - struct static_ipv6 *pp; - struct static_ipv6 *cp; - struct route_table *stable; + struct static_route *si; + struct static_route *pp; + struct static_route *cp; + struct static_route *update = NULL; + struct zebra_vrf *zvrf = vrf_info_get (vrf_id); + struct route_table *stable = zvrf->stable[AFI_IP6][SAFI_UNICAST]; - /* Lookup table. */ - stable = vrf_static_table (AFI_IP6, SAFI_UNICAST, vrf_id); if (! stable) return -1; @@ -2695,34 +2831,44 @@ static_add_ipv6 (struct prefix *p, u_char type, struct in6_addr *gate, /* Do nothing if there is a same static route. */ for (si = rn->info; si; si = si->next) { - if (distance == si->distance - && type == si->type - && (! gate || IPV6_ADDR_SAME (gate, &si->ipv6)) + if (type == si->type + && tag == si->tag + && (! gate || IPV6_ADDR_SAME (gate, &si->addr.ipv6)) && (! ifname || strcmp (ifname, si->ifname) == 0)) { - route_unlock_node (rn); - return 0; + if (distance == si->distance) + { + route_unlock_node (rn); + return 0; + } + else + update = si; } } + if (update) + static_delete_ipv6(p, type, gate, ifname, tag, update->distance, vrf_id); + /* Make new static route structure. */ - si = XCALLOC (MTYPE_STATIC_IPV6, sizeof (struct static_ipv6)); + si = XCALLOC (MTYPE_STATIC_ROUTE, sizeof (struct static_route)); si->type = type; si->distance = distance; + si->tag = tag; si->flags = flags; + si->vrf_id = vrf_id; switch (type) { case STATIC_IPV6_GATEWAY: - si->ipv6 = *gate; + si->addr.ipv6 = *gate; break; case STATIC_IPV6_IFNAME: - si->ifname = XSTRDUP (0, ifname); + si->ifname = XSTRDUP (MTYPE_TMP, ifname); break; case STATIC_IPV6_GATEWAY_IFNAME: - si->ipv6 = *gate; - si->ifname = XSTRDUP (0, ifname); + si->addr.ipv6 = *gate; + si->ifname = XSTRDUP (MTYPE_TMP, ifname); break; } @@ -2747,7 +2893,7 @@ static_add_ipv6 (struct prefix *p, u_char type, struct in6_addr *gate, si->next = cp; /* Install into rib. */ - static_install_ipv6 (p, si); + static_install_route (AFI_IP6, SAFI_UNICAST, p, si); return 1; } @@ -2755,14 +2901,15 @@ static_add_ipv6 (struct prefix *p, u_char type, struct in6_addr *gate, /* Delete static route from static route configuration. */ int static_delete_ipv6 (struct prefix *p, u_char type, struct in6_addr *gate, - const char *ifname, u_char distance, u_int32_t vrf_id) + const char *ifname, route_tag_t tag, u_char distance, + vrf_id_t vrf_id) { struct route_node *rn; - struct static_ipv6 *si; + struct static_route *si; struct route_table *stable; /* Lookup table. */ - stable = vrf_static_table (AFI_IP6, SAFI_UNICAST, vrf_id); + stable = zebra_vrf_static_table (AFI_IP6, SAFI_UNICAST, vrf_id); if (! stable) return -1; @@ -2775,8 +2922,9 @@ static_delete_ipv6 (struct prefix *p, u_char type, struct in6_addr *gate, for (si = rn->info; si; si = si->next) if (distance == si->distance && type == si->type - && (! gate || IPV6_ADDR_SAME (gate, &si->ipv6)) - && (! ifname || strcmp (ifname, si->ifname) == 0)) + && (! gate || IPV6_ADDR_SAME (gate, &si->addr.ipv6)) + && (! ifname || strcmp (ifname, si->ifname) == 0) + && (! tag || (tag == si->tag))) break; /* Can't find static route. */ @@ -2787,7 +2935,7 @@ static_delete_ipv6 (struct prefix *p, u_char type, struct in6_addr *gate, } /* Install into rib. */ - static_uninstall_ipv6 (p, si); + static_uninstall_route (AFI_IP6, SAFI_UNICAST, p, si); /* Unlink static route from linked list. */ if (si->prev) @@ -2800,33 +2948,32 @@ static_delete_ipv6 (struct prefix *p, u_char type, struct in6_addr *gate, /* Free static route configuration. */ if (ifname) XFREE (0, si->ifname); - XFREE (MTYPE_STATIC_IPV6, si); + XFREE (MTYPE_STATIC_ROUTE, si); return 1; } -#endif /* HAVE_IPV6 */ - + /* RIB update function. */ void -rib_update (void) +rib_update (vrf_id_t vrf_id) { struct route_node *rn; struct route_table *table; - table = vrf_table (AFI_IP, SAFI_UNICAST, 0); + table = zebra_vrf_table (AFI_IP, SAFI_UNICAST, vrf_id); 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); + table = zebra_vrf_table (AFI_IP6, SAFI_UNICAST, vrf_id); 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) @@ -2837,10 +2984,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; @@ -2854,10 +2999,18 @@ rib_weed_table (struct route_table *table) void rib_weed_tables (void) { - rib_weed_table (vrf_table (AFI_IP, SAFI_UNICAST, 0)); - rib_weed_table (vrf_table (AFI_IP6, SAFI_UNICAST, 0)); + vrf_iter_t iter; + struct zebra_vrf *zvrf; + + for (iter = vrf_first (); iter != VRF_ITER_INVALID; iter = vrf_next (iter)) + if ((zvrf = vrf_iter2info (iter)) != NULL) + { + rib_weed_table (zvrf->table[AFI_IP][SAFI_UNICAST]); + rib_weed_table (zvrf->table[AFI_IP6][SAFI_UNICAST]); + } } - + +#if 0 /* Delete self installed routes after zebra is relaunched. */ static void rib_sweep_table (struct route_table *table) @@ -2869,29 +3022,35 @@ 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; if (rib->type == ZEBRA_ROUTE_KERNEL && CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELFROUTE)) { - ret = rib_uninstall_kernel (rn, rib); + ret = rib_update_kernel (rn, rib, NULL); if (! ret) rib_delnode (rn, rib); } } } +#endif /* Sweep all RIB tables. */ void rib_sweep_route (void) { - rib_sweep_table (vrf_table (AFI_IP, SAFI_UNICAST, 0)); - rib_sweep_table (vrf_table (AFI_IP6, SAFI_UNICAST, 0)); + vrf_iter_t iter; + struct zebra_vrf *zvrf; + + for (iter = vrf_first (); iter != VRF_ITER_INVALID; iter = vrf_next (iter)) + if ((zvrf = vrf_iter2info (iter)) != NULL) + { + rib_weed_table (zvrf->table[AFI_IP][SAFI_UNICAST]); + rib_weed_table (zvrf->table[AFI_IP6][SAFI_UNICAST]); + } } /* Remove specific by protocol routes from 'table'. */ @@ -2905,9 +3064,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) @@ -2924,24 +3082,38 @@ rib_score_proto_table (u_char proto, struct route_table *table) unsigned long rib_score_proto (u_char proto) { - return rib_score_proto_table (proto, vrf_table (AFI_IP, SAFI_UNICAST, 0)) - +rib_score_proto_table (proto, vrf_table (AFI_IP6, SAFI_UNICAST, 0)); + vrf_iter_t iter; + struct zebra_vrf *zvrf; + unsigned long cnt = 0; + + for (iter = vrf_first (); iter != VRF_ITER_INVALID; iter = vrf_next (iter)) + if ((zvrf = vrf_iter2info (iter)) != NULL) + cnt += rib_score_proto_table (proto, zvrf->table[AFI_IP][SAFI_UNICAST]) + +rib_score_proto_table (proto, zvrf->table[AFI_IP6][SAFI_UNICAST]); + + return cnt; } /* Close RIB and clean up kernel routes. */ -static void +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->status, RIB_ENTRY_SELECTED_FIB)) + continue; + + if (info->safi == SAFI_UNICAST) + zfpm_trigger_update (rn, NULL); + + if (! RIB_SYSTEM_ROUTE (rib)) + rib_update_kernel (rn, rib, NULL); } } @@ -2949,15 +3121,227 @@ rib_close_table (struct route_table *table) void rib_close (void) { - rib_close_table (vrf_table (AFI_IP, SAFI_UNICAST, 0)); - rib_close_table (vrf_table (AFI_IP6, SAFI_UNICAST, 0)); + vrf_iter_t iter; + struct zebra_vrf *zvrf; + + for (iter = vrf_first (); iter != VRF_ITER_INVALID; iter = vrf_next (iter)) + if ((zvrf = vrf_iter2info (iter)) != NULL) + { + rib_close_table (zvrf->table[AFI_IP][SAFI_UNICAST]); + rib_close_table (zvrf->table[AFI_IP6][SAFI_UNICAST]); + } } - + /* Routing information base initialize. */ void rib_init (void) { rib_queue_init (&zebrad); - /* 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 (vrf_id_t vrf_id, vrf_id_t *next_id_p) +{ + vrf_iter_t iter = vrf_iterator (vrf_id); + struct zebra_vrf *zvrf = vrf_iter2info (iter); + + /* The same one ? Then find out the next. */ + if (zvrf && (zvrf->vrf_id == vrf_id)) + zvrf = vrf_iter2info (vrf_next (iter)); + + if (zvrf) + { + *next_id_p = zvrf->vrf_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 = VRF_DEFAULT; + 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 = zebra_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; +} + +/* Lookup VRF by identifier. */ +struct zebra_vrf * +zebra_vrf_lookup (vrf_id_t vrf_id) +{ + return vrf_info_lookup (vrf_id); +} + +/* + * Create a routing table for the specific AFI/SAFI in the given VRF. + */ +static void +zebra_vrf_table_create (struct zebra_vrf *zvrf, afi_t afi, safi_t safi) +{ + rib_table_info_t *info; + struct route_table *table; + + assert (!zvrf->table[afi][safi]); + + table = route_table_init (); + zvrf->table[afi][safi] = table; + + info = XCALLOC (MTYPE_RIB_TABLE_INFO, sizeof (*info)); + info->zvrf = zvrf; + info->afi = afi; + info->safi = safi; + table->info = info; +} + +/* Allocate new zebra VRF. */ +struct zebra_vrf * +zebra_vrf_alloc (vrf_id_t vrf_id) +{ + struct zebra_vrf *zvrf; +#ifdef HAVE_NETLINK + char nl_name[64]; +#endif + + zvrf = XCALLOC (MTYPE_ZEBRA_VRF, sizeof (struct zebra_vrf)); + + /* Allocate routing table and static table. */ + zebra_vrf_table_create (zvrf, AFI_IP, SAFI_UNICAST); + zebra_vrf_table_create (zvrf, AFI_IP6, SAFI_UNICAST); + zvrf->stable[AFI_IP][SAFI_UNICAST] = route_table_init (); + zvrf->stable[AFI_IP6][SAFI_UNICAST] = route_table_init (); + zebra_vrf_table_create (zvrf, AFI_IP, SAFI_MULTICAST); + zebra_vrf_table_create (zvrf, AFI_IP6, SAFI_MULTICAST); + zvrf->stable[AFI_IP][SAFI_MULTICAST] = route_table_init (); + zvrf->stable[AFI_IP6][SAFI_MULTICAST] = route_table_init (); + + zvrf->rnh_table[AFI_IP] = route_table_init(); + zvrf->rnh_table[AFI_IP6] = route_table_init(); + + /* Set VRF ID */ + zvrf->vrf_id = vrf_id; + +#ifdef HAVE_NETLINK + /* Initialize netlink sockets */ + snprintf (nl_name, 64, "netlink-listen (vrf %u)", vrf_id); + zvrf->netlink.sock = -1; + zvrf->netlink.name = XSTRDUP (MTYPE_NETLINK_NAME, nl_name); + + snprintf (nl_name, 64, "netlink-cmd (vrf %u)", vrf_id); + zvrf->netlink_cmd.sock = -1; + zvrf->netlink_cmd.name = XSTRDUP (MTYPE_NETLINK_NAME, nl_name); +#endif + + return zvrf; +} + +/* Lookup the routing table in an enabled VRF. */ +struct route_table * +zebra_vrf_table (afi_t afi, safi_t safi, vrf_id_t vrf_id) +{ + struct zebra_vrf *zvrf = vrf_info_lookup (vrf_id); + + if (!zvrf) + return NULL; + + if (afi >= AFI_MAX || safi >= SAFI_MAX) + return NULL; + + return zvrf->table[afi][safi]; +} + +/* Lookup the static routing table in a VRF. */ +struct route_table * +zebra_vrf_static_table (afi_t afi, safi_t safi, vrf_id_t vrf_id) +{ + struct zebra_vrf *zvrf = vrf_info_lookup (vrf_id); + + if (!zvrf) + return NULL; + + if (afi >= AFI_MAX || safi >= SAFI_MAX) + return NULL; + + return zvrf->stable[afi][safi]; +} + diff --git a/zebra/zebra_rnh.c b/zebra/zebra_rnh.c new file mode 100644 index 000000000..859b6d793 --- /dev/null +++ b/zebra/zebra_rnh.c @@ -0,0 +1,629 @@ +/* Zebra next hop tracking code + * Copyright (C) 2013 Cumulus Networks, Inc. + * + * 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 "table.h" +#include "memory.h" +#include "str.h" +#include "command.h" +#include "if.h" +#include "log.h" +#include "sockunion.h" +#include "linklist.h" +#include "thread.h" +#include "workqueue.h" +#include "prefix.h" +#include "routemap.h" +#include "stream.h" +#include "nexthop.h" + +#include "zebra/rib.h" +#include "zebra/rt.h" +#include "zebra/zserv.h" +#include "zebra/redistribute.h" +#include "zebra/debug.h" +#include "zebra/zebra_rnh.h" + +#define lookup_rnh_table(v, f) \ +({ \ + struct zebra_vrf *zvrf; \ + struct route_table *t = NULL; \ + zvrf = zebra_vrf_lookup(v); \ + if (zvrf) \ + t = zvrf->rnh_table[family2afi(f)]; \ + t; \ +}) + +static void free_state(struct rib *rib); +static void copy_state(struct rnh *rnh, struct rib *rib); +static int compare_state(struct rib *r1, struct rib *r2); +static int send_client(struct rnh *rnh, struct zserv *client, vrf_id_t vrf_id); +static void print_rnh(struct route_node *rn, struct vty *vty); + +char * +rnh_str (struct rnh *rnh, char *buf, int size) +{ + prefix2str(&(rnh->node->p), buf, size); + return buf; +} + +struct rnh * +zebra_add_rnh (struct prefix *p, vrf_id_t vrfid) +{ + struct route_table *table; + struct route_node *rn; + struct rnh *rnh = NULL; + + if (IS_ZEBRA_DEBUG_NHT) + { + char buf[INET6_ADDRSTRLEN]; + prefix2str(p, buf, INET6_ADDRSTRLEN); + zlog_debug("add rnh %s in vrf %d", buf, vrfid); + } + table = lookup_rnh_table(vrfid, PREFIX_FAMILY(p)); + if (!table) + { + zlog_debug("add_rnh: rnh table not found\n"); + return NULL; + } + + /* Make it sure prefixlen is applied to the prefix. */ + apply_mask (p); + + /* Lookup (or add) route node.*/ + rn = route_node_get (table, p); + + if (!rn->info) + { + rnh = XCALLOC(MTYPE_RNH, sizeof(struct rnh)); + rnh->client_list = list_new(); + route_lock_node (rn); + rn->info = rnh; + rnh->node = rn; + } + + route_unlock_node (rn); + return (rn->info); +} + +struct rnh * +zebra_lookup_rnh (struct prefix *p, vrf_id_t vrfid) +{ + struct route_table *table; + struct route_node *rn; + + table = lookup_rnh_table(vrfid, PREFIX_FAMILY(p)); + if (!table) + return NULL; + + /* Make it sure prefixlen is applied to the prefix. */ + apply_mask (p); + + /* Lookup route node.*/ + rn = route_node_lookup (table, p); + if (!rn) + return NULL; + + route_unlock_node (rn); + return (rn->info); +} + +void +zebra_delete_rnh (struct rnh *rnh) +{ + struct route_node *rn; + + if (!rnh || !(rn = rnh->node)) + return; + + if (IS_ZEBRA_DEBUG_NHT) + { + char buf[INET6_ADDRSTRLEN]; + zlog_debug("delete rnh %s", rnh_str(rnh, buf, INET6_ADDRSTRLEN)); + } + + list_free(rnh->client_list); + free_state(rnh->state); + XFREE(MTYPE_RNH, rn->info); + rn->info = NULL; + route_unlock_node (rn); + return; +} + +void +zebra_add_rnh_client (struct rnh *rnh, struct zserv *client, vrf_id_t vrf_id) +{ + if (IS_ZEBRA_DEBUG_NHT) + { + char buf[INET6_ADDRSTRLEN]; + zlog_debug("client %s registers rnh %s", + zebra_route_string(client->proto), + rnh_str(rnh, buf, INET6_ADDRSTRLEN)); + } + if (!listnode_lookup(rnh->client_list, client)) + { + listnode_add(rnh->client_list, client); + send_client(rnh, client, vrf_id); + } +} + +void +zebra_remove_rnh_client (struct rnh *rnh, struct zserv *client) +{ + if (IS_ZEBRA_DEBUG_NHT) + { + char buf[INET6_ADDRSTRLEN]; + zlog_debug("client %s unregisters rnh %s", + zebra_route_string(client->proto), + rnh_str(rnh, buf, INET6_ADDRSTRLEN)); + } + listnode_delete(rnh->client_list, client); + if (list_isempty(rnh->client_list)) + zebra_delete_rnh(rnh); +} + +int +zebra_evaluate_rnh_table (vrf_id_t vrfid, int family) +{ + struct route_table *ptable; + struct route_table *ntable; + struct route_node *prn; + struct route_node *nrn; + struct rnh *rnh; + struct zserv *client; + struct listnode *node; + struct rib *rib; + + ntable = lookup_rnh_table(vrfid, family); + if (!ntable) + { + zlog_debug("evaluate_rnh_table: rnh table not found\n"); + return -1; + } + + ptable = zebra_vrf_table(family2afi(family), SAFI_UNICAST, vrfid); + if (!ptable) + { + zlog_debug("evaluate_rnh_table: prefix table not found\n"); + return -1; + } + + for (nrn = route_top (ntable); nrn; nrn = route_next (nrn)) + { + if (!nrn->info) + continue; + + rnh = nrn->info; + prn = route_node_match(ptable, &nrn->p); + if (!prn) + rib = NULL; + else + { + RNODE_FOREACH_RIB(prn, rib) + { + if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED)) + continue; + if (! CHECK_FLAG (rib->status, RIB_ENTRY_SELECTED_FIB)) + continue; + + if (CHECK_FLAG(rnh->flags, ZEBRA_NHT_CONNECTED)) + { + if (rib->type == ZEBRA_ROUTE_CONNECT) + break; + + if (rib->type == ZEBRA_ROUTE_NHRP) + { + struct nexthop *nexthop; + for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) + if (nexthop->type == NEXTHOP_TYPE_IFINDEX || + nexthop->type == NEXTHOP_TYPE_IFNAME) + break; + if (nexthop) + break; + } + } + else + break; + } + } + + if (compare_state(rib, rnh->state)) + { + if (IS_ZEBRA_DEBUG_NHT) + { + char bufn[INET6_ADDRSTRLEN]; + char bufp[INET6_ADDRSTRLEN]; + prefix2str(&nrn->p, bufn, INET6_ADDRSTRLEN); + if (prn) + prefix2str(&prn->p, bufp, INET6_ADDRSTRLEN); + else + strcpy(bufp, "null"); + zlog_debug("rnh %s resolved through route %s - sending " + "nexthop %s event to clients", bufn, bufp, + rib ? "reachable" : "unreachable"); + } + copy_state(rnh, rib); + for (ALL_LIST_ELEMENTS_RO(rnh->client_list, node, client)) + send_client(rnh, client, vrfid); + } + } + return 1; +} + +int +zebra_dispatch_rnh_table (vrf_id_t vrfid, int family, struct zserv *client) +{ + struct route_table *ntable; + struct route_node *nrn; + struct rnh *rnh; + + ntable = lookup_rnh_table(vrfid, family); + if (!ntable) + { + zlog_debug("dispatch_rnh_table: rnh table not found\n"); + return -1; + } + + for (nrn = route_top (ntable); nrn; nrn = route_next (nrn)) + { + if (!nrn->info) + continue; + + rnh = nrn->info; + if (IS_ZEBRA_DEBUG_NHT) + { + char bufn[INET6_ADDRSTRLEN]; + prefix2str(&nrn->p, bufn, INET6_ADDRSTRLEN); + zlog_debug("rnh %s - sending nexthop %s event to client %s", bufn, + rnh->state ? "reachable" : "unreachable", + zebra_route_string(client->proto)); + } + send_client(rnh, client, vrfid); + } + return 1; +} + +void +zebra_print_rnh_table (vrf_id_t vrfid, int af, struct vty *vty) +{ + struct route_table *table; + struct route_node *rn; + + table = lookup_rnh_table(vrfid, af); + if (!table) + { + zlog_debug("print_rnhs: rnh table not found\n"); + return; + } + + for (rn = route_top(table); rn; rn = route_next(rn)) + if (rn->info) + print_rnh(rn, vty); +} + +int +zebra_cleanup_rnh_client (vrf_id_t vrfid, int family, struct zserv *client) +{ + struct route_table *ntable; + struct route_node *nrn; + struct rnh *rnh; + + ntable = lookup_rnh_table(vrfid, family); + if (!ntable) + { + zlog_debug("cleanup_rnh_client: rnh table not found\n"); + return -1; + } + + for (nrn = route_top (ntable); nrn; nrn = route_next (nrn)) + { + if (!nrn->info) + continue; + + rnh = nrn->info; + if (IS_ZEBRA_DEBUG_NHT) + { + char bufn[INET6_ADDRSTRLEN]; + prefix2str(&nrn->p, bufn, INET6_ADDRSTRLEN); + zlog_debug("rnh %s - cleaning state for client %s", bufn, + zebra_route_string(client->proto)); + } + zebra_remove_rnh_client(rnh, client); + } + return 1; +} + +/** + * free_state - free up the rib structure associated with the rnh. + */ +static void +free_state (struct rib *rib) +{ + struct nexthop *nexthop, *next; + + if (!rib) + return; + + /* free RIB and nexthops */ + for (nexthop = rib->nexthop; nexthop; nexthop = next) + { + next = nexthop->next; + nexthop_free (nexthop); + } + XFREE (MTYPE_RIB, rib); +} + +/** + * copy_nexthop - copy a nexthop to the rib structure. + */ +static void +rib_copy_nexthop (struct rib *state, struct nexthop *nh) +{ + struct nexthop *nexthop; + + nexthop = nexthop_new(); + nexthop->flags = nh->flags; + nexthop->type = nh->type; + nexthop->ifindex = nh->ifindex; + if (nh->ifname) + nexthop->ifname = XSTRDUP(0, nh->ifname); + memcpy(&(nexthop->gate), &(nh->gate), sizeof(union g_addr)); + memcpy(&(nexthop->src), &(nh->src), sizeof(union g_addr)); + + rib_nexthop_add(state, nexthop); +} + +static void +copy_state (struct rnh *rnh, struct rib *rib) +{ + struct rib *state; + struct nexthop *nh; + + if (rnh->state) + { + free_state(rnh->state); + rnh->state = NULL; + } + + if (!rib) + return; + + state = XCALLOC (MTYPE_RIB, sizeof (struct rib)); + state->type = rib->type; + state->metric = rib->metric; + + for (nh = rib->nexthop; nh; nh = nh->next) + rib_copy_nexthop(state, nh); + rnh->state = state; +} + +static int +compare_state (struct rib *r1, struct rib *r2) +{ + struct nexthop *nh1; + struct nexthop *nh2; + u_char found_nh = 0; + + if (!r1 && !r2) + return 0; + + if ((!r1 && r2) || (r1 && !r2)) + return 1; + + if (r1->metric != r2->metric) + return 1; + + if (r1->nexthop_num != r2->nexthop_num) + return 1; + + /* We need to verify that the nexthops for r1 match the nexthops for r2. + * Since it is possible for a rib entry to have the same nexthop multiple + * times (Example: [a,a]) we need to keep track of which r2 nexthops we have + * already used as a match against a r1 nexthop. We track this + * via NEXTHOP_FLAG_MATCHED. Clear this flag for all r2 nexthops when you + * are finished. + * + * TRUE: r1 [a,b], r2 [a,b] + * TRUE: r1 [a,b], r2 [b,a] + * FALSE: r1 [a,b], r2 [a,c] + * FALSE: r1 [a,a], r2 [a,b] + */ + for (nh1 = r1->nexthop; nh1; nh1 = nh1->next) + { + found_nh = 0; + for (nh2 = r2->nexthop; nh2; nh2 = nh2->next) + { + if (CHECK_FLAG (nh2->flags, NEXTHOP_FLAG_MATCHED)) + continue; + + if (nexthop_same_no_recurse(nh1, nh2)) + { + SET_FLAG (nh2->flags, NEXTHOP_FLAG_MATCHED); + found_nh = 1; + break; + } + } + + if (!found_nh) + { + for (nh2 = r2->nexthop; nh2; nh2 = nh2->next) + if (CHECK_FLAG (nh2->flags, NEXTHOP_FLAG_MATCHED)) + UNSET_FLAG (nh2->flags, NEXTHOP_FLAG_MATCHED); + return 1; + } + } + + for (nh2 = r2->nexthop; nh2; nh2 = nh2->next) + if (CHECK_FLAG (nh2->flags, NEXTHOP_FLAG_MATCHED)) + UNSET_FLAG (nh2->flags, NEXTHOP_FLAG_MATCHED); + + return 0; +} + +static int +send_client (struct rnh *rnh, struct zserv *client, vrf_id_t vrf_id) +{ + struct stream *s; + struct rib *rib; + unsigned long nump; + u_char num; + struct nexthop *nexthop; + struct route_node *rn; + + rn = rnh->node; + rib = rnh->state; + + /* Get output stream. */ + s = client->obuf; + stream_reset (s); + + zserv_create_header (s, ZEBRA_NEXTHOP_UPDATE, vrf_id); + + stream_putw(s, rn->p.family); + stream_put_prefix (s, &rn->p); + + if (rib) + { + stream_putl (s, rib->metric); + num = 0; + 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) && + ! CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE) && + CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE)) + { + stream_putc (s, nexthop->type); + switch (nexthop->type) + { + case ZEBRA_NEXTHOP_IPV4: + stream_put_in_addr (s, &nexthop->gate.ipv4); + break; + case ZEBRA_NEXTHOP_IFINDEX: + case ZEBRA_NEXTHOP_IFNAME: + stream_putl (s, nexthop->ifindex); + break; + case ZEBRA_NEXTHOP_IPV4_IFINDEX: + case ZEBRA_NEXTHOP_IPV4_IFNAME: + stream_put_in_addr (s, &nexthop->gate.ipv4); + stream_putl (s, nexthop->ifindex); + break; +#ifdef HAVE_IPV6 + case ZEBRA_NEXTHOP_IPV6: + stream_put (s, &nexthop->gate.ipv6, 16); + break; + case ZEBRA_NEXTHOP_IPV6_IFINDEX: + case ZEBRA_NEXTHOP_IPV6_IFNAME: + stream_put (s, &nexthop->gate.ipv6, 16); + stream_putl (s, nexthop->ifindex); + break; +#endif /* HAVE_IPV6 */ + default: + /* do nothing */ + break; + } + num++; + } + stream_putc_at (s, nump, num); + } + else + { + stream_putl (s, 0); + stream_putc (s, 0); + } + stream_putw_at (s, 0, stream_get_endp (s)); + + client->nh_last_upd_time = quagga_time(NULL); + client->last_write_cmd = ZEBRA_NEXTHOP_UPDATE; + return zebra_server_send_message(client); +} + +static void +print_nh (struct nexthop *nexthop, struct vty *vty) +{ + char buf[BUFSIZ]; + + switch (nexthop->type) + { + case NEXTHOP_TYPE_IPV4: + case NEXTHOP_TYPE_IPV4_IFINDEX: + vty_out (vty, " via %s", inet_ntoa (nexthop->gate.ipv4)); + if (nexthop->ifindex) + vty_out (vty, ", %s", ifindex2ifname (nexthop->ifindex)); + break; + case NEXTHOP_TYPE_IPV6: + case NEXTHOP_TYPE_IPV6_IFINDEX: + case NEXTHOP_TYPE_IPV6_IFNAME: + vty_out (vty, " %s", + inet_ntop (AF_INET6, &nexthop->gate.ipv6, buf, BUFSIZ)); + if (nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME) + vty_out (vty, ", %s", nexthop->ifname); + else if (nexthop->ifindex) + vty_out (vty, ", via %s", ifindex2ifname (nexthop->ifindex)); + break; + case NEXTHOP_TYPE_IFINDEX: + vty_out (vty, " is directly connected, %s", + ifindex2ifname (nexthop->ifindex)); + break; + case NEXTHOP_TYPE_IFNAME: + vty_out (vty, " is directly connected, %s", nexthop->ifname); + break; + case NEXTHOP_TYPE_BLACKHOLE: + vty_out (vty, " is directly connected, Null0"); + break; + default: + break; + } + vty_out(vty, "%s", VTY_NEWLINE); +} + +static void +print_rnh (struct route_node *rn, struct vty *vty) +{ + struct rnh *rnh; + struct nexthop *nexthop; + struct listnode *node; + struct zserv *client; + char buf[BUFSIZ]; + + rnh = rn->info; + vty_out(vty, "%s%s", inet_ntop(rn->p.family, &rn->p.u.prefix, buf, BUFSIZ), + VTY_NEWLINE); + if (rnh->state) + { + vty_out(vty, " resolved via %s%s", + zebra_route_string(rnh->state->type), VTY_NEWLINE); + for (nexthop = rnh->state->nexthop; nexthop; nexthop = nexthop->next) + print_nh(nexthop, vty); + } + else + vty_out(vty, " unresolved%s%s", + CHECK_FLAG(rnh->flags, ZEBRA_NHT_CONNECTED) ? "(Connected)" : "", + VTY_NEWLINE); + + vty_out(vty, " Client list:"); + for (ALL_LIST_ELEMENTS_RO(rnh->client_list, node, client)) + vty_out(vty, " %s(fd %d)", zebra_route_string(client->proto), + client->sock); + vty_out(vty, "%s", VTY_NEWLINE); +} diff --git a/zebra/zebra_rnh.h b/zebra/zebra_rnh.h new file mode 100644 index 000000000..574c95fab --- /dev/null +++ b/zebra/zebra_rnh.h @@ -0,0 +1,49 @@ +/* + * Zebra next hop tracking header + * Copyright (C) 2013 Cumulus Networks, Inc. + * + * 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_RNH_H +#define _ZEBRA_RNH_H + +#include "prefix.h" +#include "vty.h" + +/* Nexthop structure. */ +struct rnh +{ + u_char flags; +#define ZEBRA_NHT_CONNECTED 0x1 + struct rib *state; + struct list *client_list; + struct route_node *node; +}; + +extern struct rnh *zebra_add_rnh(struct prefix *p, vrf_id_t vrfid); +extern struct rnh *zebra_lookup_rnh(struct prefix *p, vrf_id_t vrfid); +extern void zebra_delete_rnh(struct rnh *rnh); +extern void zebra_add_rnh_client(struct rnh *rnh, struct zserv *client, vrf_id_t vrf_id_t); +extern void zebra_remove_rnh_client(struct rnh *rnh, struct zserv *client); +extern int zebra_evaluate_rnh_table(vrf_id_t vrfid, int family); +extern int zebra_dispatch_rnh_table(vrf_id_t vrfid, int family, struct zserv *cl); +extern void zebra_print_rnh_table(vrf_id_t vrfid, int family, struct vty *vty); +extern char *rnh_str(struct rnh *rnh, char *buf, int size); +extern int zebra_cleanup_rnh_client(vrf_id_t vrf, int family, struct zserv *client); +#endif /*_ZEBRA_RNH_H */ diff --git a/zebra/zebra_rnh_null.c b/zebra/zebra_rnh_null.c new file mode 100644 index 000000000..664667dd2 --- /dev/null +++ b/zebra/zebra_rnh_null.c @@ -0,0 +1,10 @@ +#include +#include "zebra/rib.h" +#include "zebra/zserv.h" +#include "zebra/zebra_rnh.h" + +int zebra_evaluate_rnh_table (vrf_id_t vrfid, int family) +{ return 0; } + +void zebra_print_rnh_table (vrf_id_t vrfid, int family, struct vty *vty) +{} diff --git a/zebra/zebra_routemap.c b/zebra/zebra_routemap.c index b3111b8e6..5a6a96bf0 100644 --- a/zebra/zebra_routemap.c +++ b/zebra/zebra_routemap.c @@ -28,6 +28,8 @@ #include "command.h" #include "filter.h" #include "plist.h" +#include "vrf.h" +#include "nexthop.h" #include "zebra/zserv.h" @@ -44,10 +46,10 @@ zebra_route_match_add(struct vty *vty, struct route_map_index *index, switch (ret) { case RMAP_RULE_MISSING: - vty_out (vty, "%% Can't find rule.%s", VTY_NEWLINE); + vty_out (vty, "%% Zebra Can't find rule.%s", VTY_NEWLINE); return CMD_WARNING; case RMAP_COMPILE_ERROR: - vty_out (vty, "%% Argument is malformed.%s", VTY_NEWLINE); + vty_out (vty, "%% Zebra Argument is malformed.%s", VTY_NEWLINE); return CMD_WARNING; } } @@ -67,10 +69,10 @@ zebra_route_match_delete (struct vty *vty, struct route_map_index *index, switch (ret) { case RMAP_RULE_MISSING: - vty_out (vty, "%% Can't find rule.%s", VTY_NEWLINE); + vty_out (vty, "%% Zebra Can't find rule.%s", VTY_NEWLINE); return CMD_WARNING; case RMAP_COMPILE_ERROR: - vty_out (vty, "%% Argument is malformed.%s", VTY_NEWLINE); + vty_out (vty, "%% Zebra Argument is malformed.%s", VTY_NEWLINE); return CMD_WARNING; } } @@ -90,10 +92,10 @@ zebra_route_set_add (struct vty *vty, struct route_map_index *index, switch (ret) { case RMAP_RULE_MISSING: - vty_out (vty, "%% Can't find rule.%s", VTY_NEWLINE); + vty_out (vty, "%% Zebra Can't find rule.%s", VTY_NEWLINE); return CMD_WARNING; case RMAP_COMPILE_ERROR: - vty_out (vty, "%% Argument is malformed.%s", VTY_NEWLINE); + vty_out (vty, "%% Zebra Argument is malformed.%s", VTY_NEWLINE); return CMD_WARNING; } } @@ -113,35 +115,39 @@ zebra_route_set_delete (struct vty *vty, struct route_map_index *index, switch (ret) { case RMAP_RULE_MISSING: - vty_out (vty, "%% Can't find rule.%s", VTY_NEWLINE); + vty_out (vty, "%% Zebra Can't find rule.%s", VTY_NEWLINE); return CMD_WARNING; case RMAP_COMPILE_ERROR: - vty_out (vty, "%% Argument is malformed.%s", VTY_NEWLINE); + vty_out (vty, "%% Zebra Argument is malformed.%s", VTY_NEWLINE); return CMD_WARNING; } } return CMD_SUCCESS; } - + /* `match interface IFNAME' */ /* Match function return 1 if match is success else return zero. */ static route_map_result_t route_match_interface (void *rule, struct prefix *prefix, route_map_object_t type, void *object) { + struct nexthop_vrfid *nh_vrf; struct nexthop *nexthop; char *ifname = rule; - unsigned int ifindex; + ifindex_t ifindex; if (type == RMAP_ZEBRA) { if (strcasecmp(ifname, "any") == 0) return RMAP_MATCH; - ifindex = ifname2ifindex(ifname); + nh_vrf = object; + if (!nh_vrf) + return RMAP_NOMATCH; + ifindex = ifname2ifindex_vrf (ifname, nh_vrf->vrf_id); if (ifindex == 0) return RMAP_NOMATCH; - nexthop = object; + nexthop = nh_vrf->nexthop; if (!nexthop) return RMAP_NOMATCH; if (nexthop->ifindex == ifindex) @@ -365,7 +371,8 @@ DEFUN (set_src, "src address\n") { struct in_addr src; - struct interface *pif; + struct interface *pif = NULL; + vrf_iter_t iter; if (inet_pton(AF_INET, argv[0], &src) <= 0) { @@ -373,12 +380,16 @@ DEFUN (set_src, return CMD_WARNING; } - pif = if_lookup_exact_address (src); - if (!pif) - { - vty_out (vty, "%% not a local address%s", VTY_NEWLINE); - return CMD_WARNING; - } + for (iter = vrf_first (); iter != VRF_ITER_INVALID; iter = vrf_next (iter)) + if ((pif = if_lookup_exact_address_vrf (src, vrf_iter2id (iter))) != NULL) + break; + + if (!pif) + { + vty_out (vty, "%% not a local address%s", VTY_NEWLINE); + return CMD_WARNING; + } + return zebra_route_set_add (vty, vty->index, "src", argv[0]); } @@ -414,22 +425,20 @@ route_match_ip_next_hop (void *rule, struct prefix *prefix, { struct access_list *alist; struct nexthop *nexthop; + struct nexthop_vrfid *nh_vrf; struct prefix_ipv4 p; if (type == RMAP_ZEBRA) { - nexthop = object; + nh_vrf = object; + nexthop = nh_vrf->nexthop; 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 +480,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 @@ -480,22 +489,20 @@ route_match_ip_next_hop_prefix_list (void *rule, struct prefix *prefix, { struct prefix_list *plist; struct nexthop *nexthop; + struct nexthop_vrfid *nh_vrf; struct prefix_ipv4 p; if (type == RMAP_ZEBRA) { - nexthop = object; + nh_vrf = object; + nexthop = nh_vrf->nexthop; 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 +540,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 +586,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 +627,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. */ @@ -630,10 +637,10 @@ route_set_src (void *rule, struct prefix *prefix, { if (type == RMAP_ZEBRA) { - struct nexthop *nexthop; + struct nexthop_vrfid *nh_vrf; - nexthop = object; - nexthop->src = *(union g_addr *)rule; + nh_vrf = object; + nh_vrf->nexthop->src = *(union g_addr *)rule; } return RMAP_OKAY; } diff --git a/zebra/zebra_snmp.c b/zebra/zebra_snmp.c index 0fde4bb81..3d005aa55 100644 --- a/zebra/zebra_snmp.c +++ b/zebra/zebra_snmp.c @@ -19,17 +19,15 @@ * 02111-1307, USA. */ +/* + * Currently SNMP is only running properly for MIBs in the default VRF. + */ + #include #ifdef HAVE_SNMP -#ifdef HAVE_NETSNMP #include #include -#else -#include -#include -#include -#endif #include "if.h" #include "log.h" @@ -37,10 +35,11 @@ #include "command.h" #include "smux.h" #include "table.h" +#include "vrf.h" #include "zebra/rib.h" #include "zebra/zserv.h" - + #define IPFWMIB 1,3,6,1,2,1,4,24 /* ipForwardTable */ @@ -84,7 +83,7 @@ #define ROWSTATUS ASN_INTEGER #define IPADDRESS ASN_IPADDRESS #define OBJECTIDENTIFIER ASN_OBJECT_ID - + extern struct zebra_t zebrad; oid ipfw_oid [] = { IPFWMIB }; @@ -136,7 +135,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) @@ -149,14 +148,14 @@ ipFwNumber (struct variable *v, oid objid[], size_t *objid_len, if (smux_header_generic(v, objid, objid_len, exact, val_len, write_method) == MATCH_FAILED) return NULL; - table = vrf_table (AFI_IP, SAFI_UNICAST, 0); + table = zebra_vrf_table (AFI_IP, SAFI_UNICAST, VRF_DEFAULT); if (! table) return NULL; /* 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; @@ -174,14 +173,14 @@ ipCidrNumber (struct variable *v, oid objid[], size_t *objid_len, if (smux_header_generic(v, objid, objid_len, exact, val_len, write_method) == MATCH_FAILED) return NULL; - table = vrf_table (AFI_IP, SAFI_UNICAST, 0); + table = zebra_vrf_table (AFI_IP, SAFI_UNICAST, VRF_DEFAULT); if (! table) return 0; /* 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; @@ -336,7 +335,7 @@ get_fwtable_route_node(struct variable *v, oid objid[], size_t *objid_len, if (exact && (*objid_len != (unsigned) v->namelen + 10)) return; - table = vrf_table (AFI_IP, SAFI_UNICAST, 0); + table = zebra_vrf_table (AFI_IP, SAFI_UNICAST, VRF_DEFAULT); if (! table) return; @@ -375,7 +374,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)) @@ -394,12 +393,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; @@ -457,6 +456,10 @@ ipFwTable (struct variable *v, oid objid[], size_t *objid_len, static struct in_addr netmask; struct nexthop *nexthop; + if (smux_header_table(v, objid, objid_len, exact, val_len, write_method) + == MATCH_FAILED) + return NULL; + get_fwtable_route_node(v, objid, objid_len, exact, &np, &rib); if (!np) return NULL; @@ -555,6 +558,10 @@ static u_char * ipCidrTable (struct variable *v, oid objid[], size_t *objid_len, int exact, size_t *val_len, WriteMethod **write_method) { + if (smux_header_table(v, objid, objid_len, exact, val_len, write_method) + == MATCH_FAILED) + return NULL; + switch (v->magic) { case IPCIDRROUTEDEST: diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index dafcf75af..0c08990f9 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -27,14 +27,25 @@ #include "command.h" #include "table.h" #include "rib.h" +#include "vrf.h" +#include "nexthop.h" #include "zebra/zserv.h" +#include "zebra/zebra_rnh.h" -/* General fucntion for static route. */ +static int do_show_ip_route(struct vty *vty, safi_t safi, vrf_id_t vrf_id); +static void vty_show_ip_route_detail (struct vty *vty, struct route_node *rn, + int mcast); +static void vty_show_ip_route (struct vty *vty, struct route_node *rn, + struct rib *rib); + +/* 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 *tag_str, const char *distance_str, + const char *vrf_id_str) { int ret; u_char distance; @@ -43,6 +54,8 @@ zebra_static_ipv4 (struct vty *vty, int add_cmd, const char *dest_str, struct in_addr mask; const char *ifname; u_char flag = 0; + route_tag_t tag = 0; + vrf_id_t vrf_id = VRF_DEFAULT; ret = str2prefix (dest_str, &p); if (ret <= 0) @@ -72,6 +85,18 @@ zebra_static_ipv4 (struct vty *vty, int add_cmd, const char *dest_str, else distance = ZEBRA_STATIC_DISTANCE_DEFAULT; + /* tag */ + if (tag_str) + tag = atoi (tag_str); + + /* VRF id */ + if (vrf_id_str) + VTY_GET_INTEGER ("VRF ID", vrf_id, vrf_id_str); + + /* tag */ + if (tag_str) + tag = atoi(tag_str); + /* Null0 static route. */ if ((gate_str != NULL) && (strncasecmp (gate_str, "Null0", strlen (gate_str)) == 0)) { @@ -81,9 +106,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, tag, distance, vrf_id); else - static_delete_ipv4 (&p, NULL, NULL, distance, 0); + static_delete_ipv4_safi (safi, &p, NULL, NULL, tag, distance, vrf_id); return CMD_SUCCESS; } @@ -107,9 +132,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, tag, distance, vrf_id); else - static_delete_ipv4 (&p, NULL, NULL, distance, 0); + static_delete_ipv4_safi (safi, &p, NULL, NULL, tag, distance, vrf_id); return CMD_SUCCESS; } @@ -123,9 +148,312 @@ 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, tag, distance, vrf_id); + else + static_delete_ipv4_safi (safi, &p, ifname ? NULL : &gate, ifname, tag, distance, vrf_id); + + return CMD_SUCCESS; +} + +/* 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, NULL, argc > 2 ? argv[2] : NULL, 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 (ip_mroute_dist_vrf, + ip_mroute_dist_vrf_cmd, + "ip mroute A.B.C.D/M (A.B.C.D|INTERFACE) <1-255> " VRF_CMD_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" + "Distance\n" + VRF_CMD_HELP_STR) +{ + VTY_WARN_EXPERIMENTAL(); + return zebra_static_ipv4_safi(vty, SAFI_MULTICAST, 1, argv[0], NULL, argv[1], + NULL, NULL, argc > 3 ? argv[2] : NULL, + argc > 3 ? argv[3] : argv[2]); +} + +ALIAS (ip_mroute_dist_vrf, + ip_mroute_vrf_cmd, + "ip mroute A.B.C.D/M (A.B.C.D|INTERFACE) "VRF_CMD_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" + VRF_CMD_HELP_STR) + +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, NULL, argc > 2 ? argv[2] : NULL, 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 (no_ip_mroute_dist_vrf, + no_ip_mroute_dist_vrf_cmd, + "no ip mroute A.B.C.D/M (A.B.C.D|INTERFACE) <1-255> " VRF_CMD_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" + "Distance\n" + VRF_CMD_HELP_STR) +{ + VTY_WARN_EXPERIMENTAL(); + return zebra_static_ipv4_safi(vty, SAFI_MULTICAST, 0, argv[0], NULL, argv[1], + NULL, NULL, argc > 3 ? argv[2] : NULL, + argc > 3 ? argv[3] : argv[2]); +} + +ALIAS (no_ip_mroute_dist_vrf, + no_ip_mroute_vrf_cmd, + "no ip mroute A.B.C.D/M (A.B.C.D|INTERFACE) " VRF_CMD_STR, + 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" + VRF_CMD_HELP_STR) + +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") +{ + vrf_id_t vrf_id = VRF_DEFAULT; + + if (argc > 0) + VTY_GET_INTEGER ("VRF ID", vrf_id, argv[0]); + + VTY_WARN_EXPERIMENTAL(); + return do_show_ip_route(vty, SAFI_MULTICAST, vrf_id); +} + +ALIAS (show_ip_rpf, + show_ip_rpf_vrf_cmd, + "show ip rpf " VRF_CMD_STR, + SHOW_STR + IP_STR + "Display RPF information for multicast source\n" + VRF_CMD_HELP_STR) + +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; + vrf_id_t vrf_id = VRF_DEFAULT; + int ret; + + if (argc > 1) + VTY_GET_INTEGER ("VRF ID", vrf_id, argv[1]); + + 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, vrf_id); + + 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; +} + +ALIAS (show_ip_rpf_addr, + show_ip_rpf_addr_vrf_cmd, + "show ip rpf A.B.C.D " VRF_CMD_STR, + SHOW_STR + IP_STR + "Display RPF information for multicast source\n" + "IP multicast source address (e.g. 10.0.0.0)\n" + VRF_CMD_HELP_STR) + +DEFUN (show_ip_rpf_vrf_all, + show_ip_rpf_vrf_all_cmd, + "show ip rpf " VRF_ALL_CMD_STR, + SHOW_STR + IP_STR + "Display RPF information for multicast source\n" + VRF_ALL_CMD_HELP_STR) +{ + struct zebra_vrf *zvrf; + struct route_table *table; + struct route_node *rn; + struct rib *rib; + vrf_iter_t iter; + int first = 1; + + VTY_WARN_EXPERIMENTAL(); + + for (iter = vrf_first (); iter != VRF_ITER_INVALID; iter = vrf_next (iter)) + { + if ((zvrf = vrf_iter2info (iter)) == NULL || + (table = zvrf->table[AFI_IP][SAFI_MULTICAST]) == NULL) + continue; + + /* Show all IPv4 routes. */ + for (rn = route_top (table); rn; rn = route_next (rn)) + RNODE_FOREACH_RIB (rn, rib) + { + if (first) + { + vty_out (vty, SHOW_ROUTE_V4_HEADER); + first = 0; + } + vty_show_ip_route (vty, rn, rib); + } + } + + return CMD_SUCCESS; +} + +DEFUN (show_ip_rpf_addr_vrf_all, + show_ip_rpf_addr_vrf_all_cmd, + "show ip rpf A.B.C.D " VRF_ALL_CMD_STR, + SHOW_STR + IP_STR + "Display RPF information for multicast source\n" + "IP multicast source address (e.g. 10.0.0.0)\n" + VRF_ALL_CMD_HELP_STR) +{ + struct in_addr addr; + struct route_node *rn; + vrf_iter_t iter; + 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; + } + + for (iter = vrf_first (); iter != VRF_ITER_INVALID; iter = vrf_next (iter)) + { + if (rib_match_ipv4_multicast (addr, &rn, vrf_iter2id (iter))) + vty_show_ip_route_detail (vty, rn, 1); + } return CMD_SUCCESS; } @@ -141,7 +469,41 @@ DEFUN (ip_route, "IP gateway interface name\n" "Null interface\n") { - return zebra_static_ipv4 (vty, 1, argv[0], NULL, argv[1], NULL, NULL); + return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], NULL, argv[1], + NULL, NULL, NULL, NULL); +} + +DEFUN (ip_route_tag, + ip_route_tag_cmd, + "ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0) tag <1-4294967295>", + IP_STR + "Establish static routes\n" + "IP destination prefix (e.g. 10.0.0.0/8)\n" + "IP gateway address\n" + "IP gateway interface name\n" + "Null interface\n" + "Set tag for this route\n" + "Tag value\n") +{ + return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], NULL, + argv[1], NULL, argv[2], NULL, NULL); +} + +DEFUN (ip_route_tag_vrf, + ip_route_tag_vrf_cmd, + "ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0) tag <1-4294967295>" VRF_CMD_STR, + IP_STR + "Establish static routes\n" + "IP destination prefix (e.g. 10.0.0.0/8)\n" + "IP gateway address\n" + "IP gateway interface name\n" + "Null interface\n" + "Set tag for this route\n" + "Tag value\n" + VRF_CMD_HELP_STR) +{ + return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], NULL, + argv[1], NULL, argv[2], NULL, argv[3]); } DEFUN (ip_route_flags, @@ -155,7 +517,44 @@ DEFUN (ip_route_flags, "Emit an ICMP unreachable when matched\n" "Silently discard pkts when matched\n") { - return zebra_static_ipv4 (vty, 1, argv[0], NULL, argv[1], argv[2], NULL); + return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], NULL, argv[1], + argv[2], NULL, NULL, NULL); +} + +DEFUN (ip_route_flags_tag, + ip_route_flags_tag_cmd, + "ip route A.B.C.D/M (A.B.C.D|INTERFACE) (reject|blackhole) tag <1-4294967295>", + IP_STR + "Establish static routes\n" + "IP destination prefix (e.g. 10.0.0.0/8)\n" + "IP gateway address\n" + "IP gateway interface name\n" + "Emit an ICMP unreachable when matched\n" + "Silently discard pkts when matched\n" + "Set tag for this route\n" + "Tag value\n") + +{ + return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], NULL, argv[1], + argv[2], argv[3], NULL, NULL); +} + +DEFUN (ip_route_flags_tag_vrf, + ip_route_flags_tag_vrf_cmd, + "ip route A.B.C.D/M (A.B.C.D|INTERFACE) (reject|blackhole) tag <1-4294967295>" VRF_CMD_STR, + IP_STR + "Establish static routes\n" + "IP destination prefix (e.g. 10.0.0.0/8)\n" + "IP gateway address\n" + "IP gateway interface name\n" + "Emit an ICMP unreachable when matched\n" + "Silently discard pkts when matched\n" + "Set tag for this route\n" + "Tag value\n" + VRF_CMD_HELP_STR) +{ + return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], NULL, argv[1], + argv[2], argv[3], NULL, argv[4]); } DEFUN (ip_route_flags2, @@ -167,7 +566,39 @@ DEFUN (ip_route_flags2, "Emit an ICMP unreachable when matched\n" "Silently discard pkts when matched\n") { - return zebra_static_ipv4 (vty, 1, argv[0], NULL, NULL, argv[1], NULL); + return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], NULL, + NULL, argv[1], NULL, NULL, NULL); +} + +DEFUN (ip_route_flags2_tag, + ip_route_flags2_tag_cmd, + "ip route A.B.C.D/M (reject|blackhole) tag <1-4294967295>", + IP_STR + "Establish static routes\n" + "IP destination prefix (e.g. 10.0.0.0/8)\n" + "Emit an ICMP unreachable when matched\n" + "Silently discard pkts when matched\n" + "Set tag for this route\n" + "Tag value\n") +{ + return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], NULL, NULL, + argv[1], argv[2], NULL, NULL); +} + +DEFUN (ip_route_flags2_tag_vrf, + ip_route_flags2_tag_vrf_cmd, + "ip route A.B.C.D/M (reject|blackhole) tag <1-4294967295>", + IP_STR + "Establish static routes\n" + "IP destination prefix (e.g. 10.0.0.0/8)\n" + "Emit an ICMP unreachable when matched\n" + "Silently discard pkts when matched\n" + "Set tag for this route\n" + "Tag value\n" + VRF_CMD_HELP_STR) +{ + return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], NULL, NULL, + argv[1], argv[2], NULL, argv[3]); } /* Mask as A.B.C.D format. */ @@ -182,255 +613,316 @@ DEFUN (ip_route_mask, "IP gateway interface name\n" "Null interface\n") { - return zebra_static_ipv4 (vty, 1, argv[0], argv[1], argv[2], NULL, NULL); + return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], argv[1], + argv[2], NULL, NULL, NULL, NULL); } -DEFUN (ip_route_mask_flags, - ip_route_mask_flags_cmd, - "ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE) (reject|blackhole)", +DEFUN (ip_route_mask_tag, + ip_route_mask_tag_cmd, + "ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0) tag <1-4294967295>", IP_STR "Establish static routes\n" "IP destination prefix\n" "IP destination prefix mask\n" "IP gateway address\n" "IP gateway interface name\n" - "Emit an ICMP unreachable when matched\n" - "Silently discard pkts when matched\n") + "Null interface\n" + "Set tag for this route\n" + "Tag value\n") + { - return zebra_static_ipv4 (vty, 1, argv[0], argv[1], argv[2], argv[3], NULL); + return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], argv[1], argv[2], + NULL, argv[3], NULL, NULL); } -DEFUN (ip_route_mask_flags2, - ip_route_mask_flags2_cmd, - "ip route A.B.C.D A.B.C.D (reject|blackhole)", +DEFUN (ip_route_mask_tag_vrf, + ip_route_mask_tag_vrf_cmd, + "ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0) tag <1-4294967295>" VRF_CMD_STR, IP_STR "Establish static routes\n" "IP destination prefix\n" "IP destination prefix mask\n" - "Emit an ICMP unreachable when matched\n" - "Silently discard pkts when matched\n") -{ - return zebra_static_ipv4 (vty, 1, argv[0], argv[1], NULL, argv[2], NULL); -} - -/* Distance option value. */ -DEFUN (ip_route_distance, - ip_route_distance_cmd, - "ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0) <1-255>", - IP_STR - "Establish static routes\n" - "IP destination prefix (e.g. 10.0.0.0/8)\n" "IP gateway address\n" "IP gateway interface name\n" "Null interface\n" - "Distance value for this route\n") + "Set tag for this route\n" + "Tag value\n" + VRF_CMD_HELP_STR) { - return zebra_static_ipv4 (vty, 1, argv[0], NULL, argv[1], NULL, argv[2]); + return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], argv[1], argv[2], + NULL, argv[3], NULL, argv[4]); } -DEFUN (ip_route_flags_distance, - ip_route_flags_distance_cmd, - "ip route A.B.C.D/M (A.B.C.D|INTERFACE) (reject|blackhole) <1-255>", +DEFUN (ip_route_mask_flags, + ip_route_mask_flags_cmd, + "ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE) (reject|blackhole)", IP_STR "Establish static routes\n" - "IP destination prefix (e.g. 10.0.0.0/8)\n" + "IP destination prefix\n" + "IP destination prefix mask\n" "IP gateway address\n" "IP gateway interface name\n" "Emit an ICMP unreachable when matched\n" - "Silently discard pkts when matched\n" - "Distance value for this route\n") + "Silently discard pkts when matched\n") { - return zebra_static_ipv4 (vty, 1, argv[0], NULL, argv[1], argv[2], argv[3]); + return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], argv[1], + argv[2], argv[3], NULL, NULL, NULL); } -DEFUN (ip_route_flags_distance2, - ip_route_flags_distance2_cmd, - "ip route A.B.C.D/M (reject|blackhole) <1-255>", +DEFUN (ip_route_mask_flags_tag, + ip_route_mask_flags_tag_cmd, + "ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE) (reject|blackhole) tag <1-4294967295>", IP_STR "Establish static routes\n" - "IP destination prefix (e.g. 10.0.0.0/8)\n" + "IP destination prefix\n" + "IP destination prefix mask\n" + "IP gateway address\n" + "IP gateway interface name\n" "Emit an ICMP unreachable when matched\n" "Silently discard pkts when matched\n" - "Distance value for this route\n") + "Set tag for this route\n" + "Tag value\n") { - return zebra_static_ipv4 (vty, 1, argv[0], NULL, NULL, argv[1], argv[2]); + return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], argv[1], + argv[2], argv[3], argv[4], NULL, NULL); } -DEFUN (ip_route_mask_distance, - ip_route_mask_distance_cmd, - "ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0) <1-255>", +DEFUN (ip_route_mask_flags_tag_vrf, + ip_route_mask_flags_tag_vrf_cmd, + "ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE) (reject|blackhole) tag <1-4294967295>" VRF_CMD_STR, IP_STR "Establish static routes\n" "IP destination prefix\n" "IP destination prefix mask\n" "IP gateway address\n" "IP gateway interface name\n" - "Null interface\n" - "Distance value for this route\n") + "Emit an ICMP unreachable when matched\n" + "Silently discard pkts when matched\n" + "Set tag for this route\n" + "Tag value\n" + VRF_CMD_HELP_STR) { - return zebra_static_ipv4 (vty, 1, argv[0], argv[1], argv[2], NULL, argv[3]); + return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], argv[1], + argv[2], argv[3], argv[4], NULL, argv[5]); } -DEFUN (ip_route_mask_flags_distance, - ip_route_mask_flags_distance_cmd, - "ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE) (reject|blackhole) <1-255>", +DEFUN (ip_route_mask_flags2, + ip_route_mask_flags2_cmd, + "ip route A.B.C.D A.B.C.D (reject|blackhole)", IP_STR "Establish static routes\n" "IP destination prefix\n" "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") { - return zebra_static_ipv4 (vty, 1, argv[0], argv[1], argv[2], argv[3], argv[4]); + return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], argv[1], + NULL, argv[2], NULL, NULL, NULL); } -DEFUN (ip_route_mask_flags_distance2, - ip_route_mask_flags_distance2_cmd, - "ip route A.B.C.D A.B.C.D (reject|blackhole) <1-255>", +DEFUN (ip_route_mask_flags2_tag, + ip_route_mask_flags2_tag_cmd, + "ip route A.B.C.D A.B.C.D (reject|blackhole) tag <1-4294967295>", IP_STR "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" + "Set tag for this route\n" + "Tag value\n") { - return zebra_static_ipv4 (vty, 1, argv[0], argv[1], NULL, argv[2], argv[3]); + return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], argv[1], NULL, + argv[2], argv[3], NULL, NULL); } -DEFUN (no_ip_route, - no_ip_route_cmd, - "no ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0)", - NO_STR +DEFUN (ip_route_mask_flags2_tag_vrf, + ip_route_mask_flags2_tag_vrf_cmd, + "ip route A.B.C.D A.B.C.D (reject|blackhole) tag <1-4294967295>" VRF_CMD_STR, IP_STR "Establish static routes\n" - "IP destination prefix (e.g. 10.0.0.0/8)\n" - "IP gateway address\n" - "IP gateway interface name\n" - "Null interface\n") + "IP destination prefix\n" + "IP destination prefix mask\n" + "Emit an ICMP unreachable when matched\n" + "Silently discard pkts when matched\n" + "Set tag for this route\n" + "Tag value\n" + VRF_CMD_HELP_STR) { - return zebra_static_ipv4 (vty, 0, argv[0], NULL, argv[1], NULL, NULL); + return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], argv[1], NULL, + argv[2], argv[3], NULL, argv[4]); } -ALIAS (no_ip_route, - no_ip_route_flags_cmd, - "no ip route A.B.C.D/M (A.B.C.D|INTERFACE) (reject|blackhole)", - NO_STR +/* Distance option value. */ +DEFUN (ip_route_distance, + ip_route_distance_cmd, + "ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0) <1-255>", IP_STR "Establish static routes\n" "IP destination prefix (e.g. 10.0.0.0/8)\n" "IP gateway address\n" "IP gateway interface name\n" - "Emit an ICMP unreachable when matched\n" - "Silently discard pkts when matched\n") + "Null interface\n" + "Distance value for this route\n") +{ + return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], NULL, + argv[1], NULL, NULL, argv[2], NULL); +} -DEFUN (no_ip_route_flags2, - no_ip_route_flags2_cmd, - "no ip route A.B.C.D/M (reject|blackhole)", - NO_STR +DEFUN (ip_route_tag_distance, + ip_route_tag_distance_cmd, + "ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0) tag <1-4294967295> <1-255>", IP_STR "Establish static routes\n" "IP destination prefix (e.g. 10.0.0.0/8)\n" - "Emit an ICMP unreachable when matched\n" - "Silently discard pkts when matched\n") + "IP gateway address\n" + "IP gateway interface name\n" + "Null interface\n" + "Set tag for this route\n" + "Tag value\n" + "Distance value for this route\n") { - return zebra_static_ipv4 (vty, 0, argv[0], NULL, NULL, NULL, NULL); + return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], NULL, + argv[1], NULL, argv[2], argv[3], NULL); } -DEFUN (no_ip_route_mask, - no_ip_route_mask_cmd, - "no ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0)", - NO_STR +DEFUN (ip_route_tag_distance_vrf, + ip_route_tag_distance_vrf_cmd, + "ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0) tag <1-4294967295> <1-255>" VRF_CMD_STR, IP_STR "Establish static routes\n" - "IP destination prefix\n" - "IP destination prefix mask\n" + "IP destination prefix (e.g. 10.0.0.0/8)\n" "IP gateway address\n" "IP gateway interface name\n" - "Null interface\n") + "Null interface\n" + "Set tag for this route\n" + "Tag value\n" + "Distance value for this route\n" + VRF_CMD_HELP_STR) { - return zebra_static_ipv4 (vty, 0, argv[0], argv[1], argv[2], NULL, NULL); + return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], NULL, + argv[1], NULL, argv[2], argv[3], argv[4]); } -ALIAS (no_ip_route_mask, - no_ip_route_mask_flags_cmd, - "no ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE) (reject|blackhole)", - NO_STR +DEFUN (ip_route_flags_distance, + ip_route_flags_distance_cmd, + "ip route A.B.C.D/M (A.B.C.D|INTERFACE) (reject|blackhole) <1-255>", IP_STR "Establish static routes\n" - "IP destination prefix\n" - "IP destination prefix mask\n" + "IP destination prefix (e.g. 10.0.0.0/8)\n" "IP gateway address\n" "IP gateway interface name\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_safi (vty, SAFI_UNICAST, 1, argv[0], NULL, argv[1], + argv[2], NULL, argv[3], NULL); +} -DEFUN (no_ip_route_mask_flags2, - no_ip_route_mask_flags2_cmd, - "no ip route A.B.C.D A.B.C.D (reject|blackhole)", - NO_STR +DEFUN (ip_route_flags_tag_distance, + ip_route_flags_tag_distance_cmd, + "ip route A.B.C.D/M (A.B.C.D|INTERFACE) (reject|blackhole) tag <1-4294967295> <1-255>", IP_STR "Establish static routes\n" - "IP destination prefix\n" - "IP destination prefix mask\n" + "IP destination prefix (e.g. 10.0.0.0/8)\n" + "IP gateway address\n" + "IP gateway interface name\n" "Emit an ICMP unreachable when matched\n" - "Silently discard pkts when matched\n") + "Silently discard pkts when matched\n" + "Set tag for this route\n" + "Tag value\n" + "Distance value for this route\n") { - return zebra_static_ipv4 (vty, 0, argv[0], argv[1], NULL, NULL, NULL); + return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], NULL, argv[1], + argv[2], argv[3], argv[4], NULL); } -DEFUN (no_ip_route_distance, - no_ip_route_distance_cmd, - "no ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0) <1-255>", - NO_STR +DEFUN (ip_route_flags_tag_distance_vrf, + ip_route_flags_tag_distance_vrf_cmd, + "ip route A.B.C.D/M (A.B.C.D|INTERFACE) (reject|blackhole) tag <1-4294967295> <1-255>" VRF_CMD_STR, IP_STR "Establish static routes\n" "IP destination prefix (e.g. 10.0.0.0/8)\n" "IP gateway address\n" "IP gateway interface name\n" - "Null interface\n" + "Emit an ICMP unreachable when matched\n" + "Silently discard pkts when matched\n" + "Set tag for this route\n" + "Tag value\n" + "Distance value for this route\n" + VRF_CMD_HELP_STR) +{ + return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], NULL, argv[1], + argv[2], argv[3], argv[4], argv[5]); +} + +DEFUN (ip_route_flags_distance2, + ip_route_flags_distance2_cmd, + "ip route A.B.C.D/M (reject|blackhole) <1-255>", + IP_STR + "Establish static routes\n" + "IP destination prefix (e.g. 10.0.0.0/8)\n" + "Emit an ICMP unreachable when matched\n" + "Silently discard pkts when matched\n" "Distance value for this route\n") { - return zebra_static_ipv4 (vty, 0, argv[0], NULL, argv[1], NULL, argv[2]); + return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], NULL, + NULL, argv[1], NULL, argv[2], NULL); } -DEFUN (no_ip_route_flags_distance, - no_ip_route_flags_distance_cmd, - "no ip route A.B.C.D/M (A.B.C.D|INTERFACE) (reject|blackhole) <1-255>", - NO_STR +DEFUN (ip_route_flags_tag_distance2, + ip_route_flags_tag_distance2_cmd, + "ip route A.B.C.D/M (reject|blackhole) tag <1-4294967295> <1-255>", IP_STR "Establish static routes\n" "IP destination prefix (e.g. 10.0.0.0/8)\n" - "IP gateway address\n" - "IP gateway interface name\n" "Emit an ICMP unreachable when matched\n" "Silently discard pkts when matched\n" + "Set tag for this route\n" + "Tag value\n" "Distance value for this route\n") { - return zebra_static_ipv4 (vty, 0, argv[0], NULL, argv[1], argv[2], argv[3]); + return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], NULL, + NULL, argv[1], argv[2], argv[3], NULL); } -DEFUN (no_ip_route_flags_distance2, - no_ip_route_flags_distance2_cmd, - "no ip route A.B.C.D/M (reject|blackhole) <1-255>", - NO_STR +DEFUN (ip_route_flags_tag_distance2_vrf, + ip_route_flags_tag_distance2_vrf_cmd, + "ip route A.B.C.D/M (reject|blackhole) tag <1-4294967295> <1-255>" VRF_CMD_STR, IP_STR "Establish static routes\n" "IP destination prefix (e.g. 10.0.0.0/8)\n" "Emit an ICMP unreachable when matched\n" "Silently discard pkts when matched\n" + "Set tag for this route\n" + "Tag value\n" + "Distance value for this route\n" + VRF_CMD_HELP_STR) +{ + return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], NULL, + NULL, argv[1], argv[2], argv[3], argv[4]); +} + +DEFUN (ip_route_mask_distance, + ip_route_mask_distance_cmd, + "ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0) <1-255>", + IP_STR + "Establish static routes\n" + "IP destination prefix\n" + "IP destination prefix mask\n" + "IP gateway address\n" + "IP gateway interface name\n" + "Null interface\n" "Distance value for this route\n") { - return zebra_static_ipv4 (vty, 0, argv[0], NULL, NULL, argv[1], argv[2]); + return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], argv[1], argv[2], + NULL, NULL, argv[3], NULL); } -DEFUN (no_ip_route_mask_distance, - no_ip_route_mask_distance_cmd, - "no ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0) <1-255>", - NO_STR +DEFUN (ip_route_mask_tag_distance, + ip_route_mask_tag_distance_cmd, + "ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0) tag <1-4294967295> <1-255>", IP_STR "Establish static routes\n" "IP destination prefix\n" @@ -438,921 +930,3348 @@ DEFUN (no_ip_route_mask_distance, "IP gateway address\n" "IP gateway interface name\n" "Null interface\n" + "Set tag for this route\n" + "Tag value\n" "Distance value for this route\n") { - return zebra_static_ipv4 (vty, 0, argv[0], argv[1], argv[2], NULL, argv[3]); + return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], argv[1], + argv[2], NULL, argv[3], argv[4], NULL); } -DEFUN (no_ip_route_mask_flags_distance, - no_ip_route_mask_flags_distance_cmd, - "no ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE) (reject|blackhole) <1-255>", - NO_STR +DEFUN (ip_route_mask_tag_distance_vrf, + ip_route_mask_tag_distance_vrf_cmd, + "ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0) tag <1-4294967295> <1-255>" VRF_CMD_STR, + IP_STR + "Establish static routes\n" + "IP destination prefix\n" + "IP destination prefix mask\n" + "IP gateway address\n" + "IP gateway interface name\n" + "Null interface\n" + "Set tag for this route\n" + "Tag value\n" + "Distance value for this route\n" + VRF_CMD_HELP_STR) +{ + return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], argv[1], + argv[2], NULL, argv[3], argv[4], argv[5]); +} + + +DEFUN (ip_route_mask_flags_tag_distance, + ip_route_mask_flags_tag_distance_cmd, + "ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE) (reject|blackhole) tag <1-4294967295> <1-255>", + IP_STR + "Establish static routes\n" + "IP destination prefix\n" + "IP destination prefix mask\n" + "IP gateway address\n" + "IP gateway interface name\n" + "Set tag for this route\n" + "Tag value\n" + "Distance value for this route\n" + "Emit an ICMP unreachable when matched\n" + "Silently discard pkts when matched\n") +{ + return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], argv[1], argv[2], + argv[3], argv[4], argv[5], NULL); +} + +DEFUN (ip_route_mask_flags_tag_distance_vrf, + ip_route_mask_flags_tag_distance_vrf_cmd, + "ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE) (reject|blackhole) tag <1-4294967295> <1-255>" VRF_CMD_STR, IP_STR "Establish static routes\n" "IP destination prefix\n" "IP destination prefix mask\n" "IP gateway address\n" "IP gateway interface name\n" + "Set tag for this route\n" + "Tag value\n" + "Distance value for this route\n" "Emit an ICMP unreachable when matched\n" "Silently discard pkts when matched\n" - "Distance value for this route\n") + VRF_CMD_HELP_STR) { - return zebra_static_ipv4 (vty, 0, argv[0], argv[1], argv[2], argv[3], argv[4]); + return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], argv[1], argv[2], + argv[3], argv[4], argv[5], argv[6]); } -DEFUN (no_ip_route_mask_flags_distance2, - no_ip_route_mask_flags_distance2_cmd, - "no ip route A.B.C.D A.B.C.D (reject|blackhole) <1-255>", - NO_STR +DEFUN (ip_route_mask_flags_distance, + ip_route_mask_flags_distance_cmd, + "ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE) (reject|blackhole) <1-255>", IP_STR "Establish static routes\n" "IP destination prefix\n" "IP destination prefix mask\n" + "IP gateway address\n" + "IP gateway interface name\n" "Emit an ICMP unreachable when matched\n" "Silently discard pkts when matched\n" "Distance value for this route\n") { - return zebra_static_ipv4 (vty, 0, argv[0], argv[1], NULL, argv[2], argv[3]); + return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], argv[1], argv[2], + argv[3], NULL, argv[4], NULL); } -char *proto_rm[AFI_MAX][ZEBRA_ROUTE_MAX+1]; /* "any" == ZEBRA_ROUTE_MAX */ +DEFUN (ip_route_mask_flags_distance2, + ip_route_mask_flags_distance2_cmd, + "ip route A.B.C.D A.B.C.D (reject|blackhole) <1-255>", + IP_STR + "Establish static routes\n" + "IP destination prefix\n" + "IP destination prefix mask\n" + "Emit an ICMP unreachable when matched\n" + "Silently discard pkts when matched\n" + "Distance value for this route\n") +{ + return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], argv[1], + NULL, argv[2], NULL, argv[3], NULL); +} -DEFUN (ip_protocol, - ip_protocol_cmd, - "ip protocol PROTO route-map ROUTE-MAP", - NO_STR - "Apply route map to PROTO\n" - "Protocol name\n" - "Route map name\n") +DEFUN (ip_route_mask_flags_tag_distance2, + ip_route_mask_flags_tag_distance2_cmd, + "ip route A.B.C.D A.B.C.D (reject|blackhole) tag <1-4294967295> <1-255>", + IP_STR + "Establish static routes\n" + "IP destination prefix\n" + "IP destination prefix mask\n" + "Set tag for this route\n" + "Tag value\n" + "Distance value for this route\n" + "Emit an ICMP unreachable when matched\n" + "Silently discard pkts when matched\n") { - int i; + return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], argv[1], NULL, + argv[2], argv[3], argv[4], NULL); +} - if (strcasecmp(argv[0], "any") == 0) - i = ZEBRA_ROUTE_MAX; - else - i = proto_name2num(argv[0]); - if (i < 0) - { - vty_out (vty, "invalid protocol name \"%s\"%s", argv[0] ? argv[0] : "", - VTY_NEWLINE); - return CMD_WARNING; - } - if (proto_rm[AFI_IP][i]) - XFREE (MTYPE_ROUTE_MAP_NAME, proto_rm[AFI_IP][i]); - proto_rm[AFI_IP][i] = XSTRDUP (MTYPE_ROUTE_MAP_NAME, argv[1]); - return CMD_SUCCESS; +DEFUN (ip_route_mask_flags_tag_distance2_vrf, + ip_route_mask_flags_tag_distance2_vrf_cmd, + "ip route A.B.C.D A.B.C.D (reject|blackhole) tag <1-4294967295> <1-255>" VRF_CMD_STR, + IP_STR + "Establish static routes\n" + "IP destination prefix\n" + "IP destination prefix mask\n" + "Set tag for this route\n" + "Tag value\n" + "Distance value for this route\n" + "Emit an ICMP unreachable when matched\n" + "Silently discard pkts when matched\n" + VRF_CMD_HELP_STR) +{ + return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], argv[1], NULL, + argv[2], argv[3], argv[4], argv[5]); } -DEFUN (no_ip_protocol, - no_ip_protocol_cmd, - "no ip protocol PROTO", +DEFUN (no_ip_route, + no_ip_route_cmd, + "no ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0)", NO_STR - "Remove route map from PROTO\n" - "Protocol name\n") + IP_STR + "Establish static routes\n" + "IP destination prefix (e.g. 10.0.0.0/8)\n" + "IP gateway address\n" + "IP gateway interface name\n" + "Null interface\n") { - int i; + return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 0, argv[0], NULL, + argv[1], NULL, NULL, NULL, NULL); +} - if (strcasecmp(argv[0], "any") == 0) - i = ZEBRA_ROUTE_MAX; - else - i = proto_name2num(argv[0]); - if (i < 0) - { - vty_out (vty, "invalid protocol name \"%s\"%s", argv[0] ? argv[0] : "", - VTY_NEWLINE); - return CMD_WARNING; - } - if (proto_rm[AFI_IP][i]) - XFREE (MTYPE_ROUTE_MAP_NAME, proto_rm[AFI_IP][i]); - proto_rm[AFI_IP][i] = NULL; - return CMD_SUCCESS; +DEFUN (no_ip_route_tag, + no_ip_route_tag_cmd, + "no ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0) tag <1-4294967295>", + NO_STR + IP_STR + "Establish static routes\n" + "IP destination prefix (e.g. 10.0.0.0/8)\n" + "IP gateway address\n" + "IP gateway interface name\n" + "Null interface\n" + "Tag of this route\n" + "Tag value\n") +{ + return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 0, argv[0], NULL, argv[1], + NULL, argv[2], NULL, NULL); } -/* New RIB. Detailed information for IPv4 route. */ -static void -vty_show_ip_route_detail (struct vty *vty, struct route_node *rn) +DEFUN (no_ip_route_tag_vrf, + no_ip_route_tag_vrf_cmd, + "no ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0) tag <1-4294967295>" VRF_CMD_STR, + NO_STR + IP_STR + "Establish static routes\n" + "IP destination prefix (e.g. 10.0.0.0/8)\n" + "IP gateway address\n" + "IP gateway interface name\n" + "Null interface\n" + "Tag of this route\n" + "Tag value\n" + VRF_CMD_HELP_STR) { - struct rib *rib; - struct nexthop *nexthop; + return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 0, argv[0], NULL, argv[1], + NULL, argv[2], NULL, argv[3]); +} - for (rib = rn->info; rib; rib = rib->next) - { - vty_out (vty, "Routing entry for %s/%d%s", - inet_ntoa (rn->p.u.prefix4), rn->p.prefixlen, - VTY_NEWLINE); - vty_out (vty, " Known via \"%s\"", zebra_route_string (rib->type)); - vty_out (vty, ", distance %d, metric %d", rib->distance, rib->metric); - if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED)) - vty_out (vty, ", best"); +ALIAS (no_ip_route, + no_ip_route_flags_cmd, + "no ip route A.B.C.D/M (A.B.C.D|INTERFACE) (reject|blackhole)", + NO_STR + IP_STR + "Establish static routes\n" + "IP destination prefix (e.g. 10.0.0.0/8)\n" + "IP gateway address\n" + "IP gateway interface name\n" + "Emit an ICMP unreachable when matched\n" + "Silently discard pkts when matched\n") + +ALIAS (no_ip_route_tag, + no_ip_route_flags_tag_cmd, + "no ip route A.B.C.D/M (A.B.C.D|INTERFACE) (reject|blackhole) tag <1-4294967295>", + NO_STR + IP_STR + "Establish static routes\n" + "IP destination prefix (e.g. 10.0.0.0/8)\n" + "IP gateway address\n" + "IP gateway interface name\n" + "Emit an ICMP unreachable when matched\n" + "Silently discard pkts when matched\n" + "Tag of this route\n" + "Tag value\n") + +DEFUN (no_ip_route_flags2, + no_ip_route_flags2_cmd, + "no ip route A.B.C.D/M (reject|blackhole)", + NO_STR + IP_STR + "Establish static routes\n" + "IP destination prefix (e.g. 10.0.0.0/8)\n" + "Emit an ICMP unreachable when matched\n" + "Silently discard pkts when matched\n") +{ + return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 0, argv[0], NULL, + NULL, NULL, NULL, NULL, NULL); +} + +DEFUN (no_ip_route_flags2_tag, + no_ip_route_flags2_tag_cmd, + "no ip route A.B.C.D/M (reject|blackhole) tag <1-4294967295>", + NO_STR + IP_STR + "Establish static routes\n" + "IP destination prefix (e.g. 10.0.0.0/8)\n" + "Emit an ICMP unreachable when matched\n" + "Silently discard pkts when matched\n" + "Tag of this route\n" + "Tag value\n") +{ + return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 0, argv[0], NULL, NULL, + NULL, argv[1], NULL, NULL); +} + +DEFUN (no_ip_route_flags2_tag_vrf, + no_ip_route_flags2_tag_vrf_cmd, + "no ip route A.B.C.D/M (reject|blackhole) tag <1-4294967295>" VRF_CMD_STR, + NO_STR + IP_STR + "Establish static routes\n" + "IP destination prefix (e.g. 10.0.0.0/8)\n" + "Emit an ICMP unreachable when matched\n" + "Silently discard pkts when matched\n" + "Tag of this route\n" + "Tag value\n" + VRF_CMD_HELP_STR) +{ + return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 0, argv[0], NULL, NULL, + NULL, argv[1], NULL, argv[2]); +} + +DEFUN (no_ip_route_mask, + no_ip_route_mask_cmd, + "no ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0)", + NO_STR + IP_STR + "Establish static routes\n" + "IP destination prefix\n" + "IP destination prefix mask\n" + "IP gateway address\n" + "IP gateway interface name\n" + "Null interface\n") +{ + return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 0, argv[0], argv[1], + argv[2], NULL, NULL, NULL, NULL); +} + +DEFUN (no_ip_route_mask_tag, + no_ip_route_mask_tag_cmd, + "no ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0) tag <1-4294967295>", + NO_STR + IP_STR + "Establish static routes\n" + "IP destination prefix\n" + "IP destination prefix mask\n" + "IP gateway address\n" + "IP gateway interface name\n" + "Null interface\n" + "Tag of this route\n" + "Tag value\n") +{ + return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 0, argv[0], argv[1], argv[2], + NULL, argv[3], NULL, NULL); +} + +ALIAS (no_ip_route_mask, + no_ip_route_mask_flags_cmd, + "no ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE) (reject|blackhole)", + NO_STR + IP_STR + "Establish static routes\n" + "IP destination prefix\n" + "IP destination prefix mask\n" + "IP gateway address\n" + "IP gateway interface name\n" + "Emit an ICMP unreachable when matched\n" + "Silently discard pkts when matched\n") + +ALIAS (no_ip_route_mask_tag, + no_ip_route_mask_flags_tag_cmd, + "no ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE) (reject|blackhole) tag <1-4294967295>", + NO_STR + IP_STR + "Establish static routes\n" + "IP destination prefix\n" + "IP destination prefix mask\n" + "IP gateway address\n" + "IP gateway interface name\n" + "Emit an ICMP unreachable when matched\n" + "Silently discard pkts when matched\n" + "Tag of this route\n" + "Tag value\n") + +DEFUN (no_ip_route_mask_flags2, + no_ip_route_mask_flags2_cmd, + "no ip route A.B.C.D A.B.C.D (reject|blackhole)", + NO_STR + IP_STR + "Establish static routes\n" + "IP destination prefix\n" + "IP destination prefix mask\n" + "Emit an ICMP unreachable when matched\n" + "Silently discard pkts when matched\n") +{ + return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 0, argv[0], argv[1], + NULL, NULL, NULL, NULL, NULL); +} + +DEFUN (no_ip_route_mask_flags2_tag, + no_ip_route_mask_flags2_tag_cmd, + "no ip route A.B.C.D A.B.C.D (reject|blackhole) tag <1-4294967295>", + NO_STR + IP_STR + "Establish static routes\n" + "IP destination prefix\n" + "IP destination prefix mask\n" + "Emit an ICMP unreachable when matched\n" + "Silently discard pkts when matched\n" + "Tag of this route\n" + "Tag value\n") +{ + return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 0, argv[0], argv[1], + NULL, NULL, argv[2], NULL, NULL); +} + +DEFUN (no_ip_route_mask_flags2_tag_vrf, + no_ip_route_mask_flags2_tag_vrf_cmd, + "no ip route A.B.C.D A.B.C.D (reject|blackhole) tag <1-4294967295>" VRF_CMD_STR, + NO_STR + IP_STR + "Establish static routes\n" + "IP destination prefix\n" + "IP destination prefix mask\n" + "Emit an ICMP unreachable when matched\n" + "Silently discard pkts when matched\n" + "Tag of this route\n" + "Tag value\n" + VRF_CMD_HELP_STR) +{ + return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 0, argv[0], argv[1], + NULL, NULL, argv[2], NULL, argv[3]); +} + +DEFUN (no_ip_route_distance, + no_ip_route_distance_cmd, + "no ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0) <1-255>", + NO_STR + IP_STR + "Establish static routes\n" + "IP destination prefix (e.g. 10.0.0.0/8)\n" + "IP gateway address\n" + "IP gateway interface name\n" + "Null interface\n" + "Distance value for this route\n") +{ + return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 0, argv[0], NULL, + argv[1], NULL, NULL, argv[2], NULL); +} + +DEFUN (no_ip_route_tag_distance, + no_ip_route_tag_distance_cmd, + "no ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0) tag <1-4294967295> <1-255>", + NO_STR + IP_STR + "Establish static routes\n" + "IP destination prefix (e.g. 10.0.0.0/8)\n" + "IP gateway address\n" + "IP gateway interface name\n" + "Null interface\n" + "Tag of this route\n" + "Tag value\n" + "Distance value for this route\n") +{ + return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 0, argv[0], NULL, argv[1], + NULL, argv[2], argv[3], NULL); +} + +DEFUN (no_ip_route_tag_distance_vrf, + no_ip_route_tag_distance_vrf_cmd, + "no ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0) tag <1-4294967295> <1-255>" VRF_CMD_STR, + NO_STR + IP_STR + "Establish static routes\n" + "IP destination prefix (e.g. 10.0.0.0/8)\n" + "IP gateway address\n" + "IP gateway interface name\n" + "Null interface\n" + "Tag of this route\n" + "Tag value\n" + "Distance value for this route\n" + VRF_CMD_HELP_STR) +{ + return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 0, argv[0], NULL, argv[1], + NULL, argv[2], argv[3], argv[4]); +} + +DEFUN (no_ip_route_flags_distance, + no_ip_route_flags_distance_cmd, + "no ip route A.B.C.D/M (A.B.C.D|INTERFACE) (reject|blackhole) <1-255>", + NO_STR + IP_STR + "Establish static routes\n" + "IP destination prefix (e.g. 10.0.0.0/8)\n" + "IP gateway address\n" + "IP gateway interface name\n" + "Emit an ICMP unreachable when matched\n" + "Silently discard pkts when matched\n" + "Distance value for this route\n") +{ + return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 0, argv[0], NULL, + argv[1], argv[2], NULL, argv[3], NULL); +} + +DEFUN (no_ip_route_flags_tag_distance, + no_ip_route_flags_tag_distance_cmd, + "no ip route A.B.C.D/M (A.B.C.D|INTERFACE) (reject|blackhole) tag <1-4294967295> <1-255>", + NO_STR + IP_STR + "Establish static routes\n" + "IP destination prefix (e.g. 10.0.0.0/8)\n" + "IP gateway address\n" + "IP gateway interface name\n" + "Emit an ICMP unreachable when matched\n" + "Silently discard pkts when matched\n" + "Tag of this route\n" + "Tag value\n" + "Distance value for this route\n") +{ + return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 0, argv[0], NULL, argv[1], + argv[2], argv[3], argv[4], NULL); +} + +DEFUN (no_ip_route_flags_tag_distance_vrf, + no_ip_route_flags_tag_distance_vrf_cmd, + "no ip route A.B.C.D/M (A.B.C.D|INTERFACE) (reject|blackhole) tag <1-4294967295> <1-255>" VRF_CMD_STR, + NO_STR + IP_STR + "Establish static routes\n" + "IP destination prefix (e.g. 10.0.0.0/8)\n" + "IP gateway address\n" + "IP gateway interface name\n" + "Emit an ICMP unreachable when matched\n" + "Silently discard pkts when matched\n" + "Tag of this route\n" + "Tag value\n" + "Distance value for this route\n" + VRF_CMD_HELP_STR) +{ + return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 0, argv[0], NULL, argv[1], + argv[2], argv[3], argv[4], argv[5]); +} + +DEFUN (no_ip_route_flags_distance2, + no_ip_route_flags_distance2_cmd, + "no ip route A.B.C.D/M (reject|blackhole) <1-255>", + NO_STR + IP_STR + "Establish static routes\n" + "IP destination prefix (e.g. 10.0.0.0/8)\n" + "Emit an ICMP unreachable when matched\n" + "Silently discard pkts when matched\n" + "Distance value for this route\n") +{ + return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 0, argv[0], NULL, NULL, + argv[1], NULL, argv[2], NULL); +} + +DEFUN (no_ip_route_flags_tag_distance2, + no_ip_route_flags_tag_distance2_cmd, + "no ip route A.B.C.D/M (reject|blackhole) tag <1-4294967295> <1-255>", + NO_STR + IP_STR + "Establish static routes\n" + "IP destination prefix (e.g. 10.0.0.0/8)\n" + "Emit an ICMP unreachable when matched\n" + "Silently discard pkts when matched\n" + "Tag of this route\n" + "Tag value\n" + "Distance value for this route\n") +{ + return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 0, argv[0], NULL, NULL, + argv[1], argv[2] , argv[3], NULL); +} + +DEFUN (no_ip_route_flags_tag_distance2_vrf, + no_ip_route_flags_tag_distance2_vrf_cmd, + "no ip route A.B.C.D/M (reject|blackhole) tag <1-4294967295> <1-255>" VRF_CMD_STR, + NO_STR + IP_STR + "Establish static routes\n" + "IP destination prefix (e.g. 10.0.0.0/8)\n" + "Emit an ICMP unreachable when matched\n" + "Silently discard pkts when matched\n" + "Tag of this route\n" + "Tag value\n" + "Distance value for this route\n" + VRF_CMD_HELP_STR) +{ + return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 0, argv[0], NULL, NULL, + argv[1], argv[2] , argv[3], argv[4]); +} + +DEFUN (no_ip_route_mask_distance, + no_ip_route_mask_distance_cmd, + "no ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0) <1-255>", + NO_STR + IP_STR + "Establish static routes\n" + "IP destination prefix\n" + "IP destination prefix mask\n" + "IP gateway address\n" + "IP gateway interface name\n" + "Null interface\n" + "Distance value for this route\n") +{ + return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 0, argv[0], argv[1], + argv[2], NULL, NULL, argv[3], NULL); +} + +DEFUN (no_ip_route_mask_tag_distance, + no_ip_route_mask_tag_distance_cmd, + "no ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0) tag <1-4294967295> <1-255>", + NO_STR + IP_STR + "Establish static routes\n" + "IP destination prefix\n" + "IP destination prefix mask\n" + "IP gateway address\n" + "IP gateway interface name\n" + "Null interface\n" + "Tag of this route\n" + "Tag value\n" + "Distance value for this route\n") +{ + return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 0, argv[0], argv[1], + argv[2], NULL, argv[3], argv[4], NULL); +} + +DEFUN (no_ip_route_mask_tag_distance_vrf, + no_ip_route_mask_tag_distance_vrf_cmd, + "no ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0) tag <1-4294967295> <1-255>" VRF_CMD_STR, + NO_STR + IP_STR + "Establish static routes\n" + "IP destination prefix\n" + "IP destination prefix mask\n" + "IP gateway address\n" + "IP gateway interface name\n" + "Null interface\n" + "Tag of this route\n" + "Tag value\n" + "Distance value for this route\n" + VRF_CMD_HELP_STR) +{ + return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 0, argv[0], argv[1], + argv[2], NULL, argv[3], argv[4], argv[5]); +} + +DEFUN (no_ip_route_mask_flags_distance, + no_ip_route_mask_flags_distance_cmd, + "no ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE) (reject|blackhole) <1-255>", + NO_STR + IP_STR + "Establish static routes\n" + "IP destination prefix\n" + "IP destination prefix mask\n" + "IP gateway address\n" + "IP gateway interface name\n" + "Emit an ICMP unreachable when matched\n" + "Silently discard pkts when matched\n" + "Distance value for this route\n") +{ + return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 0, argv[0], argv[1], + argv[2], argv[3], NULL, argv[4], NULL); +} + +DEFUN (no_ip_route_mask_flags_tag_distance, + no_ip_route_mask_flags_tag_distance_cmd, + "no ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE) (reject|blackhole) tag <1-4294967295> <1-255>", + NO_STR + IP_STR + "Establish static routes\n" + "IP destination prefix\n" + "IP destination prefix mask\n" + "IP gateway address\n" + "IP gateway interface name\n" + "Emit an ICMP unreachable when matched\n" + "Silently discard pkts when matched\n" + "Tag of this route\n" + "Tag value\n" + "Distance value for this route\n") +{ + return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 0, argv[0], argv[1], argv[2], argv[3], + argv[4], argv[5], NULL); +} + +DEFUN (no_ip_route_mask_flags_tag_distance_vrf, + no_ip_route_mask_flags_tag_distance_vrf_cmd, + "no ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE) (reject|blackhole) tag <1-4294967295> <1-255>" VRF_CMD_STR, + NO_STR + IP_STR + "Establish static routes\n" + "IP destination prefix\n" + "IP destination prefix mask\n" + "IP gateway address\n" + "IP gateway interface name\n" + "Emit an ICMP unreachable when matched\n" + "Silently discard pkts when matched\n" + "Tag of this route\n" + "Tag value\n" + "Distance value for this route\n" + VRF_CMD_HELP_STR) +{ + return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 0, argv[0], argv[1], argv[2], argv[3], + argv[4], argv[5], argv[6]); +} + +DEFUN (no_ip_route_mask_flags_distance2, + no_ip_route_mask_flags_distance2_cmd, + "no ip route A.B.C.D A.B.C.D (reject|blackhole) <1-255>", + NO_STR + IP_STR + "Establish static routes\n" + "IP destination prefix\n" + "IP destination prefix mask\n" + "Emit an ICMP unreachable when matched\n" + "Silently discard pkts when matched\n" + "Distance value for this route\n") +{ + return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 0, argv[0], argv[1], + NULL, argv[2], NULL, argv[3], NULL); +} + +DEFUN (ip_route_vrf, + ip_route_vrf_cmd, + "ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0) " VRF_CMD_STR, + IP_STR + "Establish static routes\n" + "IP destination prefix (e.g. 10.0.0.0/8)\n" + "IP gateway address\n" + "IP gateway interface name\n" + "Null interface\n" + VRF_CMD_HELP_STR) +{ + return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], NULL, + argv[1], NULL, NULL, NULL, argv[2]); +} + +DEFUN (ip_route_flags_vrf, + ip_route_flags_vrf_cmd, + "ip route A.B.C.D/M (A.B.C.D|INTERFACE) (reject|blackhole) " VRF_CMD_STR, + IP_STR + "Establish static routes\n" + "IP destination prefix (e.g. 10.0.0.0/8)\n" + "IP gateway address\n" + "IP gateway interface name\n" + "Emit an ICMP unreachable when matched\n" + "Silently discard pkts when matched\n" + VRF_CMD_HELP_STR) +{ + return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], NULL, + argv[1], argv[2], NULL, NULL, argv[3]); +} + +DEFUN (ip_route_flags2_vrf, + ip_route_flags2_vrf_cmd, + "ip route A.B.C.D/M (reject|blackhole) " VRF_CMD_STR, + IP_STR + "Establish static routes\n" + "IP destination prefix (e.g. 10.0.0.0/8)\n" + "Emit an ICMP unreachable when matched\n" + "Silently discard pkts when matched\n" + VRF_CMD_HELP_STR) +{ + return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], NULL, + NULL, argv[1], NULL, NULL, argv[2]); +} + +/* Mask as A.B.C.D format. */ +DEFUN (ip_route_mask_vrf, + ip_route_mask_vrf_cmd, + "ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0) " VRF_CMD_STR, + IP_STR + "Establish static routes\n" + "IP destination prefix\n" + "IP destination prefix mask\n" + "IP gateway address\n" + "IP gateway interface name\n" + "Null interface\n" + VRF_CMD_HELP_STR) +{ + return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], argv[1], + argv[2], NULL, NULL, NULL, argv[3]); +} + +DEFUN (ip_route_mask_flags_vrf, + ip_route_mask_flags_vrf_cmd, + "ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE) (reject|blackhole) " VRF_CMD_STR, + IP_STR + "Establish static routes\n" + "IP destination prefix\n" + "IP destination prefix mask\n" + "IP gateway address\n" + "IP gateway interface name\n" + "Emit an ICMP unreachable when matched\n" + "Silently discard pkts when matched\n" + VRF_CMD_HELP_STR) +{ + return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], argv[1], + argv[2], argv[3], NULL, NULL, argv[4]); +} + +DEFUN (ip_route_mask_flags2_vrf, + ip_route_mask_flags2_vrf_cmd, + "ip route A.B.C.D A.B.C.D (reject|blackhole) " VRF_CMD_STR, + IP_STR + "Establish static routes\n" + "IP destination prefix\n" + "IP destination prefix mask\n" + "Emit an ICMP unreachable when matched\n" + "Silently discard pkts when matched\n" + VRF_CMD_HELP_STR) +{ + return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], argv[1], + NULL, argv[2], NULL, NULL, argv[3]); +} + +/* Distance option value. */ +DEFUN (ip_route_distance_vrf, + ip_route_distance_vrf_cmd, + "ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0) <1-255> " VRF_CMD_STR, + IP_STR + "Establish static routes\n" + "IP destination prefix (e.g. 10.0.0.0/8)\n" + "IP gateway address\n" + "IP gateway interface name\n" + "Null interface\n" + "Distance value for this route\n" + VRF_CMD_HELP_STR) +{ + return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], NULL, + argv[1], NULL, NULL, argv[2], argv[3]); +} + +DEFUN (ip_route_flags_distance_vrf, + ip_route_flags_distance_vrf_cmd, + "ip route A.B.C.D/M (A.B.C.D|INTERFACE) (reject|blackhole) <1-255> " VRF_CMD_STR, + IP_STR + "Establish static routes\n" + "IP destination prefix (e.g. 10.0.0.0/8)\n" + "IP gateway address\n" + "IP gateway interface name\n" + "Emit an ICMP unreachable when matched\n" + "Silently discard pkts when matched\n" + "Distance value for this route\n" + VRF_CMD_HELP_STR) +{ + return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], NULL, + argv[1], argv[2], NULL, argv[3], argv[4]); +} + +DEFUN (ip_route_flags_distance2_vrf, + ip_route_flags_distance2_vrf_cmd, + "ip route A.B.C.D/M (reject|blackhole) <1-255> " VRF_CMD_STR, + IP_STR + "Establish static routes\n" + "IP destination prefix (e.g. 10.0.0.0/8)\n" + "Emit an ICMP unreachable when matched\n" + "Silently discard pkts when matched\n" + "Distance value for this route\n" + VRF_CMD_HELP_STR) +{ + return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], NULL, + NULL, argv[1], NULL, argv[2], argv[3]); +} + +DEFUN (ip_route_mask_distance_vrf, + ip_route_mask_distance_vrf_cmd, + "ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0) <1-255> " VRF_CMD_STR, + IP_STR + "Establish static routes\n" + "IP destination prefix\n" + "IP destination prefix mask\n" + "IP gateway address\n" + "IP gateway interface name\n" + "Null interface\n" + "Distance value for this route\n" + VRF_CMD_HELP_STR) +{ + return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], argv[1], + argv[2], NULL, NULL, argv[3], argv[4]); +} + +DEFUN (ip_route_mask_flags_distance_vrf, + ip_route_mask_flags_distance_vrf_cmd, + "ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE) (reject|blackhole) <1-255> " VRF_CMD_STR, + IP_STR + "Establish static routes\n" + "IP destination prefix\n" + "IP destination prefix mask\n" + "IP gateway address\n" + "IP gateway interface name\n" + "Emit an ICMP unreachable when matched\n" + "Silently discard pkts when matched\n" + "Distance value for this route\n" + VRF_CMD_HELP_STR) +{ + return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], argv[1], + argv[2], argv[3], NULL, argv[4], argv[5]); +} + +DEFUN (ip_route_mask_flags_distance2_vrf, + ip_route_mask_flags_distance2_vrf_cmd, + "ip route A.B.C.D A.B.C.D (reject|blackhole) <1-255> " VRF_CMD_STR, + IP_STR + "Establish static routes\n" + "IP destination prefix\n" + "IP destination prefix mask\n" + "Emit an ICMP unreachable when matched\n" + "Silently discard pkts when matched\n" + "Distance value for this route\n" + VRF_CMD_HELP_STR) +{ + return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 1, argv[0], argv[1], + NULL, argv[2], NULL, argv[3], argv[4]); +} + +DEFUN (no_ip_route_vrf, + no_ip_route_vrf_cmd, + "no ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0) " VRF_CMD_STR, + NO_STR + IP_STR + "Establish static routes\n" + "IP destination prefix (e.g. 10.0.0.0/8)\n" + "IP gateway address\n" + "IP gateway interface name\n" + "Null interface\n" + VRF_CMD_HELP_STR) +{ + return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 0, argv[0], + NULL, argv[1], NULL, NULL, NULL, + (argc > 3) ? argv[3] : argv[2]); +} + +ALIAS (no_ip_route_vrf, + no_ip_route_flags_vrf_cmd, + "no ip route A.B.C.D/M (A.B.C.D|INTERFACE) (reject|blackhole) " VRF_CMD_STR, + NO_STR + IP_STR + "Establish static routes\n" + "IP destination prefix (e.g. 10.0.0.0/8)\n" + "IP gateway address\n" + "IP gateway interface name\n" + "Emit an ICMP unreachable when matched\n" + "Silently discard pkts when matched\n" + VRF_CMD_HELP_STR) + +DEFUN (no_ip_route_flags2_vrf, + no_ip_route_flags2_vrf_cmd, + "no ip route A.B.C.D/M (reject|blackhole) " VRF_CMD_STR, + NO_STR + IP_STR + "Establish static routes\n" + "IP destination prefix (e.g. 10.0.0.0/8)\n" + "Emit an ICMP unreachable when matched\n" + "Silently discard pkts when matched\n" + VRF_CMD_HELP_STR) +{ + return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 0, argv[0], + NULL, NULL, NULL, NULL, NULL, argv[2]); +} + +DEFUN (no_ip_route_mask_vrf, + no_ip_route_mask_vrf_cmd, + "no ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0) " VRF_CMD_STR, + NO_STR + IP_STR + "Establish static routes\n" + "IP destination prefix\n" + "IP destination prefix mask\n" + "IP gateway address\n" + "IP gateway interface name\n" + "Null interface\n" + VRF_CMD_HELP_STR) +{ + return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 0, argv[0], argv[1], + argv[2], NULL, NULL, NULL, + (argc > 4) ? argv[4] : argv[3]); +} + +ALIAS (no_ip_route_mask_vrf, + no_ip_route_mask_flags_vrf_cmd, + "no ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE) (reject|blackhole) " VRF_CMD_STR, + NO_STR + IP_STR + "Establish static routes\n" + "IP destination prefix\n" + "IP destination prefix mask\n" + "IP gateway address\n" + "IP gateway interface name\n" + "Emit an ICMP unreachable when matched\n" + "Silently discard pkts when matched\n" + VRF_CMD_HELP_STR) + +DEFUN (no_ip_route_mask_flags2_vrf, + no_ip_route_mask_flags2_vrf_cmd, + "no ip route A.B.C.D A.B.C.D (reject|blackhole) " VRF_CMD_STR, + NO_STR + IP_STR + "Establish static routes\n" + "IP destination prefix\n" + "IP destination prefix mask\n" + "Emit an ICMP unreachable when matched\n" + "Silently discard pkts when matched\n" + VRF_CMD_HELP_STR) +{ + return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 0, argv[0], argv[1], + NULL, NULL, NULL, NULL, argv[2]); +} + +DEFUN (no_ip_route_distance_vrf, + no_ip_route_distance_vrf_cmd, + "no ip route A.B.C.D/M (A.B.C.D|INTERFACE|null0) <1-255> " VRF_CMD_STR, + NO_STR + IP_STR + "Establish static routes\n" + "IP destination prefix (e.g. 10.0.0.0/8)\n" + "IP gateway address\n" + "IP gateway interface name\n" + "Null interface\n" + "Distance value for this route\n" + VRF_CMD_HELP_STR) +{ + return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 0, argv[0], NULL, + argv[1], NULL, NULL, argv[2], argv[3]); +} + +DEFUN (no_ip_route_flags_distance_vrf, + no_ip_route_flags_distance_vrf_cmd, + "no ip route A.B.C.D/M (A.B.C.D|INTERFACE) (reject|blackhole) <1-255> " VRF_CMD_STR, + NO_STR + IP_STR + "Establish static routes\n" + "IP destination prefix (e.g. 10.0.0.0/8)\n" + "IP gateway address\n" + "IP gateway interface name\n" + "Emit an ICMP unreachable when matched\n" + "Silently discard pkts when matched\n" + "Distance value for this route\n" + VRF_CMD_HELP_STR) +{ + return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 0, argv[0], NULL, argv[1], + argv[2], NULL, argv[3], argv[4]); +} + +DEFUN (no_ip_route_flags_distance2_vrf, + no_ip_route_flags_distance2_vrf_cmd, + "no ip route A.B.C.D/M (reject|blackhole) <1-255> " VRF_CMD_STR, + NO_STR + IP_STR + "Establish static routes\n" + "IP destination prefix (e.g. 10.0.0.0/8)\n" + "Emit an ICMP unreachable when matched\n" + "Silently discard pkts when matched\n" + "Distance value for this route\n" + VRF_CMD_HELP_STR) +{ + return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 0, argv[0], NULL, NULL, + argv[1], NULL, argv[2], argv[3]); +} + +DEFUN (no_ip_route_mask_distance_vrf, + no_ip_route_mask_distance_vrf_cmd, + "no ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE|null0) <1-255> " VRF_CMD_STR, + NO_STR + IP_STR + "Establish static routes\n" + "IP destination prefix\n" + "IP destination prefix mask\n" + "IP gateway address\n" + "IP gateway interface name\n" + "Null interface\n" + "Distance value for this route\n" + VRF_CMD_HELP_STR) +{ + return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 0, argv[0], argv[1], + argv[2], NULL, NULL, argv[3], argv[4]); +} + +DEFUN (no_ip_route_mask_flags_distance_vrf, + no_ip_route_mask_flags_distance_vrf_cmd, + "no ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE) (reject|blackhole) <1-255> " VRF_CMD_STR, + NO_STR + IP_STR + "Establish static routes\n" + "IP destination prefix\n" + "IP destination prefix mask\n" + "IP gateway address\n" + "IP gateway interface name\n" + "Emit an ICMP unreachable when matched\n" + "Silently discard pkts when matched\n" + "Distance value for this route\n" + VRF_CMD_HELP_STR) +{ + return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 0, argv[0], argv[1], argv[2], + argv[3], NULL, argv[4], argv[5]); +} + +DEFUN (no_ip_route_mask_flags_distance2_vrf, + no_ip_route_mask_flags_distance2_vrf_cmd, + "no ip route A.B.C.D A.B.C.D (reject|blackhole) <1-255> " VRF_CMD_STR, + NO_STR + IP_STR + "Establish static routes\n" + "IP destination prefix\n" + "IP destination prefix mask\n" + "Emit an ICMP unreachable when matched\n" + "Silently discard pkts when matched\n" + "Distance value for this route\n" + VRF_CMD_HELP_STR) +{ + return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 0, argv[0], argv[1], NULL, + argv[2], NULL, argv[3], argv[4]); +} + +DEFUN (no_ip_route_mask_flags_tag_distance2, + no_ip_route_mask_flags_tag_distance2_cmd, + "no ip route A.B.C.D A.B.C.D (reject|blackhole) tag <1-4294967295> <1-255>", + NO_STR + IP_STR + "Establish static routes\n" + "IP destination prefix\n" + "IP destination prefix mask\n" + "Emit an ICMP unreachable when matched\n" + "Silently discard pkts when matched\n" + "Tag of this route\n" + "Tag value\n" + "Distance value for this route\n") +{ + return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 0, argv[0], argv[1], NULL, + argv[2], argv[3], argv[4], NULL); +} + +DEFUN (no_ip_route_mask_flags_tag_distance2_vrf, + no_ip_route_mask_flags_tag_distance2_vrf_cmd, + "no ip route A.B.C.D A.B.C.D (reject|blackhole) tag <1-4294967295> <1-255>" VRF_CMD_STR, + NO_STR + IP_STR + "Establish static routes\n" + "IP destination prefix\n" + "IP destination prefix mask\n" + "Emit an ICMP unreachable when matched\n" + "Silently discard pkts when matched\n" + "Tag of this route\n" + "Tag value\n" + "Distance value for this route\n" + VRF_CMD_HELP_STR) +{ + return zebra_static_ipv4_safi (vty, SAFI_UNICAST, 0, argv[0], argv[1], NULL, + argv[2], argv[3], argv[4], argv[5]); +} + +char *proto_rm[AFI_MAX][ZEBRA_ROUTE_MAX+1]; /* "any" == ZEBRA_ROUTE_MAX */ + +DEFUN (ip_protocol, + ip_protocol_cmd, + "ip protocol PROTO route-map ROUTE-MAP", + NO_STR + "Apply route map to PROTO\n" + "Protocol name\n" + "Route map name\n") +{ + int i; + + if (strcasecmp(argv[0], "any") == 0) + i = ZEBRA_ROUTE_MAX; + else + i = proto_name2num(argv[0]); + if (i < 0) + { + vty_out (vty, "invalid protocol name \"%s\"%s", argv[0] ? argv[0] : "", + VTY_NEWLINE); + return CMD_WARNING; + } + if (proto_rm[AFI_IP][i]) + XFREE (MTYPE_ROUTE_MAP_NAME, proto_rm[AFI_IP][i]); + proto_rm[AFI_IP][i] = XSTRDUP (MTYPE_ROUTE_MAP_NAME, argv[1]); + return CMD_SUCCESS; +} + +DEFUN (no_ip_protocol, + no_ip_protocol_cmd, + "no ip protocol PROTO", + NO_STR + "Remove route map from PROTO\n" + "Protocol name\n") +{ + int i; + + if (strcasecmp(argv[0], "any") == 0) + i = ZEBRA_ROUTE_MAX; + else + i = proto_name2num(argv[0]); + if (i < 0) + { + vty_out (vty, "invalid protocol name \"%s\"%s", argv[0] ? argv[0] : "", + VTY_NEWLINE); + return CMD_WARNING; + } + if (proto_rm[AFI_IP][i]) + XFREE (MTYPE_ROUTE_MAP_NAME, proto_rm[AFI_IP][i]); + proto_rm[AFI_IP][i] = NULL; + return CMD_SUCCESS; +} + +/* New RIB. Detailed information for IPv4 route. */ +static void +vty_show_ip_route_detail (struct vty *vty, struct route_node *rn, int mcast) +{ + struct rib *rib; + struct nexthop *nexthop, *tnexthop; + int recursing; + char buf[PREFIX_STRLEN]; + + RNODE_FOREACH_RIB (rn, rib) + { + 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%s%s", + prefix2str (&rn->p, buf, sizeof(buf)), 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); + if (rib->mtu) + vty_out (vty, ", mtu %u", rib->mtu); + vty_out (vty, ", tag %d", rib->tag); + vty_out (vty, ", vrf %u", rib->vrf_id); + if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED)) + vty_out (vty, ", best"); + if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_FIB_OVERRIDE)) + vty_out (vty, ", fib-override"); + if (CHECK_FLAG (rib->status, RIB_ENTRY_SELECTED_FIB)) + vty_out (vty, ", fib"); if (rib->refcnt) - vty_out (vty, ", refcnt %ld", rib->refcnt); + vty_out (vty, ", refcnt %ld", rib->refcnt); if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_BLACKHOLE)) vty_out (vty, ", blackhole"); if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_REJECT)) vty_out (vty, ", reject"); vty_out (vty, "%s", VTY_NEWLINE); -#define ONE_DAY_SECOND 60*60*24 -#define ONE_WEEK_SECOND 60*60*24*7 - if (rib->type == ZEBRA_ROUTE_RIP - || rib->type == ZEBRA_ROUTE_OSPF - || rib->type == ZEBRA_ROUTE_BABEL - || rib->type == ZEBRA_ROUTE_ISIS - || rib->type == ZEBRA_ROUTE_BGP) - { - time_t uptime; - struct tm *tm; +#define ONE_DAY_SECOND 60*60*24 +#define ONE_WEEK_SECOND 60*60*24*7 + if (rib->type == ZEBRA_ROUTE_RIP + || rib->type == ZEBRA_ROUTE_RIPNG + || rib->type == ZEBRA_ROUTE_OSPF + || rib->type == ZEBRA_ROUTE_OSPF6 + || rib->type == ZEBRA_ROUTE_BABEL + || rib->type == ZEBRA_ROUTE_ISIS + || rib->type == ZEBRA_ROUTE_NHRP + || rib->type == ZEBRA_ROUTE_BGP) + { + time_t uptime; + struct tm *tm; + + uptime = time (NULL); + uptime -= rib->uptime; + tm = gmtime (&uptime); + + vty_out (vty, " Last update "); + + if (uptime < ONE_DAY_SECOND) + vty_out (vty, "%02d:%02d:%02d", + tm->tm_hour, tm->tm_min, tm->tm_sec); + else if (uptime < ONE_WEEK_SECOND) + vty_out (vty, "%dd%02dh%02dm", + tm->tm_yday, tm->tm_hour, tm->tm_min); + else + vty_out (vty, "%02dw%dd%02dh", + tm->tm_yday/7, + tm->tm_yday - ((tm->tm_yday/7) * 7), tm->tm_hour); + vty_out (vty, " ago%s", VTY_NEWLINE); + } + + for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing)) + { + vty_out (vty, " %c%c%s", + CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE) ? '>' : ' ', + CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB) ? '*' : ' ', + recursing ? " " : ""); + + switch (nexthop->type) + { + case NEXTHOP_TYPE_IPV4: + case NEXTHOP_TYPE_IPV4_IFINDEX: + vty_out (vty, " %s", inet_ntoa (nexthop->gate.ipv4)); + if (nexthop->ifindex) + vty_out (vty, ", via %s", + ifindex2ifname_vrf (nexthop->ifindex, rib->vrf_id)); + break; + case NEXTHOP_TYPE_IPV6: + case NEXTHOP_TYPE_IPV6_IFINDEX: + case NEXTHOP_TYPE_IPV6_IFNAME: + vty_out (vty, " %s", + inet_ntop (AF_INET6, &nexthop->gate.ipv6, buf, sizeof(buf))); + if (nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME) + vty_out (vty, ", %s", nexthop->ifname); + else if (nexthop->ifindex) + vty_out (vty, ", via %s", + ifindex2ifname_vrf (nexthop->ifindex, rib->vrf_id)); + break; + case NEXTHOP_TYPE_IFINDEX: + vty_out (vty, " directly connected, %s", + ifindex2ifname_vrf (nexthop->ifindex, rib->vrf_id)); + break; + case NEXTHOP_TYPE_IFNAME: + vty_out (vty, " directly connected, %s", nexthop->ifname); + break; + case NEXTHOP_TYPE_BLACKHOLE: + vty_out (vty, " directly connected, Null0"); + break; + default: + break; + } + 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->type) + { + case NEXTHOP_TYPE_IPV4: + case NEXTHOP_TYPE_IPV4_IFINDEX: + case NEXTHOP_TYPE_IPV4_IFNAME: + if (nexthop->src.ipv4.s_addr) + { + if (inet_ntop(AF_INET, &nexthop->src.ipv4, buf, sizeof buf)) + vty_out (vty, ", src %s", buf); + } + break; +#ifdef HAVE_IPV6 + case NEXTHOP_TYPE_IPV6: + case NEXTHOP_TYPE_IPV6_IFINDEX: + case NEXTHOP_TYPE_IPV6_IFNAME: + if (!IPV6_ADDR_SAME(&nexthop->src.ipv6, &in6addr_any)) + { + if (inet_ntop(AF_INET6, &nexthop->src.ipv6, buf, sizeof buf)) + vty_out (vty, ", src %s", buf); + } + break; +#endif /* HAVE_IPV6 */ + default: + break; + } + vty_out (vty, "%s", VTY_NEWLINE); + } + vty_out (vty, "%s", VTY_NEWLINE); + } +} + +static void +vty_show_ip_route (struct vty *vty, struct route_node *rn, struct rib *rib) +{ + struct nexthop *nexthop, *tnexthop; + int recursing; + int len = 0; + char buf[BUFSIZ]; + + /* Nexthop information. */ + for (ALL_NEXTHOPS_RO(rib->nexthop, nexthop, tnexthop, recursing)) + { + if (nexthop == rib->nexthop) + { + /* Prefix information. */ + len = vty_out (vty, "%c%c%c %s", + zebra_route_char (rib->type), + CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED) + ? '>' : ' ', + CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB) + ? '*' : ' ', + prefix2str (&rn->p, buf, sizeof buf)); + + /* Distance and metric display. */ + if (rib->type != ZEBRA_ROUTE_CONNECT + && rib->type != ZEBRA_ROUTE_KERNEL) + len += vty_out (vty, " [%d/%d]", rib->distance, + rib->metric); + + if (rib->vrf_id != VRF_DEFAULT) + len += vty_out (vty, " [vrf %u]", rib->vrf_id); + } + else + vty_out (vty, " %c%*c", + CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB) + ? '*' : ' ', + len - 3 + (2 * recursing), ' '); + + switch (nexthop->type) + { + case NEXTHOP_TYPE_IPV4: + case NEXTHOP_TYPE_IPV4_IFINDEX: + vty_out (vty, " via %s", inet_ntoa (nexthop->gate.ipv4)); + if (nexthop->ifindex) + vty_out (vty, ", %s", + ifindex2ifname_vrf (nexthop->ifindex, rib->vrf_id)); + break; + case NEXTHOP_TYPE_IPV6: + case NEXTHOP_TYPE_IPV6_IFINDEX: + case NEXTHOP_TYPE_IPV6_IFNAME: + vty_out (vty, " via %s", + inet_ntop (AF_INET6, &nexthop->gate.ipv6, buf, BUFSIZ)); + if (nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME) + vty_out (vty, ", %s", nexthop->ifname); + else if (nexthop->ifindex) + vty_out (vty, ", %s", + ifindex2ifname_vrf (nexthop->ifindex, rib->vrf_id)); + break; + case NEXTHOP_TYPE_IFINDEX: + vty_out (vty, " is directly connected, %s", + ifindex2ifname_vrf (nexthop->ifindex, rib->vrf_id)); + break; + case NEXTHOP_TYPE_IFNAME: + vty_out (vty, " is directly connected, %s", nexthop->ifname); + break; + case NEXTHOP_TYPE_BLACKHOLE: + vty_out (vty, " is directly connected, Null0"); + break; + default: + break; + } + 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->type) + { + case NEXTHOP_TYPE_IPV4: + case NEXTHOP_TYPE_IPV4_IFINDEX: + case NEXTHOP_TYPE_IPV4_IFNAME: + if (nexthop->src.ipv4.s_addr) + { + if (inet_ntop(AF_INET, &nexthop->src.ipv4, buf, sizeof buf)) + vty_out (vty, ", src %s", buf); + } + break; +#ifdef HAVE_IPV6 + case NEXTHOP_TYPE_IPV6: + case NEXTHOP_TYPE_IPV6_IFINDEX: + case NEXTHOP_TYPE_IPV6_IFNAME: + if (!IPV6_ADDR_SAME(&nexthop->src.ipv6, &in6addr_any)) + { + if (inet_ntop(AF_INET6, &nexthop->src.ipv6, buf, sizeof buf)) + vty_out (vty, ", src %s", buf); + } + break; +#endif /* HAVE_IPV6 */ + default: + break; + } + + if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_BLACKHOLE)) + vty_out (vty, ", bh"); + if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_REJECT)) + vty_out (vty, ", rej"); + + if (rib->type == ZEBRA_ROUTE_RIP + || rib->type == ZEBRA_ROUTE_RIPNG + || rib->type == ZEBRA_ROUTE_OSPF + || rib->type == ZEBRA_ROUTE_OSPF6 + || rib->type == ZEBRA_ROUTE_BABEL + || rib->type == ZEBRA_ROUTE_ISIS + || rib->type == ZEBRA_ROUTE_NHRP + || rib->type == ZEBRA_ROUTE_BGP) + { + time_t uptime; + struct tm *tm; + + uptime = time (NULL); + uptime -= rib->uptime; + tm = gmtime (&uptime); + +#define ONE_DAY_SECOND 60*60*24 +#define ONE_WEEK_SECOND 60*60*24*7 + + if (uptime < ONE_DAY_SECOND) + vty_out (vty, ", %02d:%02d:%02d", + tm->tm_hour, tm->tm_min, tm->tm_sec); + else if (uptime < ONE_WEEK_SECOND) + vty_out (vty, ", %dd%02dh%02dm", + tm->tm_yday, tm->tm_hour, tm->tm_min); + else + vty_out (vty, ", %02dw%dd%02dh", + tm->tm_yday/7, + tm->tm_yday - ((tm->tm_yday/7) * 7), tm->tm_hour); + } + vty_out (vty, "%s", VTY_NEWLINE); + } +} + +DEFUN (show_ip_route, + show_ip_route_cmd, + "show ip route", + SHOW_STR + IP_STR + "IP routing table\n") +{ + vrf_id_t vrf_id = VRF_DEFAULT; + + if (argc > 0) + VTY_GET_INTEGER ("VRF ID", vrf_id, argv[0]); + + return do_show_ip_route(vty, SAFI_UNICAST, vrf_id); +} + +static int do_show_ip_route(struct vty *vty, safi_t safi, vrf_id_t vrf_id) +{ + struct route_table *table; + struct route_node *rn; + struct rib *rib; + int first = 1; + + table = zebra_vrf_table (AFI_IP, safi, vrf_id); + if (! table) + return CMD_SUCCESS; + + /* Show all IPv4 routes. */ + for (rn = route_top (table); rn; rn = route_next (rn)) + RNODE_FOREACH_RIB (rn, rib) + { + if (first) + { + vty_out (vty, SHOW_ROUTE_V4_HEADER); + first = 0; + } + vty_show_ip_route (vty, rn, rib); + } + return CMD_SUCCESS; +} + +ALIAS (show_ip_route, + show_ip_route_vrf_cmd, + "show ip route " VRF_CMD_STR, + SHOW_STR + IP_STR + "IP routing table\n" + VRF_CMD_HELP_STR) + +DEFUN (show_ip_nht, + show_ip_nht_cmd, + "show ip nht", + SHOW_STR + IP_STR + "IP nexthop tracking table\n") +{ + zebra_print_rnh_table(0, AF_INET, vty); + return CMD_SUCCESS; +} + +DEFUN (show_ipv6_nht, + show_ipv6_nht_cmd, + "show ipv6 nht", + SHOW_STR + IP_STR + "IPv6 nexthop tracking table\n") +{ + zebra_print_rnh_table(0, AF_INET6, vty); + return CMD_SUCCESS; +} + +DEFUN (show_ip_route_tag, + show_ip_route_tag_cmd, + "show ip route tag <1-4294967295>", + SHOW_STR + IP_STR + "IP routing table\n" + "Show only routes with tag\n" + "Tag value\n") +{ + struct route_table *table; + struct route_node *rn; + struct rib *rib; + int first = 1; + route_tag_t tag = 0; + vrf_id_t vrf_id = VRF_DEFAULT; + + if (argv[0]) + tag = atoi(argv[0]); + + if (argc == 2) + VTY_GET_INTEGER ("VRF ID", vrf_id, argv[1]); + + table = zebra_vrf_table (AFI_IP, SAFI_UNICAST, vrf_id); + if (! table) + return CMD_SUCCESS; + + /* Show all IPv4 routes with matching tag value. */ + for (rn = route_top (table); rn; rn = route_next (rn)) + RNODE_FOREACH_RIB (rn, rib) + { + if (rib->tag != tag) + continue; + + if (first) + { + vty_out (vty, SHOW_ROUTE_V4_HEADER); + first = 0; + } + vty_show_ip_route (vty, rn, rib); + } + return CMD_SUCCESS; +} + +ALIAS (show_ip_route_tag, + show_ip_route_tag_vrf_cmd, + "show ip route tag <1-4294967295>" VRF_CMD_STR, + SHOW_STR + IP_STR + "IP routing table\n" + "Show only routes with tag\n" + "Tag value\n" + VRF_CMD_HELP_STR) + +DEFUN (show_ip_route_prefix_longer, + show_ip_route_prefix_longer_cmd, + "show ip route A.B.C.D/M longer-prefixes", + SHOW_STR + IP_STR + "IP routing table\n" + "IP prefix /, e.g., 35.0.0.0/8\n" + "Show route matching the specified Network/Mask pair only\n") +{ + struct route_table *table; + struct route_node *rn; + struct rib *rib; + struct prefix p; + int ret; + int first = 1; + vrf_id_t vrf_id = VRF_DEFAULT; + + ret = str2prefix (argv[0], &p); + if (! ret) + { + vty_out (vty, "%% Malformed Prefix%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (argc > 1) + VTY_GET_INTEGER ("VRF ID", vrf_id, argv[1]); + + table = zebra_vrf_table (AFI_IP, SAFI_UNICAST, vrf_id); + if (! table) + return CMD_SUCCESS; + + /* Show matched type IPv4 routes. */ + for (rn = route_top (table); rn; rn = route_next (rn)) + RNODE_FOREACH_RIB (rn, rib) + if (prefix_match (&p, &rn->p)) + { + if (first) + { + vty_out (vty, SHOW_ROUTE_V4_HEADER); + first = 0; + } + vty_show_ip_route (vty, rn, rib); + } + return CMD_SUCCESS; +} + +ALIAS (show_ip_route_prefix_longer, + show_ip_route_prefix_longer_vrf_cmd, + "show ip route A.B.C.D/M longer-prefixes " VRF_CMD_STR, + SHOW_STR + IP_STR + "IP routing table\n" + "IP prefix /, e.g., 35.0.0.0/8\n" + "Show route matching the specified Network/Mask pair only\n" + VRF_CMD_HELP_STR) + +DEFUN (show_ip_route_supernets, + show_ip_route_supernets_cmd, + "show ip route supernets-only", + SHOW_STR + IP_STR + "IP routing table\n" + "Show supernet entries only\n") +{ + struct route_table *table; + struct route_node *rn; + struct rib *rib; + u_int32_t addr; + int first = 1; + vrf_id_t vrf_id = VRF_DEFAULT; + + if (argc > 0) + VTY_GET_INTEGER ("VRF ID", vrf_id, argv[0]); + + table = zebra_vrf_table (AFI_IP, SAFI_UNICAST, vrf_id); + if (! table) + return CMD_SUCCESS; + + /* Show matched type IPv4 routes. */ + for (rn = route_top (table); rn; rn = route_next (rn)) + RNODE_FOREACH_RIB (rn, rib) + { + addr = ntohl (rn->p.u.prefix4.s_addr); + + if ((IN_CLASSC (addr) && rn->p.prefixlen < 24) + || (IN_CLASSB (addr) && rn->p.prefixlen < 16) + || (IN_CLASSA (addr) && rn->p.prefixlen < 8)) + { + if (first) + { + vty_out (vty, SHOW_ROUTE_V4_HEADER); + first = 0; + } + vty_show_ip_route (vty, rn, rib); + } + } + return CMD_SUCCESS; +} + +ALIAS (show_ip_route_supernets, + show_ip_route_supernets_vrf_cmd, + "show ip route supernets-only " VRF_CMD_STR, + SHOW_STR + IP_STR + "IP routing table\n" + "Show supernet entries only\n" + VRF_CMD_HELP_STR) + +DEFUN (show_ip_route_protocol, + show_ip_route_protocol_cmd, + "show ip route " QUAGGA_IP_REDIST_STR_ZEBRA, + SHOW_STR + IP_STR + "IP routing table\n" + QUAGGA_IP_REDIST_HELP_STR_ZEBRA) +{ + int type; + struct route_table *table; + struct route_node *rn; + struct rib *rib; + int first = 1; + vrf_id_t vrf_id = VRF_DEFAULT; + + type = proto_redistnum (AFI_IP, argv[0]); + if (type < 0) + { + vty_out (vty, "Unknown route type%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (argc > 1) + VTY_GET_INTEGER ("VRF ID", vrf_id, argv[1]); + + table = zebra_vrf_table (AFI_IP, SAFI_UNICAST, vrf_id); + if (! table) + return CMD_SUCCESS; + + /* Show matched type IPv4 routes. */ + for (rn = route_top (table); rn; rn = route_next (rn)) + RNODE_FOREACH_RIB (rn, rib) + if (rib->type == type) + { + if (first) + { + vty_out (vty, SHOW_ROUTE_V4_HEADER); + first = 0; + } + vty_show_ip_route (vty, rn, rib); + } + return CMD_SUCCESS; +} + +ALIAS (show_ip_route_protocol, + show_ip_route_protocol_vrf_cmd, + "show ip route " QUAGGA_IP_REDIST_STR_ZEBRA " " VRF_CMD_STR, + SHOW_STR + IP_STR + "IP routing table\n" + QUAGGA_IP_REDIST_HELP_STR_ZEBRA + VRF_CMD_HELP_STR) + +DEFUN (show_ip_route_addr, + show_ip_route_addr_cmd, + "show ip route A.B.C.D", + SHOW_STR + IP_STR + "IP routing table\n" + "Network in the IP routing table to display\n") +{ + int ret; + struct prefix_ipv4 p; + struct route_table *table; + struct route_node *rn; + vrf_id_t vrf_id = VRF_DEFAULT; + + ret = str2prefix_ipv4 (argv[0], &p); + if (ret <= 0) + { + vty_out (vty, "%% Malformed IPv4 address%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (argc > 1) + VTY_GET_INTEGER ("VRF ID", vrf_id, argv[1]); + + table = zebra_vrf_table (AFI_IP, SAFI_UNICAST, vrf_id); + if (! table) + return CMD_SUCCESS; + + rn = route_node_match (table, (struct prefix *) &p); + if (! rn) + { + vty_out (vty, "%% Network not in table%s", VTY_NEWLINE); + return CMD_WARNING; + } + + vty_show_ip_route_detail (vty, rn, 0); + + route_unlock_node (rn); + + return CMD_SUCCESS; +} + +ALIAS (show_ip_route_addr, + show_ip_route_addr_vrf_cmd, + "show ip route A.B.C.D " VRF_CMD_STR, + SHOW_STR + IP_STR + "IP routing table\n" + "Network in the IP routing table to display\n" + VRF_CMD_HELP_STR) + +DEFUN (show_ip_route_prefix, + show_ip_route_prefix_cmd, + "show ip route A.B.C.D/M", + SHOW_STR + IP_STR + "IP routing table\n" + "IP prefix /, e.g., 35.0.0.0/8\n") +{ + int ret; + struct prefix_ipv4 p; + struct route_table *table; + struct route_node *rn; + vrf_id_t vrf_id = VRF_DEFAULT; + + ret = str2prefix_ipv4 (argv[0], &p); + if (ret <= 0) + { + vty_out (vty, "%% Malformed IPv4 address%s", VTY_NEWLINE); + return CMD_WARNING; + } + + if (argc > 1) + VTY_GET_INTEGER ("VRF ID", vrf_id, argv[1]); + + table = zebra_vrf_table (AFI_IP, SAFI_UNICAST, vrf_id); + if (! table) + return CMD_SUCCESS; + + rn = route_node_match (table, (struct prefix *) &p); + 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, 0); + + route_unlock_node (rn); + + return CMD_SUCCESS; +} + +ALIAS (show_ip_route_prefix, + show_ip_route_prefix_vrf_cmd, + "show ip route A.B.C.D/M " VRF_CMD_STR, + SHOW_STR + IP_STR + "IP routing table\n" + "IP prefix /, e.g., 35.0.0.0/8\n" + VRF_CMD_HELP_STR) + +static void +vty_show_ip_route_summary (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; + + 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) + 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) + || nexthop_has_fib_child(nexthop)) + { + 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) + || nexthop_has_fib_child(nexthop)) + fib_cnt[ZEBRA_ROUTE_IBGP]++; + } + } + + vty_out (vty, "%-20s %-20s %s (vrf %u)%s", + "Route Source", "Routes", "FIB", + ((rib_table_info_t *)table->info)->zvrf->vrf_id, + 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); + vty_out (vty, "%s", 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 %s (vrf %u)%s", + "Route Source", "Prefix Routes", "FIB", + ((rib_table_info_t *)table->info)->zvrf->vrf_id, + 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); + vty_out (vty, "%s", VTY_NEWLINE); +} + +/* Show route summary. */ +DEFUN (show_ip_route_summary, + show_ip_route_summary_cmd, + "show ip route summary", + SHOW_STR + IP_STR + "IP routing table\n" + "Summary of all routes\n") +{ + struct route_table *table; + vrf_id_t vrf_id = VRF_DEFAULT; + + if (argc > 0) + VTY_GET_INTEGER ("VRF ID", vrf_id, argv[0]); + + table = zebra_vrf_table (AFI_IP, SAFI_UNICAST, vrf_id); + if (! table) + return CMD_SUCCESS; + + vty_show_ip_route_summary (vty, table); + + return CMD_SUCCESS; +} + +ALIAS (show_ip_route_summary, + show_ip_route_summary_vrf_cmd, + "show ip route summary " VRF_CMD_STR, + SHOW_STR + IP_STR + "IP routing table\n" + "Summary of all routes\n" + VRF_CMD_HELP_STR) + +/* 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; + vrf_id_t vrf_id = VRF_DEFAULT; + + if (argc > 0) + VTY_GET_INTEGER ("VRF ID", vrf_id, argv[0]); + + table = zebra_vrf_table (AFI_IP, SAFI_UNICAST, vrf_id); + if (! table) + return CMD_SUCCESS; + + vty_show_ip_route_summary_prefix (vty, table); + + return CMD_SUCCESS; +} + +ALIAS (show_ip_route_summary_prefix, + show_ip_route_summary_prefix_vrf_cmd, + "show ip route summary prefix " VRF_CMD_STR, + SHOW_STR + IP_STR + "IP routing table\n" + "Summary of all routes\n" + "Prefix routes\n" + VRF_CMD_HELP_STR) + +DEFUN (show_ip_route_vrf_all, + show_ip_route_vrf_all_cmd, + "show ip route " VRF_ALL_CMD_STR, + SHOW_STR + IP_STR + "IP routing table\n" + VRF_ALL_CMD_HELP_STR) +{ + struct route_table *table; + struct route_node *rn; + struct rib *rib; + struct zebra_vrf *zvrf; + vrf_iter_t iter; + int first = 1; + + for (iter = vrf_first (); iter != VRF_ITER_INVALID; iter = vrf_next (iter)) + { + if ((zvrf = vrf_iter2info (iter)) == NULL || + (table = zvrf->table[AFI_IP][SAFI_UNICAST]) == NULL) + continue; + + /* Show all IPv4 routes. */ + for (rn = route_top (table); rn; rn = route_next (rn)) + RNODE_FOREACH_RIB (rn, rib) + { + if (first) + { + vty_out (vty, SHOW_ROUTE_V4_HEADER); + first = 0; + } + vty_show_ip_route (vty, rn, rib); + } + } + + return CMD_SUCCESS; +} + +DEFUN (show_ip_route_prefix_longer_vrf_all, + show_ip_route_prefix_longer_vrf_all_cmd, + "show ip route A.B.C.D/M longer-prefixes " VRF_ALL_CMD_STR, + SHOW_STR + IP_STR + "IP routing table\n" + "IP prefix /, e.g., 35.0.0.0/8\n" + "Show route matching the specified Network/Mask pair only\n" + VRF_ALL_CMD_HELP_STR) +{ + struct route_table *table; + struct route_node *rn; + struct rib *rib; + struct prefix p; + struct zebra_vrf *zvrf; + vrf_iter_t iter; + int ret; + int first = 1; + + ret = str2prefix (argv[0], &p); + if (! ret) + { + vty_out (vty, "%% Malformed Prefix%s", VTY_NEWLINE); + return CMD_WARNING; + } + + for (iter = vrf_first (); iter != VRF_ITER_INVALID; iter = vrf_next (iter)) + { + if ((zvrf = vrf_iter2info (iter)) == NULL || + (table = zvrf->table[AFI_IP][SAFI_UNICAST]) == NULL) + continue; + + /* Show matched type IPv4 routes. */ + for (rn = route_top (table); rn; rn = route_next (rn)) + RNODE_FOREACH_RIB (rn, rib) + if (prefix_match (&p, &rn->p)) + { + if (first) + { + vty_out (vty, SHOW_ROUTE_V4_HEADER); + first = 0; + } + vty_show_ip_route (vty, rn, rib); + } + } + + return CMD_SUCCESS; +} + +DEFUN (show_ip_route_supernets_vrf_all, + show_ip_route_supernets_vrf_all_cmd, + "show ip route supernets-only " VRF_ALL_CMD_STR, + SHOW_STR + IP_STR + "IP routing table\n" + "Show supernet entries only\n" + VRF_ALL_CMD_HELP_STR) +{ + struct route_table *table; + struct route_node *rn; + struct rib *rib; + struct zebra_vrf *zvrf; + vrf_iter_t iter; + u_int32_t addr; + int first = 1; + + for (iter = vrf_first (); iter != VRF_ITER_INVALID; iter = vrf_next (iter)) + { + if ((zvrf = vrf_iter2info (iter)) == NULL || + (table = zvrf->table[AFI_IP][SAFI_UNICAST]) == NULL) + continue; + + /* Show matched type IPv4 routes. */ + for (rn = route_top (table); rn; rn = route_next (rn)) + RNODE_FOREACH_RIB (rn, rib) + { + addr = ntohl (rn->p.u.prefix4.s_addr); + + if ((IN_CLASSC (addr) && rn->p.prefixlen < 24) + || (IN_CLASSB (addr) && rn->p.prefixlen < 16) + || (IN_CLASSA (addr) && rn->p.prefixlen < 8)) + { + if (first) + { + vty_out (vty, SHOW_ROUTE_V4_HEADER); + first = 0; + } + vty_show_ip_route (vty, rn, rib); + } + } + } + + return CMD_SUCCESS; +} + +DEFUN (show_ip_route_protocol_vrf_all, + show_ip_route_protocol_vrf_all_cmd, + "show ip route " QUAGGA_IP_REDIST_STR_ZEBRA " " VRF_ALL_CMD_STR, + SHOW_STR + IP_STR + "IP routing table\n" + QUAGGA_IP_REDIST_HELP_STR_ZEBRA + VRF_ALL_CMD_HELP_STR) +{ + int type; + struct route_table *table; + struct route_node *rn; + struct rib *rib; + struct zebra_vrf *zvrf; + vrf_iter_t iter; + int first = 1; + + type = proto_redistnum (AFI_IP, argv[0]); + if (type < 0) + { + vty_out (vty, "Unknown route type%s", VTY_NEWLINE); + return CMD_WARNING; + } + + for (iter = vrf_first (); iter != VRF_ITER_INVALID; iter = vrf_next (iter)) + { + if ((zvrf = vrf_iter2info (iter)) == NULL || + (table = zvrf->table[AFI_IP][SAFI_UNICAST]) == NULL) + continue; + + /* Show matched type IPv4 routes. */ + for (rn = route_top (table); rn; rn = route_next (rn)) + RNODE_FOREACH_RIB (rn, rib) + if (rib->type == type) + { + if (first) + { + vty_out (vty, SHOW_ROUTE_V4_HEADER); + first = 0; + } + vty_show_ip_route (vty, rn, rib); + } + } + + return CMD_SUCCESS; +} + +DEFUN (show_ip_route_addr_vrf_all, + show_ip_route_addr_vrf_all_cmd, + "show ip route A.B.C.D " VRF_ALL_CMD_STR, + SHOW_STR + IP_STR + "IP routing table\n" + "Network in the IP routing table to display\n" + VRF_ALL_CMD_HELP_STR) +{ + int ret; + struct prefix_ipv4 p; + struct route_table *table; + struct route_node *rn; + struct zebra_vrf *zvrf; + vrf_iter_t iter; + + ret = str2prefix_ipv4 (argv[0], &p); + if (ret <= 0) + { + vty_out (vty, "%% Malformed IPv4 address%s", VTY_NEWLINE); + return CMD_WARNING; + } + + for (iter = vrf_first (); iter != VRF_ITER_INVALID; iter = vrf_next (iter)) + { + if ((zvrf = vrf_iter2info (iter)) == NULL || + (table = zvrf->table[AFI_IP][SAFI_UNICAST]) == NULL) + continue; + + rn = route_node_match (table, (struct prefix *) &p); + if (! rn) + continue; + + vty_show_ip_route_detail (vty, rn, 0); + + route_unlock_node (rn); + } + + return CMD_SUCCESS; +} + +DEFUN (show_ip_route_prefix_vrf_all, + show_ip_route_prefix_vrf_all_cmd, + "show ip route A.B.C.D/M " VRF_ALL_CMD_STR, + SHOW_STR + IP_STR + "IP routing table\n" + "IP prefix /, e.g., 35.0.0.0/8\n" + VRF_ALL_CMD_HELP_STR) +{ + int ret; + struct prefix_ipv4 p; + struct route_table *table; + struct route_node *rn; + struct zebra_vrf *zvrf; + vrf_iter_t iter; + + ret = str2prefix_ipv4 (argv[0], &p); + if (ret <= 0) + { + vty_out (vty, "%% Malformed IPv4 address%s", VTY_NEWLINE); + return CMD_WARNING; + } - uptime = time (NULL); - uptime -= rib->uptime; - tm = gmtime (&uptime); + for (iter = vrf_first (); iter != VRF_ITER_INVALID; iter = vrf_next (iter)) + { + if ((zvrf = vrf_iter2info (iter)) == NULL || + (table = zvrf->table[AFI_IP][SAFI_UNICAST]) == NULL) + continue; + + rn = route_node_match (table, (struct prefix *) &p); + if (! rn) + continue; + if (rn->p.prefixlen != p.prefixlen) + { + route_unlock_node (rn); + continue; + } - vty_out (vty, " Last update "); + vty_show_ip_route_detail (vty, rn, 0); - if (uptime < ONE_DAY_SECOND) - vty_out (vty, "%02d:%02d:%02d", - tm->tm_hour, tm->tm_min, tm->tm_sec); - else if (uptime < ONE_WEEK_SECOND) - vty_out (vty, "%dd%02dh%02dm", - tm->tm_yday, tm->tm_hour, tm->tm_min); - else - vty_out (vty, "%02dw%dd%02dh", - tm->tm_yday/7, - tm->tm_yday - ((tm->tm_yday/7) * 7), tm->tm_hour); - vty_out (vty, " ago%s", VTY_NEWLINE); - } + route_unlock_node (rn); + } - for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) - { - char addrstr[32]; + return CMD_SUCCESS; +} - vty_out (vty, " %c", - CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB) ? '*' : ' '); +DEFUN (show_ip_route_summary_vrf_all, + show_ip_route_summary_vrf_all_cmd, + "show ip route summary " VRF_ALL_CMD_STR, + SHOW_STR + IP_STR + "IP routing table\n" + "Summary of all routes\n" + VRF_ALL_CMD_HELP_STR) +{ + struct zebra_vrf *zvrf; + vrf_iter_t iter; - switch (nexthop->type) - { - case NEXTHOP_TYPE_IPV4: - case NEXTHOP_TYPE_IPV4_IFINDEX: - vty_out (vty, " %s", inet_ntoa (nexthop->gate.ipv4)); - if (nexthop->ifindex) - vty_out (vty, ", via %s", ifindex2ifname (nexthop->ifindex)); - break; - case NEXTHOP_TYPE_IFINDEX: - vty_out (vty, " directly connected, %s", - ifindex2ifname (nexthop->ifindex)); - break; - case NEXTHOP_TYPE_IFNAME: - vty_out (vty, " directly connected, %s", nexthop->ifname); - break; - case NEXTHOP_TYPE_BLACKHOLE: - vty_out (vty, " directly connected, Null0"); - break; - default: - break; - } - if (! CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE)) - vty_out (vty, " inactive"); + for (iter = vrf_first (); iter != VRF_ITER_INVALID; iter = vrf_next (iter)) + if ((zvrf = vrf_iter2info (iter)) != NULL) + vty_show_ip_route_summary (vty, zvrf->table[AFI_IP][SAFI_UNICAST]); - 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; - } - } - switch (nexthop->type) - { - case NEXTHOP_TYPE_IPV4: - case NEXTHOP_TYPE_IPV4_IFINDEX: - case NEXTHOP_TYPE_IPV4_IFNAME: - if (nexthop->src.ipv4.s_addr) - { - if (inet_ntop(AF_INET, &nexthop->src.ipv4, addrstr, - sizeof addrstr)) - vty_out (vty, ", src %s", addrstr); - } - break; -#ifdef HAVE_IPV6 - case NEXTHOP_TYPE_IPV6: - case NEXTHOP_TYPE_IPV6_IFINDEX: - case NEXTHOP_TYPE_IPV6_IFNAME: - if (!IPV6_ADDR_SAME(&nexthop->src.ipv6, &in6addr_any)) - { - if (inet_ntop(AF_INET6, &nexthop->src.ipv6, addrstr, - sizeof addrstr)) - vty_out (vty, ", src %s", addrstr); - } - break; -#endif /* HAVE_IPV6 */ - default: - break; - } - vty_out (vty, "%s", VTY_NEWLINE); - } - vty_out (vty, "%s", VTY_NEWLINE); - } + return CMD_SUCCESS; } -static void -vty_show_ip_route (struct vty *vty, struct route_node *rn, struct rib *rib) +DEFUN (show_ip_route_summary_prefix_vrf_all, + show_ip_route_summary_prefix_vrf_all_cmd, + "show ip route summary prefix " VRF_ALL_CMD_STR, + SHOW_STR + IP_STR + "IP routing table\n" + "Summary of all routes\n" + "Prefix routes\n" + VRF_ALL_CMD_HELP_STR) { - struct nexthop *nexthop; - int len = 0; - char buf[BUFSIZ]; + struct zebra_vrf *zvrf; + vrf_iter_t iter; - /* Nexthop information. */ - for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) + for (iter = vrf_first (); iter != VRF_ITER_INVALID; iter = vrf_next (iter)) + if ((zvrf = vrf_iter2info (iter)) != NULL) + vty_show_ip_route_summary_prefix (vty, zvrf->table[AFI_IP][SAFI_UNICAST]); + + return CMD_SUCCESS; +} + +/* Write IPv4 static route configuration. */ +static int +static_config_ipv4 (struct vty *vty, safi_t safi, const char *cmd) +{ + struct route_node *rn; + struct static_route *si; + struct route_table *stable; + struct zebra_vrf *zvrf; + vrf_iter_t iter; + int write; + + write = 0; + + for (iter = vrf_first (); iter != VRF_ITER_INVALID; iter = vrf_next (iter)) { - if (nexthop == rib->nexthop) - { - /* Prefix information. */ - len = vty_out (vty, "%c%c%c %s/%d", - zebra_route_char (rib->type), - CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED) - ? '>' : ' ', - CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB) - ? '*' : ' ', - inet_ntop (AF_INET, &rn->p.u.prefix, buf, BUFSIZ), - rn->p.prefixlen); - - /* Distance and metric display. */ - if (rib->type != ZEBRA_ROUTE_CONNECT - && rib->type != ZEBRA_ROUTE_KERNEL) - len += vty_out (vty, " [%d/%d]", rib->distance, - rib->metric); - } - else - vty_out (vty, " %c%*c", - CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB) - ? '*' : ' ', - len - 3, ' '); + if ((zvrf = vrf_iter2info (iter)) == NULL || + (stable = zvrf->stable[AFI_IP][safi]) == NULL) + continue; - switch (nexthop->type) - { - case NEXTHOP_TYPE_IPV4: - case NEXTHOP_TYPE_IPV4_IFINDEX: - vty_out (vty, " via %s", inet_ntoa (nexthop->gate.ipv4)); - if (nexthop->ifindex) - vty_out (vty, ", %s", ifindex2ifname (nexthop->ifindex)); - break; - case NEXTHOP_TYPE_IFINDEX: - vty_out (vty, " is directly connected, %s", - ifindex2ifname (nexthop->ifindex)); - break; - case NEXTHOP_TYPE_IFNAME: - vty_out (vty, " is directly connected, %s", nexthop->ifname); - break; - case NEXTHOP_TYPE_BLACKHOLE: - vty_out (vty, " is directly connected, Null0"); - break; - default: - break; - } - if (! CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE)) - vty_out (vty, " inactive"); + for (rn = route_top (stable); rn; rn = route_next (rn)) + for (si = rn->info; si; si = si->next) + { + vty_out (vty, "%s %s/%d", cmd, inet_ntoa (rn->p.u.prefix4), + rn->p.prefixlen); - 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; - } - } - switch (nexthop->type) - { - case NEXTHOP_TYPE_IPV4: - case NEXTHOP_TYPE_IPV4_IFINDEX: - case NEXTHOP_TYPE_IPV4_IFNAME: - if (nexthop->src.ipv4.s_addr) + switch (si->type) { - if (inet_ntop(AF_INET, &nexthop->src.ipv4, buf, sizeof buf)) - vty_out (vty, ", src %s", buf); + case STATIC_IPV4_GATEWAY: + vty_out (vty, " %s", inet_ntoa (si->addr.ipv4)); + break; + case STATIC_IPV4_IFNAME: + vty_out (vty, " %s", si->ifname); + break; + case STATIC_IPV4_BLACKHOLE: + vty_out (vty, " Null0"); + break; } - break; -#ifdef HAVE_IPV6 - case NEXTHOP_TYPE_IPV6: - case NEXTHOP_TYPE_IPV6_IFINDEX: - case NEXTHOP_TYPE_IPV6_IFNAME: - if (!IPV6_ADDR_SAME(&nexthop->src.ipv6, &in6addr_any)) + + /* flags are incompatible with STATIC_IPV4_BLACKHOLE */ + if (si->type != STATIC_IPV4_BLACKHOLE) { - if (inet_ntop(AF_INET6, &nexthop->src.ipv6, buf, sizeof buf)) - vty_out (vty, ", src %s", buf); + if (CHECK_FLAG(si->flags, ZEBRA_FLAG_REJECT)) + vty_out (vty, " %s", "reject"); + + if (CHECK_FLAG(si->flags, ZEBRA_FLAG_BLACKHOLE)) + vty_out (vty, " %s", "blackhole"); } - break; -#endif /* HAVE_IPV6 */ - default: - break; - } - if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_BLACKHOLE)) - vty_out (vty, ", bh"); - if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_REJECT)) - vty_out (vty, ", rej"); + if (si->tag) + vty_out (vty, " tag %d", si->tag); - if (rib->type == ZEBRA_ROUTE_RIP - || rib->type == ZEBRA_ROUTE_OSPF - || rib->type == ZEBRA_ROUTE_BABEL - || rib->type == ZEBRA_ROUTE_ISIS - || rib->type == ZEBRA_ROUTE_BGP) + if (si->distance != ZEBRA_STATIC_DISTANCE_DEFAULT) + vty_out (vty, " %d", si->distance); + + if (si->vrf_id != VRF_DEFAULT) + vty_out (vty, " vrf %u", si->vrf_id); + + vty_out (vty, "%s", VTY_NEWLINE); + + write = 1; + } + } + return write; +} + +DEFUN (show_ip_protocol, + show_ip_protocol_cmd, + "show ip protocol", + SHOW_STR + IP_STR + "IP protocol filtering status\n") +{ + int i; + + vty_out(vty, "Protocol : route-map %s", VTY_NEWLINE); + vty_out(vty, "------------------------%s", VTY_NEWLINE); + for (i=0;iuptime; - tm = gmtime (&uptime); + /* VRF id */ + if (vrf_id_str) + VTY_GET_INTEGER ("VRF ID", vrf_id, vrf_id_str); -#define ONE_DAY_SECOND 60*60*24 -#define ONE_WEEK_SECOND 60*60*24*7 + if (add_cmd) + static_add_ipv6 (&p, type, gate, ifname, flag, tag, distance, vrf_id); + else + static_delete_ipv6 (&p, type, gate, ifname, tag, distance, vrf_id); - if (uptime < ONE_DAY_SECOND) - vty_out (vty, ", %02d:%02d:%02d", - tm->tm_hour, tm->tm_min, tm->tm_sec); - else if (uptime < ONE_WEEK_SECOND) - vty_out (vty, ", %dd%02dh%02dm", - tm->tm_yday, tm->tm_hour, tm->tm_min); - else - vty_out (vty, ", %02dw%dd%02dh", - tm->tm_yday/7, - tm->tm_yday - ((tm->tm_yday/7) * 7), tm->tm_hour); - } - vty_out (vty, "%s", VTY_NEWLINE); - } + return CMD_SUCCESS; } -DEFUN (show_ip_route, - show_ip_route_cmd, - "show ip route", - SHOW_STR +DEFUN (ipv6_route, + ipv6_route_cmd, + "ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE)", IP_STR - "IP routing table\n") + "Establish static routes\n" + "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" + "IPv6 gateway address\n" + "IPv6 gateway interface name\n") { - struct route_table *table; - struct route_node *rn; - struct rib *rib; - int first = 1; + return static_ipv6_func (vty, 1, argv[0], argv[1], NULL, NULL, NULL, + NULL, NULL); +} - table = vrf_table (AFI_IP, SAFI_UNICAST, 0); - if (! table) - return CMD_SUCCESS; +DEFUN (ipv6_route_tag, + ipv6_route_tag_cmd, + "ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) tag <1-4294967295>", + IP_STR + "Establish static routes\n" + "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" + "IPv6 gateway address\n" + "IPv6 gateway interface name\n" + "Set tag for this route\n" + "Tag value\n") +{ + return static_ipv6_func (vty, 1, argv[0], argv[1], NULL, NULL, argv[2], NULL, NULL); +} - /* Show all IPv4 routes. */ - for (rn = route_top (table); rn; rn = route_next (rn)) - for (rib = rn->info; rib; rib = rib->next) - { - if (first) - { - vty_out (vty, SHOW_ROUTE_V4_HEADER); - first = 0; - } - vty_show_ip_route (vty, rn, rib); - } - return CMD_SUCCESS; +DEFUN (ipv6_route_tag_vrf, + ipv6_route_tag_vrf_cmd, + "ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) tag <1-4294967295>" VRF_CMD_STR, + IP_STR + "Establish static routes\n" + "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" + "IPv6 gateway address\n" + "IPv6 gateway interface name\n" + "Set tag for this route\n" + "Tag value\n" + VRF_CMD_HELP_STR) +{ + return static_ipv6_func (vty, 1, argv[0], argv[1], NULL, NULL, argv[2], NULL, argv[3]); } -DEFUN (show_ip_route_prefix_longer, - show_ip_route_prefix_longer_cmd, - "show ip route A.B.C.D/M longer-prefixes", - SHOW_STR +DEFUN (ipv6_route_flags, + ipv6_route_flags_cmd, + "ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) (reject|blackhole)", IP_STR - "IP routing table\n" - "IP prefix /, e.g., 35.0.0.0/8\n" - "Show route matching the specified Network/Mask pair only\n") + "Establish static routes\n" + "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" + "IPv6 gateway address\n" + "IPv6 gateway interface name\n" + "Emit an ICMP unreachable when matched\n" + "Silently discard pkts when matched\n") { - struct route_table *table; - struct route_node *rn; - struct rib *rib; - struct prefix p; - int ret; - int first = 1; + return static_ipv6_func (vty, 1, argv[0], argv[1], NULL, argv[2], NULL, + NULL, NULL); +} - ret = str2prefix (argv[0], &p); - if (! ret) - { - vty_out (vty, "%% Malformed Prefix%s", VTY_NEWLINE); - return CMD_WARNING; - } - - table = vrf_table (AFI_IP, SAFI_UNICAST, 0); - if (! table) - return CMD_SUCCESS; +DEFUN (ipv6_route_flags_tag, + ipv6_route_flags_tag_cmd, + "ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) (reject|blackhole) tag <1-4294967295>", + IP_STR + "Establish static routes\n" + "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" + "IPv6 gateway address\n" + "IPv6 gateway interface name\n" + "Emit an ICMP unreachable when matched\n" + "Silently discard pkts when matched\n" + "Set tag for this route\n" + "Tag value\n") +{ + return static_ipv6_func (vty, 1, argv[0], argv[1], NULL, argv[2], argv[3], NULL, NULL); +} - /* Show matched type IPv4 routes. */ - for (rn = route_top (table); rn; rn = route_next (rn)) - for (rib = rn->info; rib; rib = rib->next) - if (prefix_match (&p, &rn->p)) - { - if (first) - { - vty_out (vty, SHOW_ROUTE_V4_HEADER); - first = 0; - } - vty_show_ip_route (vty, rn, rib); - } - return CMD_SUCCESS; +DEFUN (ipv6_route_flags_tag_vrf, + ipv6_route_flags_tag_vrf_cmd, + "ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) (reject|blackhole) tag <1-4294967295>" VRF_CMD_STR, + IP_STR + "Establish static routes\n" + "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" + "IPv6 gateway address\n" + "IPv6 gateway interface name\n" + "Emit an ICMP unreachable when matched\n" + "Silently discard pkts when matched\n" + "Set tag for this route\n" + "Tag value\n" + VRF_CMD_HELP_STR) +{ + return static_ipv6_func (vty, 1, argv[0], argv[1], NULL, argv[2], argv[3], NULL, argv[4]); } -DEFUN (show_ip_route_supernets, - show_ip_route_supernets_cmd, - "show ip route supernets-only", - SHOW_STR +DEFUN (ipv6_route_ifname, + ipv6_route_ifname_cmd, + "ipv6 route X:X::X:X/M X:X::X:X INTERFACE", IP_STR - "IP routing table\n" - "Show supernet entries only\n") + "Establish static routes\n" + "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" + "IPv6 gateway address\n" + "IPv6 gateway interface name\n") { - struct route_table *table; - struct route_node *rn; - struct rib *rib; - u_int32_t addr; - int first = 1; + return static_ipv6_func (vty, 1, argv[0], argv[1], argv[2], NULL, NULL, + NULL, NULL); +} - table = vrf_table (AFI_IP, SAFI_UNICAST, 0); - if (! table) - return CMD_SUCCESS; +DEFUN (ipv6_route_ifname_tag, + ipv6_route_ifname_tag_cmd, + "ipv6 route X:X::X:X/M X:X::X:X INTERFACE tag <1-4294967295>", + IP_STR + "Establish static routes\n" + "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" + "IPv6 gateway address\n" + "IPv6 gateway interface name\n" + "Set tag for this route\n" + "Tag value\n") +{ + return static_ipv6_func (vty, 1, argv[0], argv[1], argv[2], NULL, argv[3], NULL, NULL); +} - /* Show matched type IPv4 routes. */ - for (rn = route_top (table); rn; rn = route_next (rn)) - for (rib = rn->info; rib; rib = rib->next) - { - addr = ntohl (rn->p.u.prefix4.s_addr); +DEFUN (ipv6_route_ifname_tag_vrf, + ipv6_route_ifname_tag_vrf_cmd, + "ipv6 route X:X::X:X/M X:X::X:X INTERFACE tag <1-4294967295>" VRF_CMD_STR, + IP_STR + "Establish static routes\n" + "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" + "IPv6 gateway address\n" + "IPv6 gateway interface name\n" + "Set tag for this route\n" + "Tag value\n" + VRF_CMD_HELP_STR) +{ + return static_ipv6_func (vty, 1, argv[0], argv[1], argv[2], NULL, argv[3], NULL, argv[4]); +} - if ((IN_CLASSC (addr) && rn->p.prefixlen < 24) - || (IN_CLASSB (addr) && rn->p.prefixlen < 16) - || (IN_CLASSA (addr) && rn->p.prefixlen < 8)) - { - if (first) - { - vty_out (vty, SHOW_ROUTE_V4_HEADER); - first = 0; - } - vty_show_ip_route (vty, rn, rib); - } - } - return CMD_SUCCESS; +DEFUN (ipv6_route_ifname_flags, + ipv6_route_ifname_flags_cmd, + "ipv6 route X:X::X:X/M X:X::X:X INTERFACE (reject|blackhole)", + IP_STR + "Establish static routes\n" + "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" + "IPv6 gateway address\n" + "IPv6 gateway interface name\n" + "Emit an ICMP unreachable when matched\n" + "Silently discard pkts when matched\n") +{ + return static_ipv6_func (vty, 1, argv[0], argv[1], argv[2], argv[3], NULL, + NULL, NULL); } -DEFUN (show_ip_route_protocol, - show_ip_route_protocol_cmd, - "show ip route " QUAGGA_IP_REDIST_STR_ZEBRA, - SHOW_STR +DEFUN (ipv6_route_ifname_flags_tag, + ipv6_route_ifname_flags_tag_cmd, + "ipv6 route X:X::X:X/M X:X::X:X INTERFACE (reject|blackhole) tag <1-4294967295>", IP_STR - "IP routing table\n" - QUAGGA_IP_REDIST_HELP_STR_ZEBRA) + "Establish static routes\n" + "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" + "IPv6 gateway address\n" + "IPv6 gateway interface name\n" + "Emit an ICMP unreachable when matched\n" + "Silently discard pkts when matched\n" + "Set tag for this route\n" + "Tag value\n") { - int type; - struct route_table *table; - struct route_node *rn; - struct rib *rib; - int first = 1; + return static_ipv6_func (vty, 1, argv[0], argv[1], argv[2], argv[3], argv[4], NULL, NULL); +} - type = proto_redistnum (AFI_IP, argv[0]); - if (type < 0) - { - vty_out (vty, "Unknown route type%s", VTY_NEWLINE); - return CMD_WARNING; - } - - table = vrf_table (AFI_IP, SAFI_UNICAST, 0); - if (! table) - return CMD_SUCCESS; +DEFUN (ipv6_route_ifname_flags_tag_vrf, + ipv6_route_ifname_flags_tag_vrf_cmd, + "ipv6 route X:X::X:X/M X:X::X:X INTERFACE (reject|blackhole) tag <1-4294967295>" VRF_CMD_STR, + IP_STR + "Establish static routes\n" + "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" + "IPv6 gateway address\n" + "IPv6 gateway interface name\n" + "Emit an ICMP unreachable when matched\n" + "Silently discard pkts when matched\n" + "Set tag for this route\n" + "Tag value\n" + VRF_CMD_HELP_STR) +{ + return static_ipv6_func (vty, 1, argv[0], argv[1], argv[2], argv[3], argv[4], NULL, argv[5]); +} - /* Show matched type IPv4 routes. */ - for (rn = route_top (table); rn; rn = route_next (rn)) - for (rib = rn->info; rib; rib = rib->next) - if (rib->type == type) - { - if (first) - { - vty_out (vty, SHOW_ROUTE_V4_HEADER); - first = 0; - } - vty_show_ip_route (vty, rn, rib); - } - return CMD_SUCCESS; +DEFUN (ipv6_route_pref, + ipv6_route_pref_cmd, + "ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) <1-255>", + IP_STR + "Establish static routes\n" + "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" + "IPv6 gateway address\n" + "IPv6 gateway interface name\n" + "Distance value for this prefix\n") +{ + return static_ipv6_func (vty, 1, argv[0], argv[1], NULL, NULL, NULL, argv[2], + NULL); +} + +DEFUN (ipv6_route_pref_tag, + ipv6_route_pref_tag_cmd, + "ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) tag <1-4294967295> <1-255>", + IP_STR + "Establish static routes\n" + "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" + "IPv6 gateway address\n" + "IPv6 gateway interface name\n" + "Set tag for this route\n" + "Tag value\n" + "Distance value for this prefix\n") +{ + return static_ipv6_func (vty, 1, argv[0], argv[1], NULL, NULL, argv[2], argv[3], NULL); } -DEFUN (show_ip_route_addr, - show_ip_route_addr_cmd, - "show ip route A.B.C.D", - SHOW_STR +DEFUN (ipv6_route_pref_tag_vrf, + ipv6_route_pref_tag_vrf_cmd, + "ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) tag <1-4294967295> <1-255>" VRF_CMD_STR, IP_STR - "IP routing table\n" - "Network in the IP routing table to display\n") + "Establish static routes\n" + "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" + "IPv6 gateway address\n" + "IPv6 gateway interface name\n" + "Set tag for this route\n" + "Tag value\n" + "Distance value for this prefix\n" + VRF_CMD_HELP_STR) { - int ret; - struct prefix_ipv4 p; - struct route_table *table; - struct route_node *rn; - - ret = str2prefix_ipv4 (argv[0], &p); - if (ret <= 0) - { - vty_out (vty, "%% Malformed IPv4 address%s", VTY_NEWLINE); - return CMD_WARNING; - } - - table = vrf_table (AFI_IP, SAFI_UNICAST, 0); - if (! table) - return CMD_SUCCESS; + return static_ipv6_func (vty, 1, argv[0], argv[1], NULL, NULL, argv[2], argv[3], argv[4]); +} - rn = route_node_match (table, (struct prefix *) &p); - if (! rn) - { - vty_out (vty, "%% Network not in table%s", VTY_NEWLINE); - return CMD_WARNING; - } +DEFUN (ipv6_route_flags_pref, + ipv6_route_flags_pref_cmd, + "ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) (reject|blackhole) <1-255>", + IP_STR + "Establish static routes\n" + "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" + "IPv6 gateway address\n" + "IPv6 gateway interface name\n" + "Emit an ICMP unreachable when matched\n" + "Silently discard pkts when matched\n" + "Distance value for this prefix\n") +{ + return static_ipv6_func (vty, 1, argv[0], argv[1], NULL, argv[2], NULL, argv[3], + NULL); +} - vty_show_ip_route_detail (vty, rn); +DEFUN (ipv6_route_flags_pref_tag, + ipv6_route_flags_pref_tag_cmd, + "ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) (reject|blackhole) tag <1-4294967295> <1-255>", + IP_STR + "Establish static routes\n" + "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" + "IPv6 gateway address\n" + "IPv6 gateway interface name\n" + "Emit an ICMP unreachable when matched\n" + "Silently discard pkts when matched\n" + "Set tag for this route\n" + "Tag value\n" + "Distance value for this prefix\n") +{ + return static_ipv6_func (vty, 1, argv[0], argv[1], NULL, argv[2], argv[3], argv[4], NULL); +} - route_unlock_node (rn); +DEFUN (ipv6_route_flags_pref_tag_vrf, + ipv6_route_flags_pref_tag_vrf_cmd, + "ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) (reject|blackhole) tag <1-4294967295> <1-255>" VRF_CMD_STR, + IP_STR + "Establish static routes\n" + "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" + "IPv6 gateway address\n" + "IPv6 gateway interface name\n" + "Emit an ICMP unreachable when matched\n" + "Silently discard pkts when matched\n" + "Set tag for this route\n" + "Tag value\n" + "Distance value for this prefix\n" + VRF_CMD_HELP_STR) +{ + return static_ipv6_func (vty, 1, argv[0], argv[1], NULL, argv[2], argv[3], argv[4], argv[5]); +} - return CMD_SUCCESS; +DEFUN (ipv6_route_ifname_pref, + ipv6_route_ifname_pref_cmd, + "ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>", + IP_STR + "Establish static routes\n" + "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" + "IPv6 gateway address\n" + "IPv6 gateway interface name\n" + "Distance value for this prefix\n") +{ + return static_ipv6_func (vty, 1, argv[0], argv[1], argv[2], NULL, NULL, argv[3], + NULL); } -DEFUN (show_ip_route_prefix, - show_ip_route_prefix_cmd, - "show ip route A.B.C.D/M", - SHOW_STR +DEFUN (ipv6_route_ifname_pref_tag, + ipv6_route_ifname_pref_tag_cmd, + "ipv6 route X:X::X:X/M X:X::X:X INTERFACE tag <1-4294967295> <1-255>", IP_STR - "IP routing table\n" - "IP prefix /, e.g., 35.0.0.0/8\n") + "Establish static routes\n" + "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" + "IPv6 gateway address\n" + "IPv6 gateway interface name\n" + "Set tag for this route\n" + "Tag value\n" + "Distance value for this prefix\n") { - int ret; - struct prefix_ipv4 p; - struct route_table *table; - struct route_node *rn; + return static_ipv6_func (vty, 1, argv[0], argv[1], argv[2], NULL, argv[3], argv[4], NULL); +} - ret = str2prefix_ipv4 (argv[0], &p); - if (ret <= 0) - { - vty_out (vty, "%% Malformed IPv4 address%s", VTY_NEWLINE); - return CMD_WARNING; - } +DEFUN (ipv6_route_ifname_pref_tag_vrf, + ipv6_route_ifname_pref_tag_vrf_cmd, + "ipv6 route X:X::X:X/M X:X::X:X INTERFACE tag <1-4294967295> <1-255>" VRF_CMD_STR, + IP_STR + "Establish static routes\n" + "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" + "IPv6 gateway address\n" + "IPv6 gateway interface name\n" + "Set tag for this route\n" + "Tag value\n" + "Distance value for this prefix\n" + VRF_CMD_HELP_STR) +{ + return static_ipv6_func (vty, 1, argv[0], argv[1], argv[2], NULL, argv[3], argv[4], argv[5]); +} - table = vrf_table (AFI_IP, SAFI_UNICAST, 0); - if (! table) - return CMD_SUCCESS; +DEFUN (ipv6_route_ifname_flags_pref, + ipv6_route_ifname_flags_pref_cmd, + "ipv6 route X:X::X:X/M X:X::X:X INTERFACE (reject|blackhole) <1-255>", + IP_STR + "Establish static routes\n" + "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" + "IPv6 gateway address\n" + "IPv6 gateway interface name\n" + "Emit an ICMP unreachable when matched\n" + "Silently discard pkts when matched\n" + "Distance value for this prefix\n") +{ + return static_ipv6_func (vty, 1, argv[0], argv[1], argv[2], argv[3], NULL, argv[4], + NULL); +} - rn = route_node_match (table, (struct prefix *) &p); - if (! rn || rn->p.prefixlen != p.prefixlen) - { - vty_out (vty, "%% Network not in table%s", VTY_NEWLINE); - return CMD_WARNING; - } +DEFUN (ipv6_route_ifname_flags_pref_tag, + ipv6_route_ifname_flags_pref_tag_cmd, + "ipv6 route X:X::X:X/M X:X::X:X INTERFACE (reject|blackhole) tag <1-4294967295> <1-255>", + IP_STR + "Establish static routes\n" + "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" + "IPv6 gateway address\n" + "IPv6 gateway interface name\n" + "Emit an ICMP unreachable when matched\n" + "Silently discard pkts when matched\n" + "Set tag for this route\n" + "Tag value\n" + "Distance value for this prefix\n") +{ + return static_ipv6_func (vty, 1, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], NULL); +} - vty_show_ip_route_detail (vty, rn); +DEFUN (ipv6_route_ifname_flags_pref_tag_vrf, + ipv6_route_ifname_flags_pref_tag_vrf_cmd, + "ipv6 route X:X::X:X/M X:X::X:X INTERFACE (reject|blackhole) tag <1-4294967295> <1-255>" VRF_CMD_STR, + IP_STR + "Establish static routes\n" + "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" + "IPv6 gateway address\n" + "IPv6 gateway interface name\n" + "Emit an ICMP unreachable when matched\n" + "Silently discard pkts when matched\n" + "Set tag for this route\n" + "Tag value\n" + "Distance value for this prefix\n" + VRF_CMD_HELP_STR) +{ + return static_ipv6_func (vty, 1, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6]); +} - route_unlock_node (rn); +DEFUN (no_ipv6_route, + no_ipv6_route_cmd, + "no ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE)", + NO_STR + IP_STR + "Establish static routes\n" + "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" + "IPv6 gateway address\n" + "IPv6 gateway interface name\n") +{ + return static_ipv6_func (vty, 0, argv[0], argv[1], NULL, NULL, NULL, NULL, + NULL); +} - return CMD_SUCCESS; +DEFUN (no_ipv6_route_tag, + no_ipv6_route_tag_cmd, + "no ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) tag <1-4294967295>", + NO_STR + IP_STR + "Establish static routes\n" + "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" + "IPv6 gateway address\n" + "IPv6 gateway interface name\n" + "Set tag for this route\n" + "Tag value\n") +{ + return static_ipv6_func (vty, 0, argv[0], argv[1], NULL, NULL, argv[2], NULL, NULL); } -static void -vty_show_ip_route_summary (struct vty *vty, struct route_table *table) +DEFUN (no_ipv6_route_tag_vrf, + no_ipv6_route_tag_vrf_cmd, + "no ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) tag <1-4294967295>" VRF_CMD_STR, + NO_STR + IP_STR + "Establish static routes\n" + "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" + "IPv6 gateway address\n" + "IPv6 gateway interface name\n" + "Set tag for this route\n" + "Tag value\n" + VRF_CMD_HELP_STR) { - 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; + return static_ipv6_func (vty, 0, argv[0], argv[1], NULL, NULL, argv[2], NULL, argv[3]); +} - 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) - 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)) - { - 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]++; - } - } +ALIAS (no_ipv6_route, + no_ipv6_route_flags_cmd, + "no ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) (reject|blackhole)", + NO_STR + IP_STR + "Establish static routes\n" + "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" + "IPv6 gateway address\n" + "IPv6 gateway interface name\n" + "Emit an ICMP unreachable when matched\n" + "Silently discard pkts when matched\n") - vty_out (vty, "%-20s %-20s %-20s %s", - "Route Source", "Routes", "FIB", VTY_NEWLINE); +ALIAS (no_ipv6_route_tag, + no_ipv6_route_flags_tag_cmd, + "no ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) (reject|blackhole) tag <1-4294967295>", + NO_STR + IP_STR + "Establish static routes\n" + "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" + "IPv6 gateway address\n" + "IPv6 gateway interface name\n" + "Emit an ICMP unreachable when matched\n" + "Silently discard pkts when matched\n" + "Set tag for this route\n" + "Tag value\n") - 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); - } - } +DEFUN (no_ipv6_route_ifname, + no_ipv6_route_ifname_cmd, + "no ipv6 route X:X::X:X/M X:X::X:X INTERFACE", + NO_STR + IP_STR + "Establish static routes\n" + "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" + "IPv6 gateway address\n" + "IPv6 gateway interface name\n") +{ + return static_ipv6_func (vty, 0, argv[0], argv[1], argv[2], NULL, NULL, NULL, + NULL); +} - 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); +DEFUN (no_ipv6_route_ifname_tag, + no_ipv6_route_ifname_tag_cmd, + "no ipv6 route X:X::X:X/M X:X::X:X INTERFACE tag <1-4294967295>", + NO_STR + IP_STR + "Establish static routes\n" + "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" + "IPv6 gateway address\n" + "IPv6 gateway interface name\n" + "Set tag for this route\n" + "Tag value\n") +{ + return static_ipv6_func (vty, 0, argv[0], argv[1], argv[2], NULL, argv[3], NULL, NULL); } -/* Show route summary. */ -DEFUN (show_ip_route_summary, - show_ip_route_summary_cmd, - "show ip route summary", - SHOW_STR +DEFUN (no_ipv6_route_ifname_tag_vrf, + no_ipv6_route_ifname_tag_vrf_cmd, + "no ipv6 route X:X::X:X/M X:X::X:X INTERFACE tag <1-4294967295>" VRF_CMD_STR, + NO_STR IP_STR - "IP routing table\n" - "Summary of all routes\n") + "Establish static routes\n" + "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" + "IPv6 gateway address\n" + "IPv6 gateway interface name\n" + "Set tag for this route\n" + "Tag value\n" + VRF_CMD_HELP_STR) { - struct route_table *table; + return static_ipv6_func (vty, 0, argv[0], argv[1], argv[2], NULL, argv[3], NULL, argv[4]); +} - table = vrf_table (AFI_IP, SAFI_UNICAST, 0); - if (! table) - return CMD_SUCCESS; +ALIAS (no_ipv6_route_ifname, + no_ipv6_route_ifname_flags_cmd, + "no ipv6 route X:X::X:X/M X:X::X:X INTERFACE (reject|blackhole)", + NO_STR + IP_STR + "Establish static routes\n" + "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" + "IPv6 gateway address\n" + "IPv6 gateway interface name\n" + "Emit an ICMP unreachable when matched\n" + "Silently discard pkts when matched\n") - vty_show_ip_route_summary (vty, table); +ALIAS (no_ipv6_route_ifname_tag, + no_ipv6_route_ifname_flags_tag_cmd, + "no ipv6 route X:X::X:X/M X:X::X:X INTERFACE (reject|blackhole) tag <1-4294967295>", + NO_STR + IP_STR + "Establish static routes\n" + "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" + "IPv6 gateway address\n" + "IPv6 gateway interface name\n" + "Emit an ICMP unreachable when matched\n" + "Silently discard pkts when matched\n" + "Set tag for this route\n" + "Tag value\n") - return CMD_SUCCESS; +DEFUN (no_ipv6_route_pref, + no_ipv6_route_pref_cmd, + "no ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) <1-255>", + NO_STR + IP_STR + "Establish static routes\n" + "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" + "IPv6 gateway address\n" + "IPv6 gateway interface name\n" + "Distance value for this prefix\n") +{ + return static_ipv6_func (vty, 0, argv[0], argv[1], NULL, NULL, NULL, argv[2], + NULL); } -/* Write IPv4 static route configuration. */ -static int -static_config_ipv4 (struct vty *vty) +DEFUN (no_ipv6_route_pref_tag, + no_ipv6_route_pref_tag_cmd, + "no ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) tag <1-4294967295> <1-255>", + NO_STR + IP_STR + "Establish static routes\n" + "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" + "IPv6 gateway address\n" + "IPv6 gateway interface name\n" + "Set tag for this route\n" + "Tag value\n" + "Distance value for this prefix\n") { - struct route_node *rn; - struct static_ipv4 *si; - struct route_table *stable; - int write; - - write = 0; - - /* Lookup table. */ - stable = vrf_static_table (AFI_IP, SAFI_UNICAST, 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), - rn->p.prefixlen); - - switch (si->type) - { - case STATIC_IPV4_GATEWAY: - vty_out (vty, " %s", inet_ntoa (si->gate.ipv4)); - break; - case STATIC_IPV4_IFNAME: - vty_out (vty, " %s", si->gate.ifname); - break; - case STATIC_IPV4_BLACKHOLE: - vty_out (vty, " Null0"); - break; - } - - /* flags are incompatible with STATIC_IPV4_BLACKHOLE */ - if (si->type != STATIC_IPV4_BLACKHOLE) - { - if (CHECK_FLAG(si->flags, ZEBRA_FLAG_REJECT)) - vty_out (vty, " %s", "reject"); - - if (CHECK_FLAG(si->flags, ZEBRA_FLAG_BLACKHOLE)) - vty_out (vty, " %s", "blackhole"); - } - - if (si->distance != ZEBRA_STATIC_DISTANCE_DEFAULT) - vty_out (vty, " %d", si->distance); - - vty_out (vty, "%s", VTY_NEWLINE); - - write = 1; - } - return write; + return static_ipv6_func (vty, 0, argv[0], argv[1], NULL, NULL, argv[2], argv[3], NULL); } -DEFUN (show_ip_protocol, - show_ip_protocol_cmd, - "show ip protocol", - SHOW_STR - IP_STR - "IP protocol filtering status\n") +DEFUN (no_ipv6_route_pref_tag_vrf, + no_ipv6_route_pref_tag_vrf_cmd, + "no ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) tag <1-4294967295> <1-255>" VRF_CMD_STR, + NO_STR + IP_STR + "Establish static routes\n" + "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" + "IPv6 gateway address\n" + "IPv6 gateway interface name\n" + "Set tag for this route\n" + "Tag value\n" + "Distance value for this prefix\n" + VRF_CMD_HELP_STR) { - int i; - - vty_out(vty, "Protocol : route-map %s", VTY_NEWLINE); - vty_out(vty, "------------------------%s", VTY_NEWLINE); - for (i=0;i", + NO_STR + IP_STR + "Establish static routes\n" + "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" + "IPv6 gateway address\n" + "IPv6 gateway interface name\n" + "Emit an ICMP unreachable when matched\n" + "Silently discard pkts when matched\n" + "Distance value for this prefix\n") { - int ret; - u_char distance; - struct prefix p; - struct in6_addr *gate = NULL; - struct in6_addr gate_addr; - u_char type = 0; - int table = 0; - u_char flag = 0; - - ret = str2prefix (dest_str, &p); - if (ret <= 0) - { - vty_out (vty, "%% Malformed address%s", VTY_NEWLINE); - return CMD_WARNING; - } + /* We do not care about argv[2] */ + return static_ipv6_func (vty, 0, argv[0], argv[1], NULL, argv[2], NULL, argv[3], + NULL); +} - /* Apply mask for given prefix. */ - apply_mask (&p); +DEFUN (no_ipv6_route_flags_pref_tag, + no_ipv6_route_flags_pref_tag_cmd, + "no ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) (reject|blackhole) tag <1-4294967295> <1-255>", + NO_STR + IP_STR + "Establish static routes\n" + "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" + "IPv6 gateway address\n" + "IPv6 gateway interface name\n" + "Emit an ICMP unreachable when matched\n" + "Silently discard pkts when matched\n" + "Set tag for this route\n" + "Tag value\n" + "Distance value for this prefix\n") +{ + /* We do not care about argv[2] */ + return static_ipv6_func (vty, 0, argv[0], argv[1], NULL, argv[2], argv[3], argv[4], NULL); +} - /* Route flags */ - if (flag_str) { - switch(flag_str[0]) { - case 'r': - case 'R': /* XXX */ - SET_FLAG (flag, ZEBRA_FLAG_REJECT); - break; - case 'b': - case 'B': /* XXX */ - SET_FLAG (flag, ZEBRA_FLAG_BLACKHOLE); - break; - default: - vty_out (vty, "%% Malformed flag %s %s", flag_str, VTY_NEWLINE); - return CMD_WARNING; - } - } +DEFUN (no_ipv6_route_flags_pref_tag_vrf, + no_ipv6_route_flags_pref_tag_vrf_cmd, + "no ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) (reject|blackhole) tag <1-4294967295> <1-255>" VRF_CMD_STR, + NO_STR + IP_STR + "Establish static routes\n" + "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" + "IPv6 gateway address\n" + "IPv6 gateway interface name\n" + "Emit an ICMP unreachable when matched\n" + "Silently discard pkts when matched\n" + "Set tag for this route\n" + "Tag value\n" + "Distance value for this prefix\n" + VRF_CMD_HELP_STR) +{ + /* We do not care about argv[2] */ + return static_ipv6_func (vty, 0, argv[0], argv[1], NULL, argv[2], argv[3], argv[4], argv[5]); +} - /* Administrative distance. */ - if (distance_str) - distance = atoi (distance_str); - else - distance = ZEBRA_STATIC_DISTANCE_DEFAULT; +DEFUN (no_ipv6_route_ifname_pref, + no_ipv6_route_ifname_pref_cmd, + "no ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>", + NO_STR + IP_STR + "Establish static routes\n" + "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" + "IPv6 gateway address\n" + "IPv6 gateway interface name\n" + "Distance value for this prefix\n") +{ + return static_ipv6_func (vty, 0, argv[0], argv[1], argv[2], NULL, NULL, argv[3], + NULL); +} - /* When gateway is valid IPv6 addrees, then gate is treated as - nexthop address other case gate is treated as interface name. */ - ret = inet_pton (AF_INET6, gate_str, &gate_addr); +DEFUN (no_ipv6_route_ifname_pref_tag, + no_ipv6_route_ifname_pref_tag_cmd, + "no ipv6 route X:X::X:X/M X:X::X:X INTERFACE tag <1-4294967295> <1-255>", + NO_STR + IP_STR + "Establish static routes\n" + "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" + "IPv6 gateway address\n" + "IPv6 gateway interface name\n" + "Set tag for this route\n" + "Tag value\n" + "Distance value for this prefix\n") +{ + return static_ipv6_func (vty, 0, argv[0], argv[1], argv[2], NULL, argv[3], argv[4], NULL); +} - if (ifname) - { - /* When ifname is specified. It must be come with gateway - address. */ - if (ret != 1) - { - vty_out (vty, "%% Malformed address%s", VTY_NEWLINE); - return CMD_WARNING; - } - type = STATIC_IPV6_GATEWAY_IFNAME; - gate = &gate_addr; - } - else - { - if (ret == 1) - { - type = STATIC_IPV6_GATEWAY; - gate = &gate_addr; - } - else - { - type = STATIC_IPV6_IFNAME; - ifname = gate_str; - } - } +DEFUN (no_ipv6_route_ifname_pref_tag_vrf, + no_ipv6_route_ifname_pref_tag_vrf_cmd, + "no ipv6 route X:X::X:X/M X:X::X:X INTERFACE tag <1-4294967295> <1-255>" VRF_CMD_STR, + NO_STR + IP_STR + "Establish static routes\n" + "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" + "IPv6 gateway address\n" + "IPv6 gateway interface name\n" + "Set tag for this route\n" + "Tag value\n" + "Distance value for this prefix\n" + VRF_CMD_HELP_STR) +{ + return static_ipv6_func (vty, 0, argv[0], argv[1], argv[2], NULL, argv[3], argv[4], argv[5]); +} - if (add_cmd) - static_add_ipv6 (&p, type, gate, ifname, flag, distance, table); - else - static_delete_ipv6 (&p, type, gate, ifname, distance, table); +DEFUN (no_ipv6_route_ifname_flags_pref, + no_ipv6_route_ifname_flags_pref_cmd, + "no ipv6 route X:X::X:X/M X:X::X:X INTERFACE (reject|blackhole) <1-255>", + NO_STR + IP_STR + "Establish static routes\n" + "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" + "IPv6 gateway address\n" + "IPv6 gateway interface name\n" + "Emit an ICMP unreachable when matched\n" + "Silently discard pkts when matched\n" + "Distance value for this prefix\n") +{ + return static_ipv6_func (vty, 0, argv[0], argv[1], argv[2], argv[3], NULL, argv[4], + NULL); +} - return CMD_SUCCESS; +DEFUN (no_ipv6_route_ifname_flags_pref_tag, + no_ipv6_route_ifname_flags_pref_tag_cmd, + "no ipv6 route X:X::X:X/M X:X::X:X INTERFACE (reject|blackhole) tag <1-4294967295> <1-255>", + NO_STR + IP_STR + "Establish static routes\n" + "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" + "IPv6 gateway address\n" + "IPv6 gateway interface name\n" + "Emit an ICMP unreachable when matched\n" + "Silently discard pkts when matched\n" + "Set tag for this route\n" + "Tag value\n" + "Distance value for this prefix\n") +{ + return static_ipv6_func (vty, 0, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], NULL); } -DEFUN (ipv6_route, - ipv6_route_cmd, - "ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE)", +DEFUN (no_ipv6_route_ifname_flags_pref_tag_vrf, + no_ipv6_route_ifname_flags_pref_tag_vrf_cmd, + "no ipv6 route X:X::X:X/M X:X::X:X INTERFACE (reject|blackhole) tag <1-4294967295> <1-255>" VRF_CMD_STR, + NO_STR IP_STR "Establish static routes\n" "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" "IPv6 gateway address\n" - "IPv6 gateway interface name\n") + "IPv6 gateway interface name\n" + "Emit an ICMP unreachable when matched\n" + "Silently discard pkts when matched\n" + "Set tag for this route\n" + "Tag value\n" + "Distance value for this prefix\n" + VRF_CMD_HELP_STR) { - return static_ipv6_func (vty, 1, argv[0], argv[1], NULL, NULL, NULL); + return static_ipv6_func (vty, 0, argv[0], argv[1], argv[2], argv[3], argv[4], argv[5], argv[6]); } -DEFUN (ipv6_route_flags, - ipv6_route_flags_cmd, - "ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) (reject|blackhole)", +DEFUN (ipv6_route_vrf, + ipv6_route_vrf_cmd, + "ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) " VRF_CMD_STR, + IP_STR + "Establish static routes\n" + "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" + "IPv6 gateway address\n" + "IPv6 gateway interface name\n" + VRF_CMD_HELP_STR) +{ + return static_ipv6_func (vty, 1, argv[0], argv[1], NULL, NULL, NULL, NULL, + argv[2]); +} + +DEFUN (ipv6_route_flags_vrf, + ipv6_route_flags_vrf_cmd, + "ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) (reject|blackhole) " VRF_CMD_STR, IP_STR "Establish static routes\n" "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" "IPv6 gateway address\n" "IPv6 gateway interface name\n" "Emit an ICMP unreachable when matched\n" - "Silently discard pkts when matched\n") + "Silently discard pkts when matched\n" + VRF_CMD_HELP_STR) { - return static_ipv6_func (vty, 1, argv[0], argv[1], NULL, argv[2], NULL); + return static_ipv6_func (vty, 1, argv[0], argv[1], NULL, argv[2], NULL, NULL, + argv[3]); } -DEFUN (ipv6_route_ifname, - ipv6_route_ifname_cmd, - "ipv6 route X:X::X:X/M X:X::X:X INTERFACE", +DEFUN (ipv6_route_ifname_vrf, + ipv6_route_ifname_vrf_cmd, + "ipv6 route X:X::X:X/M X:X::X:X INTERFACE " VRF_CMD_STR, IP_STR "Establish static routes\n" "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" "IPv6 gateway address\n" - "IPv6 gateway interface name\n") + "IPv6 gateway interface name\n" + VRF_CMD_HELP_STR) { - return static_ipv6_func (vty, 1, argv[0], argv[1], argv[2], NULL, NULL); + return static_ipv6_func (vty, 1, argv[0], argv[1], argv[2], NULL, NULL, NULL, + argv[3]); } -DEFUN (ipv6_route_ifname_flags, - ipv6_route_ifname_flags_cmd, - "ipv6 route X:X::X:X/M X:X::X:X INTERFACE (reject|blackhole)", +DEFUN (ipv6_route_ifname_flags_vrf, + ipv6_route_ifname_flags_vrf_cmd, + "ipv6 route X:X::X:X/M X:X::X:X INTERFACE (reject|blackhole) " VRF_CMD_STR, IP_STR "Establish static routes\n" "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" "IPv6 gateway address\n" "IPv6 gateway interface name\n" "Emit an ICMP unreachable when matched\n" - "Silently discard pkts when matched\n") + "Silently discard pkts when matched\n" + VRF_CMD_HELP_STR) { - return static_ipv6_func (vty, 1, argv[0], argv[1], argv[2], argv[3], NULL); + return static_ipv6_func (vty, 1, argv[0], argv[1], argv[2], argv[3], NULL, NULL, + argv[4]); } -DEFUN (ipv6_route_pref, - ipv6_route_pref_cmd, - "ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) <1-255>", +DEFUN (ipv6_route_pref_vrf, + ipv6_route_pref_vrf_cmd, + "ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) <1-255> " VRF_CMD_STR, IP_STR "Establish static routes\n" "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" "IPv6 gateway address\n" "IPv6 gateway interface name\n" - "Distance value for this prefix\n") + "Distance value for this prefix\n" + VRF_CMD_HELP_STR) { - return static_ipv6_func (vty, 1, argv[0], argv[1], NULL, NULL, argv[2]); + return static_ipv6_func (vty, 1, argv[0], argv[1], NULL, NULL, NULL, argv[2], + argv[3]); } -DEFUN (ipv6_route_flags_pref, - ipv6_route_flags_pref_cmd, - "ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) (reject|blackhole) <1-255>", +DEFUN (ipv6_route_flags_pref_vrf, + ipv6_route_flags_pref_vrf_cmd, + "ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) (reject|blackhole) <1-255> " VRF_CMD_STR, IP_STR "Establish static routes\n" "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" @@ -1360,27 +4279,31 @@ DEFUN (ipv6_route_flags_pref, "IPv6 gateway interface name\n" "Emit an ICMP unreachable when matched\n" "Silently discard pkts when matched\n" - "Distance value for this prefix\n") + "Distance value for this prefix\n" + VRF_CMD_HELP_STR) { - return static_ipv6_func (vty, 1, argv[0], argv[1], NULL, argv[2], argv[3]); + return static_ipv6_func (vty, 1, argv[0], argv[1], NULL, argv[2], NULL, argv[3], + argv[4]); } -DEFUN (ipv6_route_ifname_pref, - ipv6_route_ifname_pref_cmd, - "ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>", +DEFUN (ipv6_route_ifname_pref_vrf, + ipv6_route_ifname_pref_vrf_cmd, + "ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255> " VRF_CMD_STR, IP_STR "Establish static routes\n" "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" "IPv6 gateway address\n" "IPv6 gateway interface name\n" - "Distance value for this prefix\n") + "Distance value for this prefix\n" + VRF_CMD_HELP_STR) { - return static_ipv6_func (vty, 1, argv[0], argv[1], argv[2], NULL, argv[3]); + return static_ipv6_func (vty, 1, argv[0], argv[1], argv[2], NULL, NULL, argv[3], + argv[4]); } -DEFUN (ipv6_route_ifname_flags_pref, - ipv6_route_ifname_flags_pref_cmd, - "ipv6 route X:X::X:X/M X:X::X:X INTERFACE (reject|blackhole) <1-255>", +DEFUN (ipv6_route_ifname_flags_pref_vrf, + ipv6_route_ifname_flags_pref_vrf_cmd, + "ipv6 route X:X::X:X/M X:X::X:X INTERFACE (reject|blackhole) <1-255> " VRF_CMD_STR, IP_STR "Establish static routes\n" "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" @@ -1388,27 +4311,31 @@ DEFUN (ipv6_route_ifname_flags_pref, "IPv6 gateway interface name\n" "Emit an ICMP unreachable when matched\n" "Silently discard pkts when matched\n" - "Distance value for this prefix\n") + "Distance value for this prefix\n" + VRF_CMD_HELP_STR) { - return static_ipv6_func (vty, 1, argv[0], argv[1], argv[2], argv[3], argv[4]); + return static_ipv6_func (vty, 1, argv[0], argv[1], argv[2], argv[3], NULL, argv[4], + argv[5]); } -DEFUN (no_ipv6_route, - no_ipv6_route_cmd, - "no ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE)", +DEFUN (no_ipv6_route_vrf, + no_ipv6_route_vrf_cmd, + "no ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) " VRF_CMD_STR, NO_STR IP_STR "Establish static routes\n" "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" "IPv6 gateway address\n" - "IPv6 gateway interface name\n") + "IPv6 gateway interface name\n" + VRF_CMD_HELP_STR) { - return static_ipv6_func (vty, 0, argv[0], argv[1], NULL, NULL, NULL); + return static_ipv6_func (vty, 0, argv[0], argv[1], NULL, NULL, NULL, NULL, + (argc > 3) ? argv[3] : argv[2]); } -ALIAS (no_ipv6_route, - no_ipv6_route_flags_cmd, - "no ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) (reject|blackhole)", +ALIAS (no_ipv6_route_vrf, + no_ipv6_route_flags_vrf_cmd, + "no ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) (reject|blackhole) " VRF_CMD_STR, NO_STR IP_STR "Establish static routes\n" @@ -1416,24 +4343,27 @@ ALIAS (no_ipv6_route, "IPv6 gateway address\n" "IPv6 gateway interface name\n" "Emit an ICMP unreachable when matched\n" - "Silently discard pkts when matched\n") + "Silently discard pkts when matched\n" + VRF_CMD_HELP_STR) -DEFUN (no_ipv6_route_ifname, - no_ipv6_route_ifname_cmd, - "no ipv6 route X:X::X:X/M X:X::X:X INTERFACE", +DEFUN (no_ipv6_route_ifname_vrf, + no_ipv6_route_ifname_vrf_cmd, + "no ipv6 route X:X::X:X/M X:X::X:X INTERFACE " VRF_CMD_STR, NO_STR IP_STR "Establish static routes\n" "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" "IPv6 gateway address\n" - "IPv6 gateway interface name\n") + "IPv6 gateway interface name\n" + VRF_CMD_HELP_STR) { - return static_ipv6_func (vty, 0, argv[0], argv[1], argv[2], NULL, NULL); + return static_ipv6_func (vty, 0, argv[0], argv[1], argv[2], NULL, NULL, NULL, + (argc > 4) ? argv[4] : argv[3]); } -ALIAS (no_ipv6_route_ifname, - no_ipv6_route_ifname_flags_cmd, - "no ipv6 route X:X::X:X/M X:X::X:X INTERFACE (reject|blackhole)", +ALIAS (no_ipv6_route_ifname_vrf, + no_ipv6_route_ifname_flags_vrf_cmd, + "no ipv6 route X:X::X:X/M X:X::X:X INTERFACE (reject|blackhole) " VRF_CMD_STR, NO_STR IP_STR "Establish static routes\n" @@ -1441,25 +4371,28 @@ ALIAS (no_ipv6_route_ifname, "IPv6 gateway address\n" "IPv6 gateway interface name\n" "Emit an ICMP unreachable when matched\n" - "Silently discard pkts when matched\n") + "Silently discard pkts when matched\n" + VRF_CMD_HELP_STR) -DEFUN (no_ipv6_route_pref, - no_ipv6_route_pref_cmd, - "no ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) <1-255>", +DEFUN (no_ipv6_route_pref_vrf, + no_ipv6_route_pref_vrf_cmd, + "no ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) <1-255> " VRF_CMD_STR, NO_STR IP_STR "Establish static routes\n" "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" "IPv6 gateway address\n" "IPv6 gateway interface name\n" - "Distance value for this prefix\n") + "Distance value for this prefix\n" + VRF_CMD_HELP_STR) { - return static_ipv6_func (vty, 0, argv[0], argv[1], NULL, NULL, argv[2]); + return static_ipv6_func (vty, 0, argv[0], argv[1], NULL, NULL, NULL, argv[2], + argv[3]); } -DEFUN (no_ipv6_route_flags_pref, - no_ipv6_route_flags_pref_cmd, - "no ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) (reject|blackhole) <1-255>", +DEFUN (no_ipv6_route_flags_pref_vrf, + no_ipv6_route_flags_pref_vrf_cmd, + "no ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) (reject|blackhole) <1-255> " VRF_CMD_STR, NO_STR IP_STR "Establish static routes\n" @@ -1468,29 +4401,33 @@ DEFUN (no_ipv6_route_flags_pref, "IPv6 gateway interface name\n" "Emit an ICMP unreachable when matched\n" "Silently discard pkts when matched\n" - "Distance value for this prefix\n") + "Distance value for this prefix\n" + VRF_CMD_HELP_STR) { /* We do not care about argv[2] */ - return static_ipv6_func (vty, 0, argv[0], argv[1], NULL, argv[2], argv[3]); + return static_ipv6_func (vty, 0, argv[0], argv[1], NULL, argv[2], NULL, argv[3], + argv[4]); } -DEFUN (no_ipv6_route_ifname_pref, - no_ipv6_route_ifname_pref_cmd, - "no ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>", +DEFUN (no_ipv6_route_ifname_pref_vrf, + no_ipv6_route_ifname_pref_vrf_cmd, + "no ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255> " VRF_CMD_STR, NO_STR IP_STR "Establish static routes\n" "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" "IPv6 gateway address\n" "IPv6 gateway interface name\n" - "Distance value for this prefix\n") + "Distance value for this prefix\n" + VRF_CMD_HELP_STR) { - return static_ipv6_func (vty, 0, argv[0], argv[1], argv[2], NULL, argv[3]); + return static_ipv6_func (vty, 0, argv[0], argv[1], argv[2], NULL, NULL, argv[3], + argv[4]); } -DEFUN (no_ipv6_route_ifname_flags_pref, - no_ipv6_route_ifname_flags_pref_cmd, - "no ipv6 route X:X::X:X/M X:X::X:X INTERFACE (reject|blackhole) <1-255>", +DEFUN (no_ipv6_route_ifname_flags_pref_vrf, + no_ipv6_route_ifname_flags_pref_vrf_cmd, + "no ipv6 route X:X::X:X/M X:X::X:X INTERFACE (reject|blackhole) <1-255> " VRF_CMD_STR, NO_STR IP_STR "Establish static routes\n" @@ -1499,280 +4436,108 @@ DEFUN (no_ipv6_route_ifname_flags_pref, "IPv6 gateway interface name\n" "Emit an ICMP unreachable when matched\n" "Silently discard pkts when matched\n" - "Distance value for this prefix\n") + "Distance value for this prefix\n" + VRF_CMD_HELP_STR) { - return static_ipv6_func (vty, 0, argv[0], argv[1], argv[2], argv[3], argv[4]); + return static_ipv6_func (vty, 0, argv[0], argv[1], argv[2], argv[3], NULL, argv[4], + argv[5]); } -/* New RIB. Detailed information for IPv6 route. */ -static void -vty_show_ipv6_route_detail (struct vty *vty, struct route_node *rn) +DEFUN (show_ipv6_route, + show_ipv6_route_cmd, + "show ipv6 route", + SHOW_STR + IP_STR + "IPv6 routing table\n") { + struct route_table *table; + struct route_node *rn; struct rib *rib; - struct nexthop *nexthop; - char buf[BUFSIZ]; - - for (rib = rn->info; rib; rib = rib->next) - { - vty_out (vty, "Routing entry for %s/%d%s", - inet_ntop (AF_INET6, &rn->p.u.prefix6, buf, BUFSIZ), - rn->p.prefixlen, - VTY_NEWLINE); - vty_out (vty, " Known via \"%s\"", zebra_route_string (rib->type)); - vty_out (vty, ", distance %d, metric %d", rib->distance, rib->metric); - if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED)) - vty_out (vty, ", best"); - if (rib->refcnt) - vty_out (vty, ", refcnt %ld", rib->refcnt); - if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_BLACKHOLE)) - vty_out (vty, ", blackhole"); - if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_REJECT)) - vty_out (vty, ", reject"); - vty_out (vty, "%s", VTY_NEWLINE); - -#define ONE_DAY_SECOND 60*60*24 -#define ONE_WEEK_SECOND 60*60*24*7 - if (rib->type == ZEBRA_ROUTE_RIPNG - || rib->type == ZEBRA_ROUTE_OSPF6 - || rib->type == ZEBRA_ROUTE_BABEL - || rib->type == ZEBRA_ROUTE_ISIS - || rib->type == ZEBRA_ROUTE_BGP) - { - time_t uptime; - struct tm *tm; - - uptime = time (NULL); - uptime -= rib->uptime; - tm = gmtime (&uptime); - - vty_out (vty, " Last update "); - - if (uptime < ONE_DAY_SECOND) - vty_out (vty, "%02d:%02d:%02d", - tm->tm_hour, tm->tm_min, tm->tm_sec); - else if (uptime < ONE_WEEK_SECOND) - vty_out (vty, "%dd%02dh%02dm", - tm->tm_yday, tm->tm_hour, tm->tm_min); - else - vty_out (vty, "%02dw%dd%02dh", - tm->tm_yday/7, - tm->tm_yday - ((tm->tm_yday/7) * 7), tm->tm_hour); - vty_out (vty, " ago%s", VTY_NEWLINE); - } + int first = 1; + vrf_id_t vrf_id = VRF_DEFAULT; - for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) - { - vty_out (vty, " %c", - CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB) ? '*' : ' '); + if (argc > 0) + VTY_GET_INTEGER ("VRF ID", vrf_id, argv[0]); - switch (nexthop->type) - { - case NEXTHOP_TYPE_IPV6: - case NEXTHOP_TYPE_IPV6_IFINDEX: - case NEXTHOP_TYPE_IPV6_IFNAME: - vty_out (vty, " %s", - inet_ntop (AF_INET6, &nexthop->gate.ipv6, buf, BUFSIZ)); - if (nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME) - vty_out (vty, ", %s", nexthop->ifname); - else if (nexthop->ifindex) - vty_out (vty, ", via %s", ifindex2ifname (nexthop->ifindex)); - break; - case NEXTHOP_TYPE_IFINDEX: - vty_out (vty, " directly connected, %s", - ifindex2ifname (nexthop->ifindex)); - break; - case NEXTHOP_TYPE_IFNAME: - vty_out (vty, " directly connected, %s", - nexthop->ifname); - break; - default: - break; - } - if (! CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE)) - vty_out (vty, " inactive"); + table = zebra_vrf_table (AFI_IP6, SAFI_UNICAST, vrf_id); + if (! table) + return CMD_SUCCESS; - 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, "%s", VTY_NEWLINE); - } - vty_out (vty, "%s", VTY_NEWLINE); - } + /* Show all IPv6 route. */ + for (rn = route_top (table); rn; rn = route_next (rn)) + RNODE_FOREACH_RIB (rn, rib) + { + if (first) + { + vty_out (vty, SHOW_ROUTE_V6_HEADER); + first = 0; + } + vty_show_ip_route (vty, rn, rib); + } + return CMD_SUCCESS; } -static void -vty_show_ipv6_route (struct vty *vty, struct route_node *rn, - struct rib *rib) -{ - struct nexthop *nexthop; - int len = 0; - char buf[BUFSIZ]; - - /* Nexthop information. */ - for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) - { - if (nexthop == rib->nexthop) - { - /* Prefix information. */ - len = vty_out (vty, "%c%c%c %s/%d", - zebra_route_char (rib->type), - CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED) - ? '>' : ' ', - CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB) - ? '*' : ' ', - inet_ntop (AF_INET6, &rn->p.u.prefix6, buf, BUFSIZ), - rn->p.prefixlen); - - /* Distance and metric display. */ - if (rib->type != ZEBRA_ROUTE_CONNECT - && rib->type != ZEBRA_ROUTE_KERNEL) - len += vty_out (vty, " [%d/%d]", rib->distance, - rib->metric); - } - else - vty_out (vty, " %c%*c", - CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB) - ? '*' : ' ', - len - 3, ' '); - - switch (nexthop->type) - { - case NEXTHOP_TYPE_IPV6: - case NEXTHOP_TYPE_IPV6_IFINDEX: - case NEXTHOP_TYPE_IPV6_IFNAME: - vty_out (vty, " via %s", - inet_ntop (AF_INET6, &nexthop->gate.ipv6, buf, BUFSIZ)); - if (nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME) - vty_out (vty, ", %s", nexthop->ifname); - else if (nexthop->ifindex) - vty_out (vty, ", %s", ifindex2ifname (nexthop->ifindex)); - break; - case NEXTHOP_TYPE_IFINDEX: - vty_out (vty, " is directly connected, %s", - ifindex2ifname (nexthop->ifindex)); - break; - case NEXTHOP_TYPE_IFNAME: - vty_out (vty, " is directly connected, %s", - nexthop->ifname); - break; - default: - break; - } - if (! CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE)) - 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; - } - } - - if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_BLACKHOLE)) - vty_out (vty, ", bh"); - if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_REJECT)) - vty_out (vty, ", rej"); - - if (rib->type == ZEBRA_ROUTE_RIPNG - || rib->type == ZEBRA_ROUTE_OSPF6 - || rib->type == ZEBRA_ROUTE_BABEL - || rib->type == ZEBRA_ROUTE_ISIS - || rib->type == ZEBRA_ROUTE_BGP) - { - time_t uptime; - struct tm *tm; - - uptime = time (NULL); - uptime -= rib->uptime; - tm = gmtime (&uptime); - -#define ONE_DAY_SECOND 60*60*24 -#define ONE_WEEK_SECOND 60*60*24*7 - - if (uptime < ONE_DAY_SECOND) - vty_out (vty, ", %02d:%02d:%02d", - tm->tm_hour, tm->tm_min, tm->tm_sec); - else if (uptime < ONE_WEEK_SECOND) - vty_out (vty, ", %dd%02dh%02dm", - tm->tm_yday, tm->tm_hour, tm->tm_min); - else - vty_out (vty, ", %02dw%dd%02dh", - tm->tm_yday/7, - tm->tm_yday - ((tm->tm_yday/7) * 7), tm->tm_hour); - } - vty_out (vty, "%s", VTY_NEWLINE); - } -} +ALIAS (show_ipv6_route, + show_ipv6_route_vrf_cmd, + "show ipv6 route " VRF_CMD_STR, + SHOW_STR + IP_STR + "IPv6 routing table\n" + VRF_CMD_HELP_STR) -DEFUN (show_ipv6_route, - show_ipv6_route_cmd, - "show ipv6 route", +DEFUN (show_ipv6_route_tag, + show_ipv6_route_tag_cmd, + "show ipv6 route tag <1-4294967295>", SHOW_STR IP_STR - "IPv6 routing table\n") + "IPv6 routing table\n" + "Show only routes with tag\n" + "Tag value\n") { struct route_table *table; struct route_node *rn; struct rib *rib; int first = 1; + route_tag_t tag = 0; + vrf_id_t vrf_id = VRF_DEFAULT; - table = vrf_table (AFI_IP6, SAFI_UNICAST, 0); + if (argv[0]) + tag = atoi(argv[0]); + + if (argc == 2) + VTY_GET_INTEGER ("VRF ID", vrf_id, argv[1]); + + table = zebra_vrf_table (AFI_IP6, SAFI_UNICAST, vrf_id); if (! table) return CMD_SUCCESS; - /* Show all IPv6 route. */ + /* Show all IPv6 routes with matching tag value. */ 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->tag != tag) + continue; + if (first) { vty_out (vty, SHOW_ROUTE_V6_HEADER); first = 0; } - vty_show_ipv6_route (vty, rn, rib); + vty_show_ip_route (vty, rn, rib); } return CMD_SUCCESS; } +ALIAS (show_ipv6_route_tag, + show_ipv6_route_tag_vrf_cmd, + "show ipv6 route tag <1-4294967295>" VRF_CMD_STR, + SHOW_STR + IP_STR + "IPv6 routing table\n" + "Show only routes with tag\n" + "Tag value\n" + VRF_CMD_HELP_STR) + DEFUN (show_ipv6_route_prefix_longer, show_ipv6_route_prefix_longer_cmd, "show ipv6 route X:X::X:X/M longer-prefixes", @@ -1788,10 +4553,7 @@ DEFUN (show_ipv6_route_prefix_longer, struct prefix p; int ret; int first = 1; - - table = vrf_table (AFI_IP6, SAFI_UNICAST, 0); - if (! table) - return CMD_SUCCESS; + vrf_id_t vrf_id = VRF_DEFAULT; ret = str2prefix (argv[0], &p); if (! ret) @@ -1800,9 +4562,16 @@ DEFUN (show_ipv6_route_prefix_longer, return CMD_WARNING; } + if (argc > 1) + VTY_GET_INTEGER ("VRF ID", vrf_id, argv[1]); + + table = zebra_vrf_table (AFI_IP6, SAFI_UNICAST, vrf_id); + if (! table) + return CMD_SUCCESS; + /* 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) @@ -1810,11 +4579,21 @@ DEFUN (show_ipv6_route_prefix_longer, vty_out (vty, SHOW_ROUTE_V6_HEADER); first = 0; } - vty_show_ipv6_route (vty, rn, rib); + vty_show_ip_route (vty, rn, rib); } return CMD_SUCCESS; } +ALIAS (show_ipv6_route_prefix_longer, + show_ipv6_route_prefix_longer_vrf_cmd, + "show ipv6 route X:X::X:X/M longer-prefixes " VRF_CMD_STR, + SHOW_STR + IP_STR + "IPv6 routing table\n" + "IPv6 prefix\n" + "Show route matching the specified Network/Mask pair only\n" + VRF_CMD_HELP_STR) + DEFUN (show_ipv6_route_protocol, show_ipv6_route_protocol_cmd, "show ipv6 route " QUAGGA_IP6_REDIST_STR_ZEBRA, @@ -1828,6 +4607,7 @@ DEFUN (show_ipv6_route_protocol, struct route_node *rn; struct rib *rib; int first = 1; + vrf_id_t vrf_id = VRF_DEFAULT; type = proto_redistnum (AFI_IP6, argv[0]); if (type < 0) @@ -1835,14 +4615,17 @@ DEFUN (show_ipv6_route_protocol, vty_out (vty, "Unknown route type%s", VTY_NEWLINE); return CMD_WARNING; } - - table = vrf_table (AFI_IP6, SAFI_UNICAST, 0); + + if (argc > 1) + VTY_GET_INTEGER ("VRF ID", vrf_id, argv[1]); + + table = zebra_vrf_table (AFI_IP6, SAFI_UNICAST, vrf_id); if (! table) return CMD_SUCCESS; /* 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) @@ -1850,11 +4633,20 @@ DEFUN (show_ipv6_route_protocol, vty_out (vty, SHOW_ROUTE_V6_HEADER); first = 0; } - vty_show_ipv6_route (vty, rn, rib); + vty_show_ip_route (vty, rn, rib); } return CMD_SUCCESS; } +ALIAS (show_ipv6_route_protocol, + show_ipv6_route_protocol_vrf_cmd, + "show ipv6 route " QUAGGA_IP6_REDIST_STR_ZEBRA " " VRF_CMD_STR, + SHOW_STR + IP_STR + "IP routing table\n" + QUAGGA_IP6_REDIST_HELP_STR_ZEBRA + VRF_CMD_HELP_STR) + DEFUN (show_ipv6_route_addr, show_ipv6_route_addr_cmd, "show ipv6 route X:X::X:X", @@ -1867,6 +4659,7 @@ DEFUN (show_ipv6_route_addr, struct prefix_ipv6 p; struct route_table *table; struct route_node *rn; + vrf_id_t vrf_id = VRF_DEFAULT; ret = str2prefix_ipv6 (argv[0], &p); if (ret <= 0) @@ -1875,7 +4668,10 @@ DEFUN (show_ipv6_route_addr, return CMD_WARNING; } - table = vrf_table (AFI_IP6, SAFI_UNICAST, 0); + if (argc > 1) + VTY_GET_INTEGER ("VRF ID", vrf_id, argv[1]); + + table = zebra_vrf_table (AFI_IP6, SAFI_UNICAST, vrf_id); if (! table) return CMD_SUCCESS; @@ -1886,13 +4682,22 @@ DEFUN (show_ipv6_route_addr, return CMD_WARNING; } - vty_show_ipv6_route_detail (vty, rn); + vty_show_ip_route_detail (vty, rn, 0); route_unlock_node (rn); return CMD_SUCCESS; } +ALIAS (show_ipv6_route_addr, + show_ipv6_route_addr_vrf_cmd, + "show ipv6 route X:X::X:X " VRF_CMD_STR, + SHOW_STR + IP_STR + "IPv6 routing table\n" + "IPv6 Address\n" + VRF_CMD_HELP_STR) + DEFUN (show_ipv6_route_prefix, show_ipv6_route_prefix_cmd, "show ipv6 route X:X::X:X/M", @@ -1905,6 +4710,7 @@ DEFUN (show_ipv6_route_prefix, struct prefix_ipv6 p; struct route_table *table; struct route_node *rn; + vrf_id_t vrf_id = VRF_DEFAULT; ret = str2prefix_ipv6 (argv[0], &p); if (ret <= 0) @@ -1913,7 +4719,10 @@ DEFUN (show_ipv6_route_prefix, return CMD_WARNING; } - table = vrf_table (AFI_IP6, SAFI_UNICAST, 0); + if (argc > 1) + VTY_GET_INTEGER ("VRF ID", vrf_id, argv[1]); + + table = zebra_vrf_table (AFI_IP6, SAFI_UNICAST, vrf_id); if (! table) return CMD_SUCCESS; @@ -1921,16 +4730,27 @@ 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; } - vty_show_ipv6_route_detail (vty, rn); + vty_show_ip_route_detail (vty, rn, 0); route_unlock_node (rn); return CMD_SUCCESS; } +ALIAS (show_ipv6_route_prefix, + show_ipv6_route_prefix_vrf_cmd, + "show ipv6 route X:X::X:X/M " VRF_CMD_STR, + SHOW_STR + IP_STR + "IPv6 routing table\n" + "IPv6 prefix\n" + VRF_CMD_HELP_STR) + /* Show route summary. */ DEFUN (show_ipv6_route_summary, show_ipv6_route_summary_cmd, @@ -1941,8 +4761,12 @@ DEFUN (show_ipv6_route_summary, "Summary of all IPv6 routes\n") { struct route_table *table; + vrf_id_t vrf_id = VRF_DEFAULT; + + if (argc > 0) + VTY_GET_INTEGER ("VRF ID", vrf_id, argv[0]); - table = vrf_table (AFI_IP6, SAFI_UNICAST, 0); + table = zebra_vrf_table (AFI_IP6, SAFI_UNICAST, vrf_id); if (! table) return CMD_SUCCESS; @@ -1951,40 +4775,50 @@ DEFUN (show_ipv6_route_summary, return CMD_SUCCESS; } -/* - * Show IP mroute command to dump the BGP Multicast - * routing table - */ -DEFUN (show_ip_mroute, - show_ip_mroute_cmd, - "show ip mroute", +ALIAS (show_ipv6_route_summary, + show_ipv6_route_summary_vrf_cmd, + "show ipv6 route summary " VRF_CMD_STR, + SHOW_STR + IP_STR + "IPv6 routing table\n" + "Summary of all IPv6 routes\n" + VRF_CMD_HELP_STR) + +/* 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 - "IP Multicast routing table\n") + "IPv6 routing table\n" + "Summary of all IPv6 routes\n" + "Prefix routes\n") { struct route_table *table; - struct route_node *rn; - struct rib *rib; - int first = 1; + vrf_id_t vrf_id = VRF_DEFAULT; - table = vrf_table (AFI_IP, SAFI_MULTICAST, 0); + if (argc > 0) + VTY_GET_INTEGER ("VRF ID", vrf_id, argv[0]); + + table = zebra_vrf_table (AFI_IP6, SAFI_UNICAST, vrf_id); 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) - { - if (first) - { - vty_out (vty, SHOW_ROUTE_V4_HEADER); - first = 0; - } - vty_show_ip_route (vty, rn, rib); - } + vty_show_ip_route_summary_prefix (vty, table); + return CMD_SUCCESS; } +ALIAS (show_ipv6_route_summary_prefix, + show_ipv6_route_summary_prefix_vrf_cmd, + "show ipv6 route summary prefix " VRF_CMD_STR, + SHOW_STR + IP_STR + "IPv6 routing table\n" + "Summary of all IPv6 routes\n" + "Prefix routes\n" + VRF_CMD_HELP_STR) + /* * Show IPv6 mroute command.Used to dump * the Multicast routing table. @@ -2001,83 +4835,396 @@ DEFUN (show_ipv6_mroute, struct route_node *rn; struct rib *rib; int first = 1; + vrf_id_t vrf_id = VRF_DEFAULT; + + if (argc > 0) + VTY_GET_INTEGER ("VRF ID", vrf_id, argv[0]); - table = vrf_table (AFI_IP6, SAFI_MULTICAST, 0); + table = zebra_vrf_table (AFI_IP6, SAFI_MULTICAST, vrf_id); if (! table) return CMD_SUCCESS; /* 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) { vty_out (vty, SHOW_ROUTE_V6_HEADER); first = 0; } - vty_show_ipv6_route (vty, rn, rib); + vty_show_ip_route (vty, rn, rib); } return CMD_SUCCESS; } +ALIAS (show_ipv6_mroute, + show_ipv6_mroute_vrf_cmd, + "show ipv6 mroute " VRF_CMD_STR, + SHOW_STR + IP_STR + "IPv6 Multicast routing table\n" + VRF_CMD_HELP_STR) + +DEFUN (show_ipv6_route_vrf_all, + show_ipv6_route_vrf_all_cmd, + "show ipv6 route " VRF_ALL_CMD_STR, + SHOW_STR + IP_STR + "IPv6 routing table\n" + VRF_ALL_CMD_HELP_STR) +{ + struct route_table *table; + struct route_node *rn; + struct rib *rib; + struct zebra_vrf *zvrf; + vrf_iter_t iter; + int first = 1; + + for (iter = vrf_first (); iter != VRF_ITER_INVALID; iter = vrf_next (iter)) + { + if ((zvrf = vrf_iter2info (iter)) == NULL || + (table = zvrf->table[AFI_IP6][SAFI_UNICAST]) == NULL) + continue; + + /* Show all IPv6 route. */ + for (rn = route_top (table); rn; rn = route_next (rn)) + RNODE_FOREACH_RIB (rn, rib) + { + if (first) + { + vty_out (vty, SHOW_ROUTE_V6_HEADER); + first = 0; + } + vty_show_ip_route (vty, rn, rib); + } + } + + return CMD_SUCCESS; +} + +DEFUN (show_ipv6_route_prefix_longer_vrf_all, + show_ipv6_route_prefix_longer_vrf_all_cmd, + "show ipv6 route X:X::X:X/M longer-prefixes " VRF_ALL_CMD_STR, + SHOW_STR + IP_STR + "IPv6 routing table\n" + "IPv6 prefix\n" + "Show route matching the specified Network/Mask pair only\n" + VRF_ALL_CMD_HELP_STR) +{ + struct route_table *table; + struct route_node *rn; + struct rib *rib; + struct prefix p; + struct zebra_vrf *zvrf; + vrf_iter_t iter; + int ret; + int first = 1; + + ret = str2prefix (argv[0], &p); + if (! ret) + { + vty_out (vty, "%% Malformed Prefix%s", VTY_NEWLINE); + return CMD_WARNING; + } + + for (iter = vrf_first (); iter != VRF_ITER_INVALID; iter = vrf_next (iter)) + { + if ((zvrf = vrf_iter2info (iter)) == NULL || + (table = zvrf->table[AFI_IP6][SAFI_UNICAST]) == NULL) + continue; + + /* Show matched type IPv6 routes. */ + for (rn = route_top (table); rn; rn = route_next (rn)) + RNODE_FOREACH_RIB (rn, rib) + if (prefix_match (&p, &rn->p)) + { + if (first) + { + vty_out (vty, SHOW_ROUTE_V6_HEADER); + first = 0; + } + vty_show_ip_route (vty, rn, rib); + } + } + + return CMD_SUCCESS; +} + +DEFUN (show_ipv6_route_protocol_vrf_all, + show_ipv6_route_protocol_vrf_all_cmd, + "show ipv6 route " QUAGGA_IP6_REDIST_STR_ZEBRA " " VRF_ALL_CMD_STR, + SHOW_STR + IP_STR + "IP routing table\n" + QUAGGA_IP6_REDIST_HELP_STR_ZEBRA + VRF_ALL_CMD_HELP_STR) +{ + int type; + struct route_table *table; + struct route_node *rn; + struct rib *rib; + struct zebra_vrf *zvrf; + vrf_iter_t iter; + int first = 1; + + type = proto_redistnum (AFI_IP6, argv[0]); + if (type < 0) + { + vty_out (vty, "Unknown route type%s", VTY_NEWLINE); + return CMD_WARNING; + } + + for (iter = vrf_first (); iter != VRF_ITER_INVALID; iter = vrf_next (iter)) + { + if ((zvrf = vrf_iter2info (iter)) == NULL || + (table = zvrf->table[AFI_IP6][SAFI_UNICAST]) == NULL) + continue; + + /* Show matched type IPv6 routes. */ + for (rn = route_top (table); rn; rn = route_next (rn)) + RNODE_FOREACH_RIB (rn, rib) + if (rib->type == type) + { + if (first) + { + vty_out (vty, SHOW_ROUTE_V6_HEADER); + first = 0; + } + vty_show_ip_route (vty, rn, rib); + } + } + + return CMD_SUCCESS; +} + +DEFUN (show_ipv6_route_addr_vrf_all, + show_ipv6_route_addr_vrf_all_cmd, + "show ipv6 route X:X::X:X " VRF_ALL_CMD_STR, + SHOW_STR + IP_STR + "IPv6 routing table\n" + "IPv6 Address\n" + VRF_ALL_CMD_HELP_STR) +{ + int ret; + struct prefix_ipv6 p; + struct route_table *table; + struct route_node *rn; + struct zebra_vrf *zvrf; + vrf_iter_t iter; + + ret = str2prefix_ipv6 (argv[0], &p); + if (ret <= 0) + { + vty_out (vty, "Malformed IPv6 address%s", VTY_NEWLINE); + return CMD_WARNING; + } + + for (iter = vrf_first (); iter != VRF_ITER_INVALID; iter = vrf_next (iter)) + { + if ((zvrf = vrf_iter2info (iter)) == NULL || + (table = zvrf->table[AFI_IP6][SAFI_UNICAST]) == NULL) + continue; + + rn = route_node_match (table, (struct prefix *) &p); + if (! rn) + continue; + + vty_show_ip_route_detail (vty, rn, 0); + + route_unlock_node (rn); + } + + return CMD_SUCCESS; +} + +DEFUN (show_ipv6_route_prefix_vrf_all, + show_ipv6_route_prefix_vrf_all_cmd, + "show ipv6 route X:X::X:X/M " VRF_ALL_CMD_STR, + SHOW_STR + IP_STR + "IPv6 routing table\n" + "IPv6 prefix\n" + VRF_ALL_CMD_HELP_STR) +{ + int ret; + struct prefix_ipv6 p; + struct route_table *table; + struct route_node *rn; + struct zebra_vrf *zvrf; + vrf_iter_t iter; + + ret = str2prefix_ipv6 (argv[0], &p); + if (ret <= 0) + { + vty_out (vty, "Malformed IPv6 prefix%s", VTY_NEWLINE); + return CMD_WARNING; + } + + for (iter = vrf_first (); iter != VRF_ITER_INVALID; iter = vrf_next (iter)) + { + if ((zvrf = vrf_iter2info (iter)) == NULL || + (table = zvrf->table[AFI_IP6][SAFI_UNICAST]) == NULL) + continue; + + rn = route_node_match (table, (struct prefix *) &p); + if (! rn) + continue; + if (rn->p.prefixlen != p.prefixlen) + { + route_unlock_node (rn); + continue; + } + + vty_show_ip_route_detail (vty, rn, 0); + + route_unlock_node (rn); + } + + return CMD_SUCCESS; +} + +/* Show route summary. */ +DEFUN (show_ipv6_route_summary_vrf_all, + show_ipv6_route_summary_vrf_all_cmd, + "show ipv6 route summary " VRF_ALL_CMD_STR, + SHOW_STR + IP_STR + "IPv6 routing table\n" + "Summary of all IPv6 routes\n" + VRF_ALL_CMD_HELP_STR) +{ + struct zebra_vrf *zvrf; + vrf_iter_t iter; + + for (iter = vrf_first (); iter != VRF_ITER_INVALID; iter = vrf_next (iter)) + if ((zvrf = vrf_iter2info (iter)) != NULL) + vty_show_ip_route_summary (vty, zvrf->table[AFI_IP6][SAFI_UNICAST]); + + return CMD_SUCCESS; +} + +DEFUN (show_ipv6_mroute_vrf_all, + show_ipv6_mroute_vrf_all_cmd, + "show ipv6 mroute " VRF_ALL_CMD_STR, + SHOW_STR + IP_STR + "IPv6 Multicast routing table\n" + VRF_ALL_CMD_HELP_STR) +{ + struct route_table *table; + struct route_node *rn; + struct rib *rib; + struct zebra_vrf *zvrf; + vrf_iter_t iter; + int first = 1; + + for (iter = vrf_first (); iter != VRF_ITER_INVALID; iter = vrf_next (iter)) + { + if ((zvrf = vrf_iter2info (iter)) == NULL || + (table = zvrf->table[AFI_IP6][SAFI_UNICAST]) == NULL) + continue; + /* Show all IPv6 route. */ + for (rn = route_top (table); rn; rn = route_next (rn)) + RNODE_FOREACH_RIB (rn, rib) + { + if (first) + { + vty_out (vty, SHOW_ROUTE_V6_HEADER); + first = 0; + } + vty_show_ip_route (vty, rn, rib); + } + } + return CMD_SUCCESS; +} +DEFUN (show_ipv6_route_summary_prefix_vrf_all, + show_ipv6_route_summary_prefix_vrf_all_cmd, + "show ipv6 route summary prefix " VRF_ALL_CMD_STR, + SHOW_STR + IP_STR + "IPv6 routing table\n" + "Summary of all IPv6 routes\n" + "Prefix routes\n" + VRF_ALL_CMD_HELP_STR) +{ + struct zebra_vrf *zvrf; + vrf_iter_t iter; + for (iter = vrf_first (); iter != VRF_ITER_INVALID; iter = vrf_next (iter)) + if ((zvrf = vrf_iter2info (iter)) != NULL) + vty_show_ip_route_summary_prefix (vty, zvrf->table[AFI_IP6][SAFI_UNICAST]); + return CMD_SUCCESS; +} /* Write IPv6 static route configuration. */ static int static_config_ipv6 (struct vty *vty) { struct route_node *rn; - struct static_ipv6 *si; + struct static_route *si; int write; char buf[BUFSIZ]; struct route_table *stable; + struct zebra_vrf *zvrf; + vrf_iter_t iter; write = 0; - /* Lookup table. */ - stable = vrf_static_table (AFI_IP6, SAFI_UNICAST, 0); - if (! stable) - return -1; + for (iter = vrf_first (); iter != VRF_ITER_INVALID; iter = vrf_next (iter)) + { + if ((zvrf = vrf_iter2info (iter)) == NULL || + (stable = zvrf->stable[AFI_IP6][SAFI_UNICAST]) == NULL) + continue; - for (rn = route_top (stable); rn; rn = route_next (rn)) - for (si = rn->info; si; si = si->next) - { - vty_out (vty, "ipv6 route %s/%d", - inet_ntop (AF_INET6, &rn->p.u.prefix6, buf, BUFSIZ), - rn->p.prefixlen); + for (rn = route_top (stable); rn; rn = route_next (rn)) + for (si = rn->info; si; si = si->next) + { + vty_out (vty, "ipv6 route %s", prefix2str (&rn->p, buf, sizeof buf)); - switch (si->type) - { - case STATIC_IPV6_GATEWAY: - vty_out (vty, " %s", inet_ntop (AF_INET6, &si->ipv6, buf, BUFSIZ)); - break; - case STATIC_IPV6_IFNAME: - vty_out (vty, " %s", si->ifname); - break; - case STATIC_IPV6_GATEWAY_IFNAME: - vty_out (vty, " %s %s", - inet_ntop (AF_INET6, &si->ipv6, buf, BUFSIZ), si->ifname); - break; - } + switch (si->type) + { + case STATIC_IPV6_GATEWAY: + vty_out (vty, " %s", + inet_ntop (AF_INET6, &si->addr.ipv6, buf, BUFSIZ)); + break; + case STATIC_IPV6_IFNAME: + vty_out (vty, " %s", si->ifname); + break; + case STATIC_IPV6_GATEWAY_IFNAME: + vty_out (vty, " %s %s", + inet_ntop (AF_INET6, &si->addr.ipv6, buf, BUFSIZ), + si->ifname); + break; + } + + if (CHECK_FLAG(si->flags, ZEBRA_FLAG_REJECT)) + vty_out (vty, " %s", "reject"); - if (CHECK_FLAG(si->flags, ZEBRA_FLAG_REJECT)) - vty_out (vty, " %s", "reject"); + if (CHECK_FLAG(si->flags, ZEBRA_FLAG_BLACKHOLE)) + vty_out (vty, " %s", "blackhole"); - if (CHECK_FLAG(si->flags, ZEBRA_FLAG_BLACKHOLE)) - vty_out (vty, " %s", "blackhole"); + if (si->tag) + vty_out (vty, " tag %d", si->tag); - if (si->distance != ZEBRA_STATIC_DISTANCE_DEFAULT) - vty_out (vty, " %d", si->distance); - vty_out (vty, "%s", VTY_NEWLINE); + if (si->distance != ZEBRA_STATIC_DISTANCE_DEFAULT) + vty_out (vty, " %d", si->distance); - write = 1; - } + if (si->vrf_id != VRF_DEFAULT) + vty_out (vty, " vrf %u", si->vrf_id); + + vty_out (vty, "%s", VTY_NEWLINE); + + write = 1; + } + } return write; } -#endif /* HAVE_IPV6 */ /* Static ip route configuration write function. */ static int @@ -2085,7 +5232,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 */ @@ -2093,10 +5241,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;ilast_write_time = quagga_time(NULL); return 0; } -static int +int zebra_server_send_message(struct zserv *client) { if (client->t_suicide) return -1; + + stream_set_getp(client->obuf, 0); + client->last_write_cmd = stream_getw_from(client->obuf, 4); switch (buffer_write(client->wb, client->sock, STREAM_DATA(client->obuf), stream_get_endp(client->obuf))) { @@ -127,19 +135,53 @@ zebra_server_send_message(struct zserv *client) zserv_flush_data, client, client->sock); break; } + + client->last_write_time = quagga_time(NULL); return 0; } -static void -zserv_create_header (struct stream *s, uint16_t cmd) +void +zserv_create_header (struct stream *s, uint16_t cmd, vrf_id_t vrf_id) { /* length placeholder, caller can update */ stream_putw (s, ZEBRA_HEADER_SIZE); stream_putc (s, ZEBRA_HEADER_MARKER); stream_putc (s, ZSERV_VERSION); + stream_putw (s, vrf_id); stream_putw (s, cmd); } +static void +zserv_encode_interface (struct stream *s, struct interface *ifp) +{ + /* Interface information. */ + stream_put (s, ifp->name, INTERFACE_NAMSIZ); + stream_putl (s, ifp->ifindex); + stream_putc (s, ifp->status); + stream_putq (s, ifp->flags); + stream_putl (s, ifp->metric); + stream_putl (s, ifp->mtu); + stream_putl (s, ifp->mtu6); + stream_putl (s, ifp->bandwidth); + stream_putl (s, ifp->ll_type); + stream_putl (s, ifp->hw_addr_len); + if (ifp->hw_addr_len) + stream_put (s, ifp->hw_addr, ifp->hw_addr_len); + + zlog_info("Try to set TE Link Param"); + /* Then, Traffic Engineering parameters if any */ + if (HAS_LINK_PARAMS(ifp) && IS_LINK_PARAMS_SET(ifp->link_params)) + { + stream_putc (s, 1); + zebra_interface_link_params_write (s, ifp); + } + else + stream_putc (s, 0); + + /* Write packet size. */ + stream_putw_at (s, 0, stream_get_endp (s)); +} + /* Interface is added. Send ZEBRA_INTERFACE_ADD to client. */ /* * This function is called in the following situations: @@ -157,35 +199,16 @@ zsend_interface_add (struct zserv *client, struct interface *ifp) struct stream *s; /* Check this client need interface information. */ - if (! client->ifinfo) + if (! vrf_bitmap_check (client->ifinfo, ifp->vrf_id)) return 0; s = client->obuf; stream_reset (s); - /* Message type. */ - zserv_create_header (s, ZEBRA_INTERFACE_ADD); - - /* Interface information. */ - stream_put (s, ifp->name, INTERFACE_NAMSIZ); - stream_putl (s, ifp->ifindex); - stream_putc (s, ifp->status); - stream_putq (s, ifp->flags); - stream_putl (s, ifp->metric); - stream_putl (s, ifp->mtu); - stream_putl (s, ifp->mtu6); - stream_putl (s, ifp->bandwidth); -#ifdef HAVE_STRUCT_SOCKADDR_DL - stream_put (s, &ifp->sdl, sizeof (ifp->sdl)); -#else - stream_putl (s, ifp->hw_addr_len); - if (ifp->hw_addr_len) - stream_put (s, ifp->hw_addr, ifp->hw_addr_len); -#endif /* HAVE_STRUCT_SOCKADDR_DL */ - - /* Write packet size. */ - stream_putw_at (s, 0, stream_get_endp (s)); + zserv_create_header (s, ZEBRA_INTERFACE_ADD, ifp->vrf_id); + zserv_encode_interface (s, ifp); + client->ifadd_cnt++; return zebra_server_send_message(client); } @@ -196,27 +219,45 @@ zsend_interface_delete (struct zserv *client, struct interface *ifp) struct stream *s; /* Check this client need interface information. */ - if (! client->ifinfo) + if (! vrf_bitmap_check (client->ifinfo, ifp->vrf_id)) return 0; s = client->obuf; stream_reset (s); + + zserv_create_header (s, ZEBRA_INTERFACE_DELETE, ifp->vrf_id); + zserv_encode_interface (s, ifp); + + client->ifdel_cnt++; + return zebra_server_send_message (client); +} + +int +zsend_interface_link_params (struct zserv *client, struct interface *ifp) +{ + struct stream *s; + + /* Check this client need interface information. */ + if (! client->ifinfo) + return 0; + + if (!ifp->link_params) + return 0; + s = client->obuf; + stream_reset (s); - zserv_create_header (s, ZEBRA_INTERFACE_DELETE); + zserv_create_header (s, ZEBRA_INTERFACE_LINK_PARAMS, ifp->vrf_id); - /* Interface information. */ - stream_put (s, ifp->name, INTERFACE_NAMSIZ); + /* Add Interface Index */ stream_putl (s, ifp->ifindex); - stream_putc (s, ifp->status); - stream_putq (s, ifp->flags); - stream_putl (s, ifp->metric); - stream_putl (s, ifp->mtu); - stream_putl (s, ifp->mtu6); - stream_putl (s, ifp->bandwidth); - /* Write packet length. */ - stream_putw_at (s, 0, stream_get_endp (s)); + /* Then TE Link Parameters */ + if (zebra_interface_link_params_write (s, ifp) == 0) + return 0; + /* Write packet size. */ + stream_putw_at (s, 0, stream_get_endp (s)); + return zebra_server_send_message (client); } @@ -267,13 +308,13 @@ zsend_interface_address (int cmd, struct zserv *client, struct prefix *p; /* Check this client need interface information. */ - if (! client->ifinfo) + if (! vrf_bitmap_check (client->ifinfo, ifp->vrf_id)) return 0; s = client->obuf; stream_reset (s); - zserv_create_header (s, cmd); + zserv_create_header (s, cmd, ifp->vrf_id); stream_putl (s, ifp->ifindex); /* Interface address flag. */ @@ -302,6 +343,7 @@ zsend_interface_address (int cmd, struct zserv *client, /* Write packet size. */ stream_putw_at (s, 0, stream_get_endp (s)); + client->connected_rt_add_cnt++; return zebra_server_send_message(client); } @@ -321,26 +363,19 @@ zsend_interface_update (int cmd, struct zserv *client, struct interface *ifp) struct stream *s; /* Check this client need interface information. */ - if (! client->ifinfo) + if (! vrf_bitmap_check (client->ifinfo, ifp->vrf_id)) return 0; s = client->obuf; stream_reset (s); - zserv_create_header (s, cmd); - - /* Interface information. */ - stream_put (s, ifp->name, INTERFACE_NAMSIZ); - stream_putl (s, ifp->ifindex); - stream_putc (s, ifp->status); - stream_putq (s, ifp->flags); - stream_putl (s, ifp->metric); - stream_putl (s, ifp->mtu); - stream_putl (s, ifp->mtu6); - stream_putl (s, ifp->bandwidth); + zserv_create_header (s, cmd, ifp->vrf_id); + zserv_encode_interface (s, ifp); - /* Write packet size. */ - stream_putw_at (s, 0, stream_get_endp (s)); + if (cmd == ZEBRA_INTERFACE_UP) + client->ifup_cnt++; + else + client->ifdown_cnt++; return zebra_server_send_message(client); } @@ -378,11 +413,17 @@ zsend_route_multipath (int cmd, struct zserv *client, struct prefix *p, unsigned long nhnummark = 0, messmark = 0; int nhnum = 0; u_char zapi_flags = 0; - + + /* Check this client need this route. */ + if (!vrf_bitmap_check (client->redist[rib->type], rib->vrf_id) && + !(is_default (p) && + vrf_bitmap_check (client->redist_default, rib->vrf_id))) + return 0; + s = client->obuf; stream_reset (s); - zserv_create_header (s, cmd); + zserv_create_header (s, cmd, rib->vrf_id); /* Put type and nexthop. */ stream_putc (s, rib->type); @@ -409,7 +450,7 @@ 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_ACTIVE)) { SET_FLAG (zapi_flags, ZAPI_MESSAGE_NEXTHOP); SET_FLAG (zapi_flags, ZAPI_MESSAGE_IFINDEX); @@ -466,6 +507,14 @@ zsend_route_multipath (int cmd, struct zserv *client, struct prefix *p, stream_putc (s, rib->distance); SET_FLAG (zapi_flags, ZAPI_MESSAGE_METRIC); stream_putl (s, rib->metric); + SET_FLAG (zapi_flags, ZAPI_MESSAGE_MTU); + stream_putl (s, rib->mtu); + /* tag */ + if (rib->tag) + { + SET_FLAG(zapi_flags, ZAPI_MESSAGE_TAG); + stream_putl (s, rib->tag); + } } /* write real message flags value */ @@ -483,7 +532,8 @@ zsend_route_multipath (int cmd, struct zserv *client, struct prefix *p, #ifdef HAVE_IPV6 static int -zsend_ipv6_nexthop_lookup (struct zserv *client, struct in6_addr *addr) +zsend_ipv6_nexthop_lookup (struct zserv *client, struct in6_addr *addr, + vrf_id_t vrf_id) { struct stream *s; struct rib *rib; @@ -492,15 +542,15 @@ zsend_ipv6_nexthop_lookup (struct zserv *client, struct in6_addr *addr) struct nexthop *nexthop; /* Lookup nexthop. */ - rib = rib_match_ipv6 (addr); + rib = rib_match_ipv6 (addr, vrf_id); /* Get output stream. */ s = client->obuf; stream_reset (s); /* Fill in result. */ - zserv_create_header (s, ZEBRA_IPV6_NEXTHOP_LOOKUP); - stream_put (s, &addr, 16); + zserv_create_header (s, ZEBRA_IPV6_NEXTHOP_LOOKUP, vrf_id); + stream_put (s, addr, 16); if (rib) { @@ -508,8 +558,11 @@ 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)) + if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE)) { stream_putc (s, nexthop->type); switch (nexthop->type) @@ -532,6 +585,7 @@ zsend_ipv6_nexthop_lookup (struct zserv *client, struct in6_addr *addr) } num++; } + stream_putc_at (s, nump, num); } else @@ -541,13 +595,19 @@ zsend_ipv6_nexthop_lookup (struct zserv *client, struct in6_addr *addr) } stream_putw_at (s, 0, stream_get_endp (s)); - + return zebra_server_send_message(client); } #endif /* HAVE_IPV6 */ +/* + In the case of ZEBRA_IPV4_NEXTHOP_LOOKUP_MRIB: + query unicast rib if nexthop is not found on mrib + and return both route metric and protocol distance. +*/ static int -zsend_ipv4_nexthop_lookup (struct zserv *client, struct in_addr addr) +zsend_ipv4_nexthop_lookup (struct zserv *client, struct in_addr addr, + int cmd, vrf_id_t vrf_id) { struct stream *s; struct rib *rib; @@ -555,25 +615,41 @@ zsend_ipv4_nexthop_lookup (struct zserv *client, struct in_addr addr) u_char num; struct nexthop *nexthop; - /* Lookup nexthop. */ - rib = rib_match_ipv4 (addr); - /* Get output stream. */ s = client->obuf; stream_reset (s); /* Fill in result. */ - zserv_create_header (s, ZEBRA_IPV4_NEXTHOP_LOOKUP); + zserv_create_header (s, cmd, vrf_id); stream_put_in_addr (s, &addr); + /* Lookup nexthop - eBGP excluded */ + if (cmd == ZEBRA_IPV4_NEXTHOP_LOOKUP) + rib = rib_match_ipv4_safi (addr, SAFI_UNICAST, 1, NULL, vrf_id); + else /* ZEBRA_IPV4_NEXTHOP_LOOKUP_MRIB */ + { + rib = rib_match_ipv4_multicast (addr, NULL, vrf_id); + /* handle the distance field here since + * it is only needed for MRIB command */ + if (rib) + stream_putc (s, rib->distance); + else + stream_putc (s, 0); /* distance */ + } + 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); + 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)) + if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE)) { stream_putc (s, nexthop->type); switch (nexthop->type) @@ -581,22 +657,29 @@ 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); break; default: - /* do nothing */ + /* do nothing */ break; } num++; } - stream_putc_at (s, nump, num); + + stream_putc_at (s, nump, num); /* store nexthop_num */ } else { - stream_putl (s, 0); - stream_putc (s, 0); + if (IS_ZEBRA_DEBUG_PACKET && IS_ZEBRA_DEBUG_RECV) + zlog_debug("%s: No matching rib entry found.", __func__); + stream_putl (s, 0); /* metric */ + stream_putc (s, 0); /* nexthop_num */ } stream_putw_at (s, 0, stream_get_endp (s)); @@ -604,8 +687,80 @@ zsend_ipv4_nexthop_lookup (struct zserv *client, struct in_addr addr) return zebra_server_send_message(client); } +/* Nexthop register */ static int -zsend_ipv4_import_lookup (struct zserv *client, struct prefix_ipv4 *p) +zserv_nexthop_register (struct zserv *client, int sock, u_short length, vrf_id_t vrf_id) +{ + struct rnh *rnh; + struct stream *s; + struct prefix p; + u_short l = 0; + u_char connected; + + if (IS_ZEBRA_DEBUG_NHT) + zlog_debug("nexthop_register msg from client %s: length=%d\n", + zebra_route_string(client->proto), length); + + s = client->ibuf; + + while (l < length) + { + connected = stream_getc(s); + p.family = stream_getw(s); + p.prefixlen = stream_getc(s); + l += 4; + stream_get(&p.u.prefix, s, PSIZE(p.prefixlen)); + l += PSIZE(p.prefixlen); + rnh = zebra_add_rnh(&p, 0); + + client->nh_reg_time = quagga_time(NULL); + + if (connected) + SET_FLAG(rnh->flags, ZEBRA_NHT_CONNECTED); + + zebra_add_rnh_client(rnh, client, vrf_id); + } + zebra_evaluate_rnh_table(0, AF_INET); + zebra_evaluate_rnh_table(0, AF_INET6); + return 0; +} + +/* Nexthop register */ +static int +zserv_nexthop_unregister (struct zserv *client, int sock, u_short length) +{ + struct rnh *rnh; + struct stream *s; + struct prefix p; + u_short l = 0; + + if (IS_ZEBRA_DEBUG_NHT) + zlog_debug("nexthop_unregister msg from client %s: length=%d\n", + zebra_route_string(client->proto), length); + + s = client->ibuf; + + while (l < length) + { + (void)stream_getc(s); + p.family = stream_getw(s); + p.prefixlen = stream_getc(s); + l += 4; + stream_get(&p.u.prefix, s, PSIZE(p.prefixlen)); + l += PSIZE(p.prefixlen); + rnh = zebra_lookup_rnh(&p, 0); + if (rnh) + { + client->nh_dereg_time = quagga_time(NULL); + zebra_remove_rnh_client(rnh, client); + } + } + return 0; +} + +static int +zsend_ipv4_import_lookup (struct zserv *client, struct prefix_ipv4 *p, + vrf_id_t vrf_id) { struct stream *s; struct rib *rib; @@ -614,14 +769,14 @@ zsend_ipv4_import_lookup (struct zserv *client, struct prefix_ipv4 *p) struct nexthop *nexthop; /* Lookup nexthop. */ - rib = rib_lookup_ipv4 (p); + rib = rib_lookup_ipv4 (p, vrf_id); /* Get output stream. */ s = client->obuf; stream_reset (s); /* Fill in result. */ - zserv_create_header (s, ZEBRA_IPV4_IMPORT_LOOKUP); + zserv_create_header (s, ZEBRA_IPV4_IMPORT_LOOKUP, vrf_id); stream_put_in_addr (s, &p->prefix); if (rib) @@ -631,7 +786,7 @@ 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_ACTIVE)) { stream_putc (s, nexthop->type); switch (nexthop->type) @@ -639,6 +794,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); @@ -658,26 +817,27 @@ zsend_ipv4_import_lookup (struct zserv *client, struct prefix_ipv4 *p) } stream_putw_at (s, 0, stream_get_endp (s)); - + 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) +zsend_router_id_update (struct zserv *client, struct prefix *p, + vrf_id_t vrf_id) { struct stream *s; int blen; /* Check this client need interface information. */ - if (!client->ridinfo) + if (! vrf_bitmap_check (client->ridinfo, vrf_id)) return 0; s = client->obuf; stream_reset (s); /* Message type. */ - zserv_create_header (s, ZEBRA_ROUTER_ID_UPDATE); + zserv_create_header (s, ZEBRA_ROUTER_ID_UPDATE, vrf_id); /* Prefix information. */ stream_putc (s, p->family); @@ -690,11 +850,11 @@ 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 -zread_interface_add (struct zserv *client, u_short length) +zread_interface_add (struct zserv *client, u_short length, vrf_id_t vrf_id) { struct listnode *ifnode, *ifnnode; struct listnode *cnode, *cnnode; @@ -702,9 +862,9 @@ zread_interface_add (struct zserv *client, u_short length) struct connected *c; /* Interface information is needed. */ - client->ifinfo = 1; + vrf_bitmap_set (client->ifinfo, vrf_id); - for (ALL_LIST_ELEMENTS (iflist, ifnode, ifnnode, ifp)) + for (ALL_LIST_ELEMENTS (vrf_iflist (vrf_id), ifnode, ifnnode, ifp)) { /* Skip pseudo interface. */ if (! CHECK_FLAG (ifp->status, ZEBRA_INTERFACE_ACTIVE)) @@ -726,9 +886,9 @@ zread_interface_add (struct zserv *client, u_short length) /* Unregister zebra server interface information. */ static int -zread_interface_delete (struct zserv *client, u_short length) +zread_interface_delete (struct zserv *client, u_short length, vrf_id_t vrf_id) { - client->ifinfo = 0; + vrf_bitmap_unset (client->ifinfo, vrf_id); return 0; } @@ -738,7 +898,7 @@ zread_interface_delete (struct zserv *client, u_short length) * add kernel route. */ static int -zread_ipv4_add (struct zserv *client, u_short length) +zread_ipv4_add (struct zserv *client, u_short length, vrf_id_t vrf_id) { int i; struct rib *rib; @@ -748,10 +908,10 @@ zread_ipv4_add (struct zserv *client, u_short length) u_char nexthop_num; u_char nexthop_type; struct stream *s; - unsigned int ifindex; + ifindex_t ifindex; u_char ifname_len; safi_t safi; - + int ret; /* Get input stream. */ s = client->ibuf; @@ -772,6 +932,9 @@ zread_ipv4_add (struct zserv *client, u_short length) p.prefixlen = stream_getc (s); stream_get (&p.prefix, s, PSIZE (p.prefixlen)); + /* VRF ID */ + rib->vrf_id = vrf_id; + /* Nexthop parse. */ if (CHECK_FLAG (message, ZAPI_MESSAGE_NEXTHOP)) { @@ -785,7 +948,7 @@ zread_ipv4_add (struct zserv *client, u_short length) { case ZEBRA_NEXTHOP_IFINDEX: ifindex = stream_getl (s); - nexthop_ifindex_add (rib, ifindex); + rib_nexthop_ifindex_add (rib, ifindex); break; case ZEBRA_NEXTHOP_IFNAME: ifname_len = stream_getc (s); @@ -793,15 +956,20 @@ zread_ipv4_add (struct zserv *client, u_short length) break; case ZEBRA_NEXTHOP_IPV4: nexthop.s_addr = stream_get_ipv4 (s); - nexthop_ipv4_add (rib, &nexthop, NULL); + rib_nexthop_ipv4_add (rib, &nexthop, NULL); + break; + case ZEBRA_NEXTHOP_IPV4_IFINDEX: + nexthop.s_addr = stream_get_ipv4 (s); + ifindex = stream_getl (s); + rib_nexthop_ipv4_ifindex_add (rib, &nexthop, NULL, ifindex); break; case ZEBRA_NEXTHOP_IPV6: stream_forward_getp (s, IPV6_MAX_BYTELEN); break; - case ZEBRA_NEXTHOP_BLACKHOLE: - nexthop_blackhole_add (rib); - break; - } + case ZEBRA_NEXTHOP_BLACKHOLE: + rib_nexthop_blackhole_add (rib); + break; + } } } @@ -813,20 +981,34 @@ zread_ipv4_add (struct zserv *client, u_short length) if (CHECK_FLAG (message, ZAPI_MESSAGE_METRIC)) rib->metric = stream_getl (s); + if (CHECK_FLAG (message, ZAPI_MESSAGE_MTU)) + rib->mtu = stream_getl (s); + /* Tag */ + if (CHECK_FLAG (message, ZAPI_MESSAGE_TAG)) + rib->tag = stream_getl (s); + else + rib->tag = 0; + /* Table */ rib->table=zebrad.rtm_table_default; - rib_add_ipv4_multipath (&p, rib, safi); + ret = rib_add_ipv4_multipath (&p, rib, safi); + + /* Stats */ + if (ret > 0) + client->v4_route_add_cnt++; + else if (ret < 0) + client->v4_route_upd8_cnt++; return 0; } /* Zebra server IPv4 prefix delete function. */ static int -zread_ipv4_delete (struct zserv *client, u_short length) +zread_ipv4_delete (struct zserv *client, u_short length, vrf_id_t vrf_id) { int i; struct stream *s; struct zapi_ipv4 api; - struct in_addr nexthop; + struct in_addr nexthop, *nexthop_p; unsigned long ifindex; struct prefix_ipv4 p; u_char nexthop_num; @@ -836,6 +1018,7 @@ zread_ipv4_delete (struct zserv *client, u_short length) s = client->ibuf; ifindex = 0; nexthop.s_addr = 0; + nexthop_p = NULL; /* Type, flags, message. */ api.type = stream_getc (s); @@ -869,6 +1052,12 @@ zread_ipv4_delete (struct zserv *client, u_short length) break; case ZEBRA_NEXTHOP_IPV4: nexthop.s_addr = stream_get_ipv4 (s); + nexthop_p = &nexthop; + 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: stream_forward_getp (s, IPV6_MAX_BYTELEN); @@ -889,24 +1078,38 @@ zread_ipv4_delete (struct zserv *client, u_short length) else api.metric = 0; - rib_delete_ipv4 (api.type, api.flags, &p, &nexthop, ifindex, - client->rtm_table, api.safi); + /* tag */ + if (CHECK_FLAG (api.message, ZAPI_MESSAGE_TAG)) + api.tag = stream_getl (s); + else + api.tag = 0; + + rib_delete_ipv4 (api.type, api.flags, &p, nexthop_p, ifindex, + vrf_id, api.safi); + client->v4_route_del_cnt++; return 0; } /* Nexthop lookup for IPv4. */ static int -zread_ipv4_nexthop_lookup (struct zserv *client, u_short length) +zread_ipv4_nexthop_lookup (int cmd, struct zserv *client, u_short length, + vrf_id_t vrf_id) { struct in_addr addr; + char buf[BUFSIZ]; addr.s_addr = stream_get_ipv4 (client->ibuf); - return zsend_ipv4_nexthop_lookup (client, addr); + 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, cmd, vrf_id); } /* Nexthop lookup for IPv4. */ static int -zread_ipv4_import_lookup (struct zserv *client, u_short length) +zread_ipv4_import_lookup (struct zserv *client, u_short length, + vrf_id_t vrf_id) { struct prefix_ipv4 p; @@ -914,44 +1117,61 @@ zread_ipv4_import_lookup (struct zserv *client, u_short length) p.prefixlen = stream_getc (client->ibuf); p.prefix.s_addr = stream_get_ipv4 (client->ibuf); - return zsend_ipv4_import_lookup (client, &p); + return zsend_ipv4_import_lookup (client, &p, vrf_id); } #ifdef HAVE_IPV6 /* Zebra server IPv6 prefix add function. */ static int -zread_ipv6_add (struct zserv *client, u_short length) +zread_ipv6_add (struct zserv *client, u_short length, vrf_id_t vrf_id) { int i; struct stream *s; - struct zapi_ipv6 api; struct in6_addr nexthop; - unsigned long ifindex; + struct rib *rib; + u_char message; + u_char gateway_num; + u_char nexthop_type; struct prefix_ipv6 p; - + safi_t safi; + static struct in6_addr nexthops[MULTIPATH_NUM]; + static unsigned int ifindices[MULTIPATH_NUM]; + int ret; + + /* Get input stream. */ s = client->ibuf; - ifindex = 0; + memset (&nexthop, 0, sizeof (struct in6_addr)); + /* Allocate new rib. */ + rib = XCALLOC (MTYPE_RIB, sizeof (struct rib)); + /* Type, flags, message. */ - api.type = stream_getc (s); - api.flags = stream_getc (s); - api.message = stream_getc (s); - api.safi = stream_getw (s); + rib->type = stream_getc (s); + rib->flags = stream_getc (s); + message = stream_getc (s); + safi = stream_getw (s); + rib->uptime = time (NULL); - /* IPv4 prefix. */ + /* IPv6 prefix. */ memset (&p, 0, sizeof (struct prefix_ipv6)); p.family = AF_INET6; p.prefixlen = stream_getc (s); stream_get (&p.prefix, s, PSIZE (p.prefixlen)); - /* Nexthop, ifindex, distance, metric. */ - if (CHECK_FLAG (api.message, ZAPI_MESSAGE_NEXTHOP)) + /* We need to give nh-addr, nh-ifindex with the same next-hop object + * to the rib to ensure that IPv6 multipathing works; need to coalesce + * these. Clients should send the same number of paired set of + * next-hop-addr/next-hop-ifindices. */ + if (CHECK_FLAG (message, ZAPI_MESSAGE_NEXTHOP)) { - u_char nexthop_type; + int nh_count = 0; + int if_count = 0; + int max_nh_if = 0; + unsigned int ifindex; - api.nexthop_num = stream_getc (s); - for (i = 0; i < api.nexthop_num; i++) + gateway_num = stream_getc (s); + for (i = 0; i < gateway_num; i++) { nexthop_type = stream_getc (s); @@ -959,36 +1179,69 @@ zread_ipv6_add (struct zserv *client, u_short length) { case ZEBRA_NEXTHOP_IPV6: stream_get (&nexthop, s, 16); + if (nh_count < MULTIPATH_NUM) { + nexthops[nh_count++] = nexthop; + } break; case ZEBRA_NEXTHOP_IFINDEX: ifindex = stream_getl (s); + if (if_count < MULTIPATH_NUM) { + ifindices[if_count++] = ifindex; + } break; } } + + max_nh_if = (nh_count > if_count) ? nh_count : if_count; + for (i = 0; i < max_nh_if; i++) + { + if ((i < nh_count) && !IN6_IS_ADDR_UNSPECIFIED (&nexthops[i])) + { + if ((i < if_count) && ifindices[i]) + rib_nexthop_ipv6_ifindex_add (rib, &nexthops[i], ifindices[i]); + else + rib_nexthop_ipv6_add (rib, &nexthops[i]); + } + else + { + if ((i < if_count) && ifindices[i]) + rib_nexthop_ifindex_add (rib, ifindices[i]); + } + } } - if (CHECK_FLAG (api.message, ZAPI_MESSAGE_DISTANCE)) - api.distance = stream_getc (s); - else - api.distance = 0; + /* Distance. */ + if (CHECK_FLAG (message, ZAPI_MESSAGE_DISTANCE)) + rib->distance = stream_getc (s); - if (CHECK_FLAG (api.message, ZAPI_MESSAGE_METRIC)) - api.metric = stream_getl (s); - else - api.metric = 0; - - if (IN6_IS_ADDR_UNSPECIFIED (&nexthop)) - rib_add_ipv6 (api.type, api.flags, &p, NULL, ifindex, zebrad.rtm_table_default, api.metric, - api.distance, api.safi); + /* Metric. */ + if (CHECK_FLAG (message, ZAPI_MESSAGE_METRIC)) + rib->metric = stream_getl (s); + + if (CHECK_FLAG (message, ZAPI_MESSAGE_MTU)) + rib->mtu = stream_getl (s); + + /* Tag */ + if (CHECK_FLAG (message, ZAPI_MESSAGE_TAG)) + rib->tag = stream_getl (s); else - rib_add_ipv6 (api.type, api.flags, &p, &nexthop, ifindex, zebrad.rtm_table_default, api.metric, - api.distance, api.safi); + rib->tag = 0; + + /* Table */ + rib->table=zebrad.rtm_table_default; + ret = rib_add_ipv6_multipath (&p, rib, safi); + /* Stats */ + if (ret > 0) + client->v6_route_add_cnt++; + else if (ret < 0) + client->v6_route_upd8_cnt++; + return 0; } /* Zebra server IPv6 prefix delete function. */ static int -zread_ipv6_delete (struct zserv *client, u_short length) +zread_ipv6_delete (struct zserv *client, u_short length, vrf_id_t vrf_id) { int i; struct stream *s; @@ -1035,54 +1288,70 @@ zread_ipv6_delete (struct zserv *client, u_short length) } } + /* Distance. */ if (CHECK_FLAG (api.message, ZAPI_MESSAGE_DISTANCE)) api.distance = stream_getc (s); else api.distance = 0; + + /* Metric. */ if (CHECK_FLAG (api.message, ZAPI_MESSAGE_METRIC)) api.metric = stream_getl (s); else api.metric = 0; + /* tag */ + if (CHECK_FLAG (api.message, ZAPI_MESSAGE_TAG)) + api.tag = stream_getl (s); + else + api.tag = 0; + if (IN6_IS_ADDR_UNSPECIFIED (&nexthop)) - rib_delete_ipv6 (api.type, api.flags, &p, NULL, ifindex, client->rtm_table, api.safi); + rib_delete_ipv6 (api.type, api.flags, &p, NULL, ifindex, vrf_id, + api.safi); else - rib_delete_ipv6 (api.type, api.flags, &p, &nexthop, ifindex, client->rtm_table, api.safi); + rib_delete_ipv6 (api.type, api.flags, &p, &nexthop, ifindex, vrf_id, + api.safi); + + client->v6_route_del_cnt++; return 0; } static int -zread_ipv6_nexthop_lookup (struct zserv *client, u_short length) +zread_ipv6_nexthop_lookup (struct zserv *client, u_short length, + vrf_id_t vrf_id) { struct in6_addr addr; 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); + return zsend_ipv6_nexthop_lookup (client, &addr, vrf_id); } #endif /* HAVE_IPV6 */ /* Register zebra server router-id information. Send current router-id */ static int -zread_router_id_add (struct zserv *client, u_short length) +zread_router_id_add (struct zserv *client, u_short length, vrf_id_t vrf_id) { struct prefix p; /* Router-id information is needed. */ - client->ridinfo = 1; + vrf_bitmap_set (client->ridinfo, vrf_id); - router_id_get (&p); + router_id_get (&p, vrf_id); - return zsend_router_id_update (client,&p); + return zsend_router_id_update (client, &p, vrf_id); } /* Unregister zebra server router-id information. */ static int -zread_router_id_delete (struct zserv *client, u_short length) +zread_router_id_delete (struct zserv *client, u_short length, vrf_id_t vrf_id) { - client->ridinfo = 0; + vrf_bitmap_unset (client->ridinfo, vrf_id); return 0; } @@ -1108,9 +1377,25 @@ zread_hello (struct zserv *client) client->sock); route_type_oaths[proto] = client->sock; + client->proto = proto; } } +/* Unregister all information in a VRF. */ +static int +zread_vrf_unregister (struct zserv *client, u_short length, vrf_id_t vrf_id) +{ + int i; + + for (i = 0; i < ZEBRA_ROUTE_MAX; i++) + vrf_bitmap_unset (client->redist[i], vrf_id); + vrf_bitmap_unset (client->redist_default, vrf_id); + vrf_bitmap_unset (client->ifinfo, vrf_id); + vrf_bitmap_unset (client->ridinfo, vrf_id); + + return 0; +} + /* If client sent routes of specific type, zebra removes it * and returns number of deleted routes. */ @@ -1133,6 +1418,9 @@ zebra_score_rib (int client_sock) static void zebra_client_close (struct zserv *client) { + zebra_cleanup_rnh_client(0, AF_INET, client); + zebra_cleanup_rnh_client(0, AF_INET6, client); + /* Close file descriptor. */ if (client->sock) { @@ -1167,8 +1455,9 @@ static void zebra_client_create (int sock) { struct zserv *client; + int i; - client = XCALLOC (0, sizeof (struct zserv)); + client = XCALLOC (MTYPE_TMP, sizeof (struct zserv)); /* Make client input/output buffer. */ client->sock = sock; @@ -1179,6 +1468,14 @@ zebra_client_create (int sock) /* Set table number. */ client->rtm_table = zebrad.rtm_table_default; + /* Initialize flags */ + for (i = 0; i < ZEBRA_ROUTE_MAX; i++) + client->redist[i] = vrf_bitmap_init (); + client->redist_default = vrf_bitmap_init (); + client->ifinfo = vrf_bitmap_init (); + client->ridinfo = vrf_bitmap_init (); + client->connect_time = quagga_time(NULL); + /* Add this client to linked list. */ listnode_add (zebrad.client_list, client); @@ -1195,6 +1492,7 @@ zebra_client_read (struct thread *thread) size_t already; uint16_t length, command; uint8_t marker, version; + vrf_id_t vrf_id; /* Get thread data. Reset reading thread because I'm running. */ sock = THREAD_FD (thread); @@ -1236,6 +1534,7 @@ zebra_client_read (struct thread *thread) length = stream_getw (client->ibuf); marker = stream_getc (client->ibuf); version = stream_getc (client->ibuf); + vrf_id = stream_getw (client->ibuf); command = stream_getw (client->ibuf); if (marker != ZEBRA_HEADER_MARKER || version != ZSERV_VERSION) @@ -1288,63 +1587,75 @@ zebra_client_read (struct thread *thread) zlog_debug ("zebra message comes from socket [%d]", sock); if (IS_ZEBRA_DEBUG_PACKET && IS_ZEBRA_DEBUG_RECV) - zlog_debug ("zebra message received [%s] %d", - zserv_command_string (command), length); + zlog_debug ("zebra message received [%s] %d in VRF %u", + zserv_command_string (command), length, vrf_id); + + client->last_read_time = quagga_time(NULL); + client->last_read_cmd = command; switch (command) { case ZEBRA_ROUTER_ID_ADD: - zread_router_id_add (client, length); + zread_router_id_add (client, length, vrf_id); break; case ZEBRA_ROUTER_ID_DELETE: - zread_router_id_delete (client, length); + zread_router_id_delete (client, length, vrf_id); break; case ZEBRA_INTERFACE_ADD: - zread_interface_add (client, length); + zread_interface_add (client, length, vrf_id); break; case ZEBRA_INTERFACE_DELETE: - zread_interface_delete (client, length); + zread_interface_delete (client, length, vrf_id); break; case ZEBRA_IPV4_ROUTE_ADD: - zread_ipv4_add (client, length); + zread_ipv4_add (client, length, vrf_id); break; case ZEBRA_IPV4_ROUTE_DELETE: - zread_ipv4_delete (client, length); + zread_ipv4_delete (client, length, vrf_id); break; #ifdef HAVE_IPV6 case ZEBRA_IPV6_ROUTE_ADD: - zread_ipv6_add (client, length); + zread_ipv6_add (client, length, vrf_id); break; case ZEBRA_IPV6_ROUTE_DELETE: - zread_ipv6_delete (client, length); + zread_ipv6_delete (client, length, vrf_id); break; #endif /* HAVE_IPV6 */ case ZEBRA_REDISTRIBUTE_ADD: - zebra_redistribute_add (command, client, length); + zebra_redistribute_add (command, client, length, vrf_id); break; case ZEBRA_REDISTRIBUTE_DELETE: - zebra_redistribute_delete (command, client, length); + zebra_redistribute_delete (command, client, length, vrf_id); break; case ZEBRA_REDISTRIBUTE_DEFAULT_ADD: - zebra_redistribute_default_add (command, client, length); + zebra_redistribute_default_add (command, client, length, vrf_id); break; case ZEBRA_REDISTRIBUTE_DEFAULT_DELETE: - zebra_redistribute_default_delete (command, client, length); + zebra_redistribute_default_delete (command, client, length, vrf_id); break; case ZEBRA_IPV4_NEXTHOP_LOOKUP: - zread_ipv4_nexthop_lookup (client, length); + case ZEBRA_IPV4_NEXTHOP_LOOKUP_MRIB: + zread_ipv4_nexthop_lookup (command, client, length, vrf_id); break; #ifdef HAVE_IPV6 case ZEBRA_IPV6_NEXTHOP_LOOKUP: - zread_ipv6_nexthop_lookup (client, length); + zread_ipv6_nexthop_lookup (client, length, vrf_id); break; #endif /* HAVE_IPV6 */ case ZEBRA_IPV4_IMPORT_LOOKUP: - zread_ipv4_import_lookup (client, length); + zread_ipv4_import_lookup (client, length, vrf_id); break; case ZEBRA_HELLO: zread_hello (client); break; + case ZEBRA_VRF_UNREGISTER: + zread_vrf_unregister (client, length, vrf_id); + case ZEBRA_NEXTHOP_REGISTER: + zserv_nexthop_register(client, sock, length, vrf_id); + break; + case ZEBRA_NEXTHOP_UNREGISTER: + zserv_nexthop_unregister(client, sock, length); + break; default: zlog_info ("Zebra received unknown command %d", command); break; @@ -1455,7 +1766,7 @@ zebra_serv () zebra_event (ZEBRA_SERV, accept_sock, NULL); } -#endif /* HAVE_TCP_ZEBRA */ +#else /* HAVE_TCP_ZEBRA */ /* For sockaddr_un. */ #include @@ -1521,7 +1832,8 @@ zebra_serv_un (const char *path) zebra_event (ZEBRA_SERV, sock, NULL); } - +#endif /* HAVE_TCP_ZEBRA */ + static void zebra_event (enum event event, int sock, struct zserv *client) @@ -1540,7 +1852,128 @@ zebra_event (enum event event, int sock, struct zserv *client) break; } } - + +#define ZEBRA_TIME_BUF 32 +static char * +zserv_time_buf(time_t *time1, char *buf, int buflen) +{ + struct tm *tm; + time_t now; + + assert (buf != NULL); + assert (buflen >= ZEBRA_TIME_BUF); + assert (time1 != NULL); + + if (!*time1) + { + snprintf(buf, buflen, "never "); + return (buf); + } + + now = quagga_time(NULL); + now -= *time1; + tm = gmtime(&now); + + /* Making formatted timer strings. */ +#define ONE_DAY_SECOND 60*60*24 +#define ONE_WEEK_SECOND 60*60*24*7 + + if (now < ONE_DAY_SECOND) + snprintf (buf, buflen, "%02d:%02d:%02d", + tm->tm_hour, tm->tm_min, tm->tm_sec); + else if (now < ONE_WEEK_SECOND) + snprintf (buf, buflen, "%dd%02dh%02dm", + tm->tm_yday, tm->tm_hour, tm->tm_min); + else + snprintf (buf, buflen, "%02dw%dd%02dh", + tm->tm_yday/7, tm->tm_yday - ((tm->tm_yday/7) * 7), tm->tm_hour); + return buf; +} + +static void +zebra_show_client_detail (struct vty *vty, struct zserv *client) +{ + char cbuf[ZEBRA_TIME_BUF], rbuf[ZEBRA_TIME_BUF]; + char wbuf[ZEBRA_TIME_BUF], nhbuf[ZEBRA_TIME_BUF], mbuf[ZEBRA_TIME_BUF]; + + vty_out (vty, "Client: %s %s", + zebra_route_string(client->proto), VTY_NEWLINE); + vty_out (vty, "------------------------ %s", VTY_NEWLINE); + vty_out (vty, "FD: %d %s", client->sock, VTY_NEWLINE); + vty_out (vty, "Route Table ID: %d %s", client->rtm_table, VTY_NEWLINE); + + vty_out (vty, "Connect Time: %s %s", + zserv_time_buf(&client->connect_time, cbuf, ZEBRA_TIME_BUF), + VTY_NEWLINE); + if (client->nh_reg_time) + { + vty_out (vty, "Nexthop Registry Time: %s %s", + zserv_time_buf(&client->nh_reg_time, nhbuf, ZEBRA_TIME_BUF), + VTY_NEWLINE); + if (client->nh_last_upd_time) + vty_out (vty, "Nexthop Last Update Time: %s %s", + zserv_time_buf(&client->nh_last_upd_time, mbuf, ZEBRA_TIME_BUF), + VTY_NEWLINE); + else + vty_out (vty, "No Nexthop Update sent%s", VTY_NEWLINE); + } + else + vty_out (vty, "Not registered for Nexthop Updates%s", VTY_NEWLINE); + + vty_out (vty, "Last Msg Rx Time: %s %s", + zserv_time_buf(&client->last_read_time, rbuf, ZEBRA_TIME_BUF), + VTY_NEWLINE); + vty_out (vty, "Last Msg Tx Time: %s %s", + zserv_time_buf(&client->last_write_time, wbuf, ZEBRA_TIME_BUF), + VTY_NEWLINE); + if (client->last_read_time) + vty_out (vty, "Last Rcvd Cmd: %s %s", + zserv_command_string(client->last_read_cmd), VTY_NEWLINE); + if (client->last_write_time) + vty_out (vty, "Last Sent Cmd: %s %s", + zserv_command_string(client->last_write_cmd), VTY_NEWLINE); + vty_out (vty, "%s", VTY_NEWLINE); + + vty_out (vty, "Type Add Update Del %s", VTY_NEWLINE); + vty_out (vty, "================================================== %s", VTY_NEWLINE); + vty_out (vty, "IPv4 %-12d%-12d%-12d%s", client->v4_route_add_cnt, + client->v4_route_upd8_cnt, client->v4_route_del_cnt, VTY_NEWLINE); + vty_out (vty, "IPv6 %-12d%-12d%-12d%s", client->v6_route_add_cnt, + client->v6_route_upd8_cnt, client->v6_route_del_cnt, VTY_NEWLINE); + vty_out (vty, "Redist:v4 %-12d%-12d%-12d%s", client->redist_v4_add_cnt, 0, + client->redist_v4_del_cnt, VTY_NEWLINE); + vty_out (vty, "Redist:v6 %-12d%-12d%-12d%s", client->redist_v6_add_cnt, 0, + client->redist_v6_del_cnt, VTY_NEWLINE); + vty_out (vty, "Connected %-12d%-12d%-12d%s", client->ifadd_cnt, 0, + client->ifdel_cnt, VTY_NEWLINE); + vty_out (vty, "Interface Up Notifications: %d%s", client->ifup_cnt, + VTY_NEWLINE); + vty_out (vty, "Interface Down Notifications: %d%s", client->ifdown_cnt, + VTY_NEWLINE); + + vty_out (vty, "%s", VTY_NEWLINE); + return; +} + +static void +zebra_show_client_brief (struct vty *vty, struct zserv *client) +{ + char cbuf[ZEBRA_TIME_BUF], rbuf[ZEBRA_TIME_BUF]; + char wbuf[ZEBRA_TIME_BUF]; + + vty_out (vty, "%-8s%12s %12s%12s%8d/%-8d%8d/%-8d%s", + zebra_route_string(client->proto), + zserv_time_buf(&client->connect_time, cbuf, ZEBRA_TIME_BUF), + zserv_time_buf(&client->last_read_time, rbuf, ZEBRA_TIME_BUF), + zserv_time_buf(&client->last_write_time, wbuf, ZEBRA_TIME_BUF), + client->v4_route_add_cnt+client->v4_route_upd8_cnt, + client->v4_route_del_cnt, + client->v6_route_add_cnt+client->v6_route_upd8_cnt, + client->v6_route_del_cnt, VTY_NEWLINE); + +} + + /* Display default rtm_table for all clients. */ DEFUN (show_table, show_table_cmd, @@ -1618,8 +2051,31 @@ DEFUN (show_zebra_client, struct zserv *client; for (ALL_LIST_ELEMENTS_RO (zebrad.client_list, node, client)) - vty_out (vty, "Client fd %d%s", client->sock, VTY_NEWLINE); - + zebra_show_client_detail(vty, client); + + return CMD_SUCCESS; +} + +/* This command is for debugging purpose. */ +DEFUN (show_zebra_client_summary, + show_zebra_client_summary_cmd, + "show zebra client summary", + SHOW_STR + "Zebra information brief" + "Client information brief") +{ + struct listnode *node; + struct zserv *client; + + vty_out (vty, "Name Connect Time Last Read Last Write IPv4 Routes IPv6 Routes %s", + VTY_NEWLINE); + vty_out (vty,"--------------------------------------------------------------------------------%s", + VTY_NEWLINE); + + for (ALL_LIST_ELEMENTS_RO (zebrad.client_list, node, client)) + zebra_show_client_brief(vty, client); + + vty_out (vty, "Routes column shows (added+updated)/deleted%s", VTY_NEWLINE); return CMD_SUCCESS; } @@ -1640,7 +2096,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, @@ -1761,7 +2217,25 @@ static struct cmd_node forwarding_node = 1 }; - +#ifdef HAVE_FPM +/* function to write the fpm config info */ +static int +config_write_fpm (struct vty *vty) +{ + return + fpm_remote_srv_write (vty); +} + +/* Zebra node */ +static struct cmd_node zebra_node = +{ + ZEBRA_NODE, + "", + 1 +}; +#endif + + /* Initialisation of zebra and installation of commands. */ void zebra_init (void) @@ -1772,22 +2246,23 @@ zebra_init (void) /* Install configuration write function. */ install_node (&table_node, config_write_table); install_node (&forwarding_node, config_write_forwarding); +#ifdef HAVE_FPM + install_node (&zebra_node, config_write_fpm); +#endif install_element (VIEW_NODE, &show_ip_forwarding_cmd); - install_element (ENABLE_NODE, &show_ip_forwarding_cmd); install_element (CONFIG_NODE, &ip_forwarding_cmd); install_element (CONFIG_NODE, &no_ip_forwarding_cmd); install_element (ENABLE_NODE, &show_zebra_client_cmd); + install_element (ENABLE_NODE, &show_zebra_client_summary_cmd); #ifdef HAVE_NETLINK install_element (VIEW_NODE, &show_table_cmd); - install_element (ENABLE_NODE, &show_table_cmd); install_element (CONFIG_NODE, &config_table_cmd); #endif /* HAVE_NETLINK */ #ifdef HAVE_IPV6 install_element (VIEW_NODE, &show_ipv6_forwarding_cmd); - install_element (ENABLE_NODE, &show_ipv6_forwarding_cmd); install_element (CONFIG_NODE, &ipv6_forwarding_cmd); install_element (CONFIG_NODE, &no_ipv6_forwarding_cmd); #endif /* HAVE_IPV6 */ diff --git a/zebra/zserv.h b/zebra/zserv.h index 5e8bccac3..4af92f0e1 100644 --- a/zebra/zserv.h +++ b/zebra/zserv.h @@ -25,6 +25,7 @@ #include "rib.h" #include "if.h" #include "workqueue.h" +#include "vrf.h" /* Default port information. */ #define ZEBRA_VTY_PORT 2601 @@ -56,16 +57,47 @@ struct zserv int rtm_table; /* This client's redistribute flag. */ - u_char redist[ZEBRA_ROUTE_MAX]; + vrf_bitmap_t redist[ZEBRA_ROUTE_MAX]; /* Redistribute default route flag. */ - u_char redist_default; + vrf_bitmap_t redist_default; /* Interface information. */ - u_char ifinfo; + vrf_bitmap_t ifinfo; /* Router-id information. */ - u_char ridinfo; + vrf_bitmap_t ridinfo; + + /* client's protocol */ + u_char proto; + + /* Statistics */ + u_int32_t redist_v4_add_cnt; + u_int32_t redist_v4_del_cnt; + u_int32_t redist_v6_add_cnt; + u_int32_t redist_v6_del_cnt; + u_int32_t v4_route_add_cnt; + u_int32_t v4_route_upd8_cnt; + u_int32_t v4_route_del_cnt; + u_int32_t v6_route_add_cnt; + u_int32_t v6_route_del_cnt; + u_int32_t v6_route_upd8_cnt; + u_int32_t connected_rt_add_cnt; + u_int32_t connected_rt_del_cnt; + u_int32_t ifup_cnt; + u_int32_t ifdown_cnt; + u_int32_t ifadd_cnt; + u_int32_t ifdel_cnt; + + time_t connect_time; + time_t last_read_time; + time_t last_write_time; + time_t nh_reg_time; + time_t nh_dereg_time; + time_t nh_last_upd_time; + + int last_read_cmd; + int last_write_cmd; }; /* Zebra instance */ @@ -83,18 +115,16 @@ struct zebra_t struct meta_queue *mq; }; -/* Count prefix size from mask length */ -#define PSIZE(a) (((a) + 7) / (8)) - /* Prototypes. */ extern void zebra_init (void); extern void zebra_if_init (void); extern void zebra_zserv_socket_init (char *path); extern void hostinfo_get (void); extern void rib_init (void); -extern void interface_list (void); -extern void kernel_init (void); -extern void route_read (void); +extern void interface_list (struct zebra_vrf *); +extern void route_read (struct zebra_vrf *); +extern void kernel_init (struct zebra_vrf *); +extern void kernel_terminate (struct zebra_vrf *); extern void zebra_route_map_init (void); extern void zebra_snmp_init (void); extern void zebra_vty_init (void); @@ -106,8 +136,14 @@ extern int zsend_interface_address (int, struct zserv *, struct interface *, extern int zsend_interface_update (int, struct zserv *, struct interface *); extern int zsend_route_multipath (int, struct zserv *, struct prefix *, struct rib *); -extern int zsend_router_id_update(struct zserv *, struct prefix *); +extern int zsend_router_id_update (struct zserv *, struct prefix *, + vrf_id_t); + +extern int zsend_interface_link_params (struct zserv *, struct interface *); extern pid_t pid; +extern void zserv_create_header(struct stream *s, uint16_t cmd, vrf_id_t); +extern int zebra_server_send_message(struct zserv *client); + #endif /* _ZEBRA_ZEBRA_H */